nx_asset_prov_clock_cli_candidate_t346.nx source
↩ module page · 76 lines · 4202 B
1// nx_asset_prov.nx -- THE PER-ASSET PROVENANCE ROW, as a command (/compare/modding MD9, 2026-09-06). A thin caller of
2// nx_asset_prov_lib so the command line and every export door prove the SAME verdict code.
3// usage: nx_asset_prov row <file> <source_url> <licence_id> <origin> [jrnl=<path>] hash the file, append its row, print the verdict
4// nx_asset_prov verdict <file> [jrnl=<path>] hash the file, print the verdict
5// nx_asset_prov verdict-sha <sha256hex> [jrnl=<path>] the verdict for a known sha
6// exits: 0 SHIP_OK | 2 usage | 3 REVIEW | 4 REFUSE (the reason is on the line) | 5 the file could not be read
7// The journal defaults to knowledge/provenance/assets.jrnl; a gate passes its own.
8// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
9import "nx_syscalls.nx"
10import "nx_asset_prov_clock_candidate_t346.nx"
11
12const AP2_EXIT_USAGE: i64 = 2
13const AP2_EXIT_UNREADABLE: i64 = 5
14const AP2_JRNL_KEY: *u8 = "jrnl="
15const AP2_JRNL_KEY_LEN: i64 = 5
16
17func ap2_streq(a: *u8, b: *u8) -> i64 { var i: i64 = 0; while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } if b[i] != (0 as u8) { return 0 } return 1 }
18func ap2_has_prefix(s: *u8, p: *u8, n: i64) -> i64 { var i: i64 = 0; while i < n { if s[i] != p[i] { return 0 } i = i + 1 } return 1 }
19func ap2_usage() -> i64 {
20 pv_puts("usage: nx_asset_prov row <file> <source_url> <licence_id> <origin> [jrnl=<path>] | verdict <file> [jrnl=<path>] | verdict-sha <sha256hex> [jrnl=<path>]\n" as *u8)
21 return AP2_EXIT_USAGE
22}
23// the trailing jrnl=<path> is a SCAN over argv, so the positional contract is untouched
24func ap2_jrnl(argc: i64, argv: *i64) -> *u8 {
25 var i: i64 = 1
26 while i < argc {
27 let a: *u8 = argv[i] as *u8
28 if ap2_has_prefix(a, AP2_JRNL_KEY, AP2_JRNL_KEY_LEN) == 1 { return a + AP2_JRNL_KEY_LEN }
29 i = i + 1
30 }
31 return PV_JRNL_DEFAULT
32}
33func main(argc: i64, argv: *i64) -> i64 {
34 if argc < 3 { return ap2_usage() }
35 let verb: *u8 = argv[1] as *u8
36 let jrnl: *u8 = ap2_jrnl(argc, argv)
37 let sha: *u8 = sys_mmap(PV_SHA_HEX + 1)
38 let res: *i64 = sys_mmap(8 * PV_RES_N) as *i64
39 if ap2_streq(verb, "row" as *u8) == 1 {
40 if argc < 6 { return ap2_usage() }
41 let path: *u8 = argv[2] as *u8
42 let n: i64 = pv_hash_file(path, sha)
43 if n < 0 { pv_puts("ASSET-PROV-REFUSE unreadable: " as *u8); pv_puts(path); pv_puts("\n" as *u8); return AP2_EXIT_UNREADABLE }
44 let epoch: i64 = pv_now()
45 if epoch <= 0 { pv_puts("ASSET-PROV-REFUSE clock unavailable; journal unchanged\n" as *u8); return AP2_EXIT_UNREADABLE }
46 let w: i64 = pv_row_write(jrnl, sha, argv[3] as *u8, argv[4] as *u8, argv[5] as *u8, epoch)
47 if w < 0 { pv_puts("ASSET-PROV-REFUSE journal not writable: " as *u8); pv_puts(jrnl); pv_puts("\n" as *u8); return AP2_EXIT_UNREADABLE }
48 pv_puts("ASSET-PROV-ROW sha=" as *u8); pv_puts(sha)
49 pv_puts(" bytes=" as *u8); pv_putn(n)
50 pv_puts(" source=" as *u8); pv_puts(argv[3] as *u8)
51 pv_puts(" lic=" as *u8); pv_puts(argv[4] as *u8)
52 pv_puts(" origin=" as *u8); pv_puts(argv[5] as *u8)
53 pv_puts(" epoch=" as *u8); pv_putn(epoch)
54 pv_puts(" row_bytes=" as *u8); pv_putn(w)
55 pv_puts(" jrnl=" as *u8); pv_puts(jrnl); pv_puts("\n" as *u8)
56 let rc: i64 = pv_verdict(jrnl, sha, res)
57 pv_print(sha, rc, res)
58 return rc
59 }
60 if ap2_streq(verb, "verdict" as *u8) == 1 {
61 let path: *u8 = argv[2] as *u8
62 let n: i64 = pv_hash_file(path, sha)
63 if n < 0 { pv_puts("ASSET-PROV-REFUSE unreadable: " as *u8); pv_puts(path); pv_puts("\n" as *u8); return AP2_EXIT_UNREADABLE }
64 let rc: i64 = pv_verdict(jrnl, sha, res)
65 pv_print(sha, rc, res)
66 return rc
67 }
68 if ap2_streq(verb, "verdict-sha" as *u8) == 1 {
69 let s: *u8 = argv[2] as *u8
70 if pv_is_sha_hex(s) != 1 { pv_puts("ASSET-PROV-REFUSE not a sha256 hex: " as *u8); pv_puts(s); pv_puts("\n" as *u8); return AP2_EXIT_USAGE }
71 let rc: i64 = pv_verdict(jrnl, s, res)
72 pv_print(s, rc, res)
73 return rc
74 }
75 return ap2_usage()
76}