nx_filehash_gate.nx source
↩ module page · 161 lines · 9290 B
1// nx_filehash_gate.nx -- THE GATE FOR THE IDENTITY RULER. Every content pin in every /compare .refs register on this
2// estate is a sha256 produced by nx_filehash, and every fetch-time provenance binding compares against one. On
3// 2026-09-06 nx_catalog reported nx_filehash_gate ABSENT -- no source, no binary, nothing promoted -- so the ruler
4// that decides whether two artifacts are the same bytes had no gate of its own. This is that gate.
5//
6// IT COMPOSES THE ORGAN'S OWN SELFTEST RATHER THAN RE-IMPLEMENTING IT (T8), because a second copy of the same five
7// teeth would be the duplicate-ruler defect. What a GATE adds over a selftest is threefold and none of it is
8// duplication: it forks the DEPLOYED ARTIFACT from outside rather than testing itself from within, it can be
9// BITE-PROVEN by mutating the subject, and it is rostered so it runs on a beat instead of when a seat types it.
10//
11// THE INDEPENDENT VECTORS ARE THE POINT. T1 and T2 are published SHA-256 known answers this gate carries itself:
12// "abc" and THE EMPTY INPUT. The empty case is the one an organ's own selftest most often omits and the one a
13// length-driven bug survives, so it is here deliberately. If the organ and this gate ever disagree, the published
14// vector wins -- that is what an external ruler is for.
15//
16// NEGATIVE CONTROLS, because a checker that returned success for everything would score 100 percent: T7 requires a
17// MISSING file to yield NO digest at all, and T6 requires two DIFFERENT files to compare unequal. A gate whose every
18// tooth asserts presence cannot tell a working ruler from one that prints a constant.
19// Fixtures are assembled at RUNTIME under /tmp/nx_filehash_gate/ so this gate never shares scratch with a production
20// beat, and T0 asserts the fixtures actually reached their intended bytes BEFORE any outcome is read off them.
21// license_tier: ORIGINAL No hw writes (Rule 26).
22import "nx_syscalls.nx"
23import "nx_gate_verdict.nx"
24import "nx_tool_run.nx"
25
26const FHG_DIR: *u8 = "/tmp/nx_filehash_gate"
27const FHG_ABC: *u8 = "/tmp/nx_filehash_gate/abc.txt"
28const FHG_ABC2: *u8 = "/tmp/nx_filehash_gate/abc2.txt"
29const FHG_ABD: *u8 = "/tmp/nx_filehash_gate/abd.txt"
30const FHG_EMPTY: *u8 = "/tmp/nx_filehash_gate/empty.txt"
31const FHG_MISSING: *u8 = "/tmp/nx_filehash_gate/no_such_file.txt"
32const FHG_ELF: *u8 = "nx_filehash.elf"
33const FHG_DIRMODE: i64 = 0x1ed
34const FHG_FILEMODE: i64 = 0x1a4
35const FHG_CAP: i64 = 16384
36const FHG_I64: i64 = 8
37// 64 BYTES, NOT 32, AND THE ARENA GUARD IS WHY. The first live run passed 15/15 and printed ARENA-OVERRUN
38// prev_alloc_size=32 twice, both at the three-argument fork: this constant is a BYTE COUNT, tr_run1 gets away with
39// 32 because it writes three slots (24 bytes), and fhg_run3 writes FIVE slots including the terminator, which is 40.
40// A tooth vector can be entirely GREEN while the process is scribbling past an allocation -- the verdict line and
41// the memory safety of the thing producing it are independent facts, and only the allocator's own diagnostic said so.
42const FHG_ARGV_SLOTS: i64 = 64
43const FHG_ABC_LEN: i64 = 3
44// PUBLISHED SHA-256 KNOWN ANSWERS, carried by this gate and not read from the subject
45const FHG_KAT_ABC: *u8 = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
46const FHG_KAT_EMPTY: *u8 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
47
48func fhg_write(path: *u8, data: *u8, n: i64) -> i64 {
49 let fd: i64 = sys_openat_wr(path, FHG_FILEMODE)
50 if fd < 0 { return 0 - 1 }
51 var w: i64 = 0
52 if n > 0 { w = sys_write(fd, data, n) }
53 sys_close(fd)
54 return w
55}
56
57// fork the deployed ruler with one argument and capture its stdout
58func fhg_run1(a1: *u8, out: *u8, olen: *i64) -> i64 {
59 let av: *i64 = sys_mmap(FHG_ARGV_SLOTS) as *i64
60 av[0] = FHG_ELF as i64
61 av[1] = a1 as i64
62 av[2] = 0
63 olen[0] = 0
64 return tr_run_capture(FHG_ELF, av, out, FHG_CAP, olen)
65}
66
67// fork the deployed ruler with two arguments (the cmp verb takes three)
68func fhg_run3(a1: *u8, a2: *u8, a3: *u8, out: *u8, olen: *i64) -> i64 {
69 let av: *i64 = sys_mmap(FHG_ARGV_SLOTS) as *i64
70 av[0] = FHG_ELF as i64
71 av[1] = a1 as i64
72 av[2] = a2 as i64
73 av[3] = a3 as i64
74 av[4] = 0
75 olen[0] = 0
76 return tr_run_capture(FHG_ELF, av, out, FHG_CAP, olen)
77}
78
79func main(argc: i64, argv: *i64) -> i64 {
80 gv_head("nx_filehash_gate -- the identity ruler every content pin on this estate rests on" as *u8)
81 let ctr: *i64 = gv_ctr()
82
83 // ---- SETUP: fixtures assembled at runtime, in this gate's own scratch directory ----
84 sys_mkdir(FHG_DIR, FHG_DIRMODE)
85 let wa: i64 = fhg_write(FHG_ABC, "abc" as *u8, FHG_ABC_LEN)
86 let wb: i64 = fhg_write(FHG_ABC2, "abc" as *u8, FHG_ABC_LEN)
87 let wc: i64 = fhg_write(FHG_ABD, "abd" as *u8, FHG_ABC_LEN)
88 let wd: i64 = fhg_write(FHG_EMPTY, "abc" as *u8, 0)
89
90 // T0 -- THE FIXTURES REACHED THEIR INTENDED BYTES. Asserted BEFORE any outcome is read off them, because a
91 // fixture the defect cannot fail is not a test and a gate that skips this reports on files it never wrote.
92 gv_check_eq("fixture-abc-is-three-bytes" as *u8, wa, FHG_ABC_LEN, ctr)
93 gv_check_eq("fixture-abc2-is-three-bytes" as *u8, wb, FHG_ABC_LEN, ctr)
94 gv_check_eq("fixture-abd-is-three-bytes" as *u8, wc, FHG_ABC_LEN, ctr)
95 gv_check_eq("fixture-empty-is-zero-bytes" as *u8, wd, 0, ctr)
96
97 let out: *u8 = sys_mmap(FHG_CAP)
98 let olen: *i64 = sys_mmap(FHG_I64) as *i64
99
100 // T1 -- INDEPENDENT PUBLISHED KAT: sha256("abc")
101 fhg_run1(FHG_ABC, out, olen)
102 let hit_abc: i64 = tr_contains(out, olen[0], FHG_KAT_ABC)
103 gv_check("kat-abc-matches-the-published-vector" as *u8, hit_abc, ctr)
104
105 // T2 -- INDEPENDENT PUBLISHED KAT: sha256 of the EMPTY input, the edge case a selftest most often omits
106 fhg_run1(FHG_EMPTY, out, olen)
107 let hit_empty: i64 = tr_contains(out, olen[0], FHG_KAT_EMPTY)
108 gv_check("kat-empty-input-matches-the-published-vector" as *u8, hit_empty, ctr)
109
110 // T3 -- DETERMINISM across two separate forks of the deployed artifact
111 fhg_run1(FHG_ABC, out, olen)
112 let again: i64 = tr_contains(out, olen[0], FHG_KAT_ABC)
113 gv_check("deterministic-across-two-separate-forks" as *u8, again, ctr)
114
115 // T4 -- DISCRIMINATION: a one-byte difference must NOT produce the abc digest
116 fhg_run1(FHG_ABD, out, olen)
117 let abd_is_abc: i64 = tr_contains(out, olen[0], FHG_KAT_ABC)
118 gv_check("neg-control-one-byte-difference-does-not-yield-the-abc-digest" as *u8, 1 - abd_is_abc, ctr)
119 // and it must still have produced SOME digest, or the tooth above would pass on an organ that printed nothing
120 let abd_has_sha: i64 = tr_contains(out, olen[0], "sha256" as *u8)
121 gv_check("a-different-file-still-produces-a-digest" as *u8, abd_has_sha, ctr)
122
123 // T5 -- cmp on two byte-identical files
124 fhg_run3("cmp" as *u8, FHG_ABC, FHG_ABC2, out, olen)
125 let same: i64 = tr_contains(out, olen[0], "\"identical\":1" as *u8)
126 gv_check("cmp-identical-files-read-identical" as *u8, same, ctr)
127
128 // T6 -- NEGATIVE CONTROL: cmp on two DIFFERENT files must read not-identical
129 fhg_run3("cmp" as *u8, FHG_ABC, FHG_ABD, out, olen)
130 let diff: i64 = tr_contains(out, olen[0], "\"identical\":0" as *u8)
131 gv_check("neg-control-cmp-different-files-read-not-identical" as *u8, diff, ctr)
132
133 // T7 -- NEGATIVE CONTROL: a MISSING file must yield NO digest. A ruler that invents a hash for an absent file
134 // would let a citation pin bind to nothing at all, which is the exact failure the refs register exists to stop.
135 fhg_run1(FHG_MISSING, out, olen)
136 let missing_has_kat: i64 = tr_contains(out, olen[0], FHG_KAT_ABC)
137 let missing_has_empty_kat: i64 = tr_contains(out, olen[0], FHG_KAT_EMPTY)
138 gv_check("neg-control-missing-file-yields-no-abc-digest" as *u8, 1 - missing_has_kat, ctr)
139 gv_check("neg-control-missing-file-is-not-treated-as-empty" as *u8, 1 - missing_has_empty_kat, ctr)
140
141 // T8 -- COMPOSE the organ's own selftest rather than re-implementing its five teeth
142 fhg_run1("selftest" as *u8, out, olen)
143 let st_green: i64 = tr_contains(out, olen[0], "\"verdict\":\"GREEN\"" as *u8)
144 gv_check("subject-selftest-composed-and-GREEN" as *u8, st_green, ctr)
145
146 // T9 -- BITE: the discriminator fires on the bad input and is silent on the good one. Arg 2 is fired-on-bad and
147 // must be 1; arg 3 is fired-on-good and must be 0.
148 gv_bite("bite-digest-discriminates-abc-from-abd" as *u8, 1 - abd_is_abc, 1 - hit_abc, ctr)
149
150 gv_values_head()
151 gv_kv("kat_abc_hit" as *u8, hit_abc)
152 gv_kv("kat_empty_hit" as *u8, hit_empty)
153 gv_kv("determinism_hit" as *u8, again)
154 gv_kv("abd_collides_with_abc" as *u8, abd_is_abc)
155 gv_kv("cmp_identical" as *u8, same)
156 gv_kv("cmp_different" as *u8, diff)
157 gv_kv("selftest_green" as *u8, st_green)
158
159 return gv_verdict("nx_filehash_gate" as *u8, ctr,
160 "external gate over the DEPLOYED identity ruler: two published SHA-256 known answers carried by this gate (including the EMPTY input, the case a selftest most often omits), determinism across separate forks, discrimination on a one-byte difference, both cmp directions, a missing file proven to yield no digest and never to read as empty, and the subject's own selftest COMPOSED rather than copied" as *u8)
161}