nx_image_search_gate.nx source
↩ module page · 263 lines · 13261 B
1// nx_image_search_gate.nx -- the engineer's re-runnable instrument for the REUSABLE search-by-image
2// client (nx_image_search). Builds TWO isolated site fingerprint-indices from SYNTHESIZED grayscale
3// images with CONTROLLED dHash relationships, registers both in a sites TSV, writes query images to
4// /tmp, then forks+execs the REAL /tmp/nx_image_search.sov.elf over a KAT + neg-control + isolation
5// oracle. It PROVES (not asserts) the research-documented dHash properties end-to-end (image -> hash
6// -> per-site nearest):
7// row1 identical query -> right image rank 1 at Hamming 0
8// row2 +brightness query -> STILL Hamming 0 (dHash brightness invariance, Krawetz 2011)
9// row3 near-dup (block tweak) -> right image rank 1, small Hamming (<= threshold)
10// row4 precision -> a structurally-different indexed image (Hamming 64) is EXCLUDED
11// row5 isolation A + neg-ctrl -> a BETA-only image queried on ALPHA returns matches=0 (alpha cannot
12// surface content it does not hold)
13// row6 isolation B -> an image present in BOTH sites, queried on BETA, returns the BETA
14// cid and NEVER an alpha cid (client loaded the right per-site index)
15// row7 unknown site -> clean exit 1 with "unknown site" (no crash)
16// GREEN iff all 7 rows pass. Durable verdict appends to knowledge/status/image_search_gate.log.
17// license_tier: ORIGINAL
18import "nx_phash.nx"
19
20func g_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
21func g_num(v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;sys_write(1,"-" as *u8,1)}; let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=(48 as u8);k=1}; while m>0{t[k]=((48+(m%10)) as u8);m=m/10;k=k+1}; var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1}; sys_write(1,bb,k); return 0 }
22func g_w(fd: i64, s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(fd,s,n); return 0 }
23func g_wn(fd: i64, v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m}; let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=(48 as u8);k=1}; while m>0{t[k]=((48+(m%10)) as u8);m=m/10;k=k+1}; var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1}; sys_write(fd,bb,k); return 0 }
24func g_strlen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
25
26func g_write_file(path: *u8, data: *u8) -> i64 {
27 let fd: i64=sys_openat_wr(path, 0x1a4)
28 if fd<0 { return 0-1 }
29 sys_write(fd, data, g_strlen(data))
30 sys_close(fd)
31 return 0
32}
33func g_write_gray(path: *u8, buf: *u8, n: i64) -> i64 {
34 let fd: i64=sys_openat_wr(path, 0x1a4)
35 if fd<0 { return 0-1 }
36 sys_write(fd, buf, n)
37 sys_close(fd)
38 return 0
39}
40func g_read_small(path: *u8, buf: *u8, cap: i64) -> i64 {
41 let fd: i64=sys_openat_rd(path)
42 if fd<0 { return 0 }
43 var total: i64=0
44 var nrd: i64=sys_read(fd, buf, cap)
45 while nrd>0 { total=total+nrd; if total>=cap { nrd=0 } else { nrd=sys_read(fd, ((buf as i64)+total) as *u8, cap-total) } }
46 sys_close(fd)
47 return total
48}
49
50// emit one i64 as 16 lowercase-hex chars (high nibble first); &0xF masks sign-extension cleanly
51func g_hexemit(fd: i64, v: i64) -> i64 {
52 let buf: *u8=sys_mmap(16)
53 var i: i64=0
54 while i<16 {
55 let shift: i64=(15-i)*4
56 let nib: i64=(v>>shift)&0xF
57 var c: i64=0
58 if nib<10 { c=48+nib } else { c=87+nib }
59 buf[i]=c as u8
60 i=i+1
61 }
62 sys_write(fd, buf, 16)
63 return 0
64}
65
66// synthesize a grayscale image. kind 0=horizontal gradient up (dHash all-0), 1=gradient down
67// (dHash all-1), 3=period-4 vertical stripes (~24 bits set, far from both). bright adds a uniform
68// offset (clamped) to exercise dHash's brightness invariance.
69func g_synth(buf: *u8, w: i64, h: i64, kind: i64, bright: i64) -> i64 {
70 var y: i64=0
71 while y<h {
72 var x: i64=0
73 while x<w {
74 var v: i64=0
75 if kind==0 { v=x*4 }
76 if kind==1 { v=(w-1-x)*4 }
77 if kind==3 { v=(x&2)*30 }
78 v=v+bright
79 if v<0 { v=0 }
80 if v>255 { v=255 }
81 buf[y*w+x]=v as u8
82 x=x+1
83 }
84 y=y+1
85 }
86 return 0
87}
88
89// append one fp-index row: cid<TAB>dhash_hex<TAB>ahash_hex
90func g_emit_idx_row(fd: i64, cid: *u8, buf: *u8, w: i64, h: i64) -> i64 {
91 let d: i64=nx_phash_dhash(buf, w, h)
92 let a: i64=nx_phash_ahash(buf, w, h)
93 sys_write(fd, cid, g_strlen(cid)); sys_write(fd, "\t" as *u8, 1)
94 g_hexemit(fd, d); sys_write(fd, "\t" as *u8, 1)
95 g_hexemit(fd, a); sys_write(fd, "\n" as *u8, 1)
96 return 0
97}
98
99// fork+exec the real client, capturing stdout to out_path; return its exit code.
100func g_run(sites: *u8, site: *u8, qpath: *u8, ws: *u8, hs: *u8, ths: *u8, out_path: *u8) -> i64 {
101 let pid: i64=sys_fork()
102 if pid==0 {
103 let out: i64=sys_openat_wr(out_path, 0x1a4)
104 if out>=0 { sys_dup3(out, 1, 0) }
105 let argv: *i64=sys_mmap(8*8) as *i64
106 argv[0]="/tmp/nx_image_search.sov.elf" as *u8 as i64
107 argv[1]=sites as i64; argv[2]=site as i64; argv[3]=qpath as i64
108 argv[4]=ws as i64; argv[5]=hs as i64; argv[6]=ths as i64; argv[7]=0
109 let envp: *i64=sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0
110 sys_execve("/tmp/nx_image_search.sov.elf" as *u8, argv, envp)
111 sys_exit(127)
112 }
113 let st: *i64=sys_mmap(16) as *i64
114 sys_wait4(pid, st, 0)
115 if (st[0]&0x7f)!=0 { return 128+(st[0]&0x7f) }
116 return (st[0]>>8)&0xff
117}
118
119func g_find(hay: *u8, n: i64, needle: *u8) -> i64 {
120 let nl: i64=g_strlen(needle)
121 if nl==0 { return 0-1 }
122 var i: i64=0
123 while i+nl<=n {
124 var j: i64=0; var hit: i64=1
125 while j<nl { if hay[i+j]!=needle[j] { hit=0; j=nl } else { j=j+1 } }
126 if hit==1 { return i }
127 i=i+1
128 }
129 return 0-1
130}
131func g_contains(hay: *u8, n: i64, needle: *u8) -> i64 { if g_find(hay,n,needle)>=0 { return 1 } return 0 }
132func g_rank_of(buf: *u8, n: i64, expected: *u8) -> i64 {
133 let at: i64=g_find(buf, n, expected)
134 if at<0 { return 0 }
135 var ls: i64=at
136 while ls>0 { if buf[ls-1]==(10 as u8) { ls=0-ls } else { ls=ls-1 } }
137 if ls<0 { ls=0-ls }
138 let hash_at: i64=g_find(((buf as i64)+ls) as *u8, at-ls+1, "#" as *u8)
139 if hash_at<0 { return 0 }
140 var p: i64=ls+hash_at+1
141 var rank: i64=0
142 while p<n { let c: i64=buf[p] as i64; if c>=48 { if c<=57 { rank=rank*10+(c-48); p=p+1 } else { p=n } } else { p=n } }
143 return rank
144}
145
146func main() -> i64 {
147 g_puts("=== IMAGE SEARCH GATE (reusable search-by-image: dHash KAT + brightness-invariance + precision + isolation + unknown-site) ===\n" as *u8)
148 let W: i64=16
149 let H: i64=16
150 let N: i64=256
151
152 // synthesize the corpus + query images
153 let up: *u8=sys_mmap(N); g_synth(up, W, H, 0, 0)
154 let down: *u8=sys_mmap(N); g_synth(down, W, H, 1, 0)
155 let mid: *u8=sys_mmap(N); g_synth(mid, W, H, 3, 0)
156 let bright: *u8=sys_mmap(N); g_synth(bright, W, H, 0, 40)
157 let near: *u8=sys_mmap(N); g_synth(near, W, H, 0, 0)
158 var by: i64=2
159 while by<5 { var bx: i64=2; while bx<5 { near[by*W+bx]=63 as u8; bx=bx+1 } by=by+1 }
160
161 // measured dHash benchmark (print every value -- honest-perf cardinal)
162 let d_up: i64=nx_phash_dhash(up, W, H)
163 g_puts(" dHash(up..down)=" as *u8); g_num(nx_simhash_hamming(d_up, nx_phash_dhash(down, W, H)))
164 g_puts(" dHash(up..mid)=" as *u8); g_num(nx_simhash_hamming(d_up, nx_phash_dhash(mid, W, H)))
165 g_puts(" dHash(up..bright)=" as *u8); g_num(nx_simhash_hamming(d_up, nx_phash_dhash(bright, W, H)))
166 g_puts(" dHash(up..near)=" as *u8); g_num(nx_simhash_hamming(d_up, nx_phash_dhash(near, W, H)))
167 g_puts("\n" as *u8)
168
169 // write query images
170 g_write_gray("/tmp/q_up.gray" as *u8, up, N)
171 g_write_gray("/tmp/q_upbright.gray" as *u8, bright, N)
172 g_write_gray("/tmp/q_upnear.gray" as *u8, near, N)
173 g_write_gray("/tmp/q_mid.gray" as *u8, mid, N)
174
175 // build ALPHA index {/A/up, /A/down}
176 let fa: i64=sys_openat_wr("/tmp/img_alpha.idx" as *u8, 0x1a4)
177 if fa>=0 {
178 g_emit_idx_row(fa, "/A/up" as *u8, up, W, H)
179 g_emit_idx_row(fa, "/A/down" as *u8, down, W, H)
180 sys_close(fa)
181 }
182 // build BETA index {/B/up (same image, different site), /B/mid (beta-only)}
183 let fb: i64=sys_openat_wr("/tmp/img_beta.idx" as *u8, 0x1a4)
184 if fb>=0 {
185 g_emit_idx_row(fb, "/B/up" as *u8, up, W, H)
186 g_emit_idx_row(fb, "/B/mid" as *u8, mid, W, H)
187 sys_close(fb)
188 }
189 g_write_file("/tmp/img_sites.tsv" as *u8,
190 "alpha\t/tmp/img_alpha.idx\thttps://site-a\nbeta\t/tmp/img_beta.idx\thttps://site-b\n" as *u8)
191
192 let sites: *u8="/tmp/img_sites.tsv" as *u8
193 let outp: *u8="/tmp/img_gate_out.txt" as *u8
194 let obuf: *u8=sys_mmap(65536)
195 let ws: *u8="16" as *u8
196 let hs: *u8="16" as *u8
197 let ths: *u8="10" as *u8
198
199 var pass: i64=0
200 let rows: i64=7
201
202 // row1: identical -> /A/up rank 1, ham 0
203 g_run(sites, "alpha" as *u8, "/tmp/q_up.gray" as *u8, ws, hs, ths, outp)
204 let n1: i64=g_read_small(outp, obuf, 65536)
205 let rk1: i64=g_rank_of(obuf, n1, "/A/up" as *u8)
206 let h0_1: i64=g_contains(obuf, n1, "ham=0" as *u8)
207 g_puts(" row1 identical -> rank " as *u8); g_num(rk1); g_puts(" ham0? " as *u8); g_num(h0_1)
208 if rk1==1 { if h0_1==1 { pass=pass+1; g_puts(" PASS\n" as *u8) } else { g_puts(" FAIL\n" as *u8) } } else { g_puts(" FAIL\n" as *u8) }
209
210 // row2: +brightness -> /A/up rank 1, STILL ham 0 (invariance)
211 g_run(sites, "alpha" as *u8, "/tmp/q_upbright.gray" as *u8, ws, hs, ths, outp)
212 let n2: i64=g_read_small(outp, obuf, 65536)
213 let rk2: i64=g_rank_of(obuf, n2, "/A/up" as *u8)
214 let h0_2: i64=g_contains(obuf, n2, "ham=0" as *u8)
215 g_puts(" row2 +brightness -> rank " as *u8); g_num(rk2); g_puts(" ham0? " as *u8); g_num(h0_2)
216 if rk2==1 { if h0_2==1 { pass=pass+1; g_puts(" PASS (brightness-invariant)\n" as *u8) } else { g_puts(" FAIL\n" as *u8) } } else { g_puts(" FAIL\n" as *u8) }
217
218 // row3: near-dup -> /A/up rank 1 (small ham, within threshold)
219 g_run(sites, "alpha" as *u8, "/tmp/q_upnear.gray" as *u8, ws, hs, ths, outp)
220 let n3: i64=g_read_small(outp, obuf, 65536)
221 let rk3: i64=g_rank_of(obuf, n3, "/A/up" as *u8)
222 g_puts(" row3 near-dup -> rank " as *u8); g_num(rk3)
223 if rk3==1 { pass=pass+1; g_puts(" PASS (near-dup found)\n" as *u8) } else { g_puts(" FAIL\n" as *u8) }
224
225 // row4: precision -> only the similar image is within threshold (down at ham 64 excluded)
226 g_run(sites, "alpha" as *u8, "/tmp/q_up.gray" as *u8, ws, hs, ths, outp)
227 let n4: i64=g_read_small(outp, obuf, 65536)
228 let m1_4: i64=g_contains(obuf, n4, "matches=1" as *u8)
229 g_puts(" row4 precision -> matches=1? " as *u8); g_num(m1_4)
230 if m1_4==1 { pass=pass+1; g_puts(" PASS (dissimilar excluded)\n" as *u8) } else { g_puts(" FAIL\n" as *u8) }
231
232 // row5: isolation A + neg-control -> beta-only image on alpha returns matches=0
233 g_run(sites, "alpha" as *u8, "/tmp/q_mid.gray" as *u8, ws, hs, ths, outp)
234 let n5: i64=g_read_small(outp, obuf, 65536)
235 let m0_5: i64=g_contains(obuf, n5, "matches=0" as *u8)
236 g_puts(" row5 isolation-A -> matches=0? " as *u8); g_num(m0_5)
237 if m0_5==1 { pass=pass+1; g_puts(" PASS (alpha blind to beta-only content)\n" as *u8) } else { g_puts(" FAIL\n" as *u8) }
238
239 // row6: isolation B -> up exists in BOTH; querying beta returns /B/up and NEVER an /A/ cid
240 g_run(sites, "beta" as *u8, "/tmp/q_up.gray" as *u8, ws, hs, ths, outp)
241 let n6: i64=g_read_small(outp, obuf, 65536)
242 let rk6: i64=g_rank_of(obuf, n6, "/B/up" as *u8)
243 let leakA: i64=g_contains(obuf, n6, "/A/" as *u8)
244 g_puts(" row6 isolation-B -> /B/up rank " as *u8); g_num(rk6); g_puts(" leak(/A/)? " as *u8); g_num(leakA)
245 if rk6==1 { if leakA==0 { pass=pass+1; g_puts(" PASS (beta returns its own, no alpha leak)\n" as *u8) } else { g_puts(" FAIL (LEAK!)\n" as *u8) } } else { g_puts(" FAIL\n" as *u8) }
246
247 // row7: unknown site -> clean exit 1 + message
248 let rc7: i64=g_run(sites, "ghost" as *u8, "/tmp/q_up.gray" as *u8, ws, hs, ths, outp)
249 let n7: i64=g_read_small(outp, obuf, 65536)
250 let unk7: i64=g_contains(obuf, n7, "unknown site" as *u8)
251 g_puts(" row7 unknown site -> exit " as *u8); g_num(rc7); g_puts(" msg? " as *u8); g_num(unk7)
252 if rc7==1 { if unk7==1 { pass=pass+1; g_puts(" PASS (clean unknown-site fail)\n" as *u8) } else { g_puts(" FAIL\n" as *u8) } } else { g_puts(" FAIL\n" as *u8) }
253
254 g_puts("----\nIMAGE-SEARCH-GATE rows=" as *u8); g_num(rows); g_puts(" pass=" as *u8); g_num(pass); g_puts("\n" as *u8)
255 let lg: i64=sys_openat_append("knowledge/status/image_search_gate.log" as *u8, 0x1a4)
256 if lg>=0 {
257 g_w(lg, "IMAGE-SEARCH-GATE rows=" as *u8); g_wn(lg, rows); g_w(lg, " pass=" as *u8); g_wn(lg, pass)
258 if pass==rows { g_w(lg, " verdict=GREEN\n" as *u8) } else { g_w(lg, " verdict=RED\n" as *u8) }
259 sys_close(lg)
260 }
261 if pass==rows { g_puts("IMAGE-SEARCH-GATE GREEN\n" as *u8); sys_exit(0); return 0 }
262 g_puts("IMAGE-SEARCH-GATE RED\n" as *u8); sys_exit(1); return 1
263}