code wiki / _hdl_build / nx_nist_ingest.nx
nx_nist_ingest.nx source
↩ module page · 175 lines · 7226 B
1// nx_nist_ingest.nx -- NAS-side STRUCTURED ingester for NIST's data.nist.gov RMM DCAT catalog.
2// Pages the RMM API (sovereign fetch cascade), splits ResultData[] into per-dataset NERDm records,
3// prepends a clean title line, and ingests each into the domain's PUBLIC seg_store corpus via
4// dp_ingest -- the IDENTICAL path the docportal admin ingest uses (auto .terms BM25 => instantly
5// searchable by nishi_search / researcher / census). Idempotent (segid=CID): re-ingest is a no-op.
6//
7// Runs in the tools-daemon (nishihost) CWD: data/mozilla_certdata.txt AND knowledge/store/ both resolve.
8// Reuses nx_fetch_any (S1 sovereign TLS cascade -> S2 Firefox-spoof) + dp_ingest. Nothing hand-rolled
9// that already exists. Anti-overfit: the record source is DCAT/schema.org (standard), so the same
10// splitter generalizes to any DCAT catalog (data.gov, EU portals).
11//
12// usage: nx_nist_ingest <domain> <pages> [size] e.g. nx_nist_ingest nishifamily.com 3 100
13// license_tier: ORIGINAL | genealogy_id: nishi_nist_ingest_2026_07_14
14import "nx_syscalls.nx"
15import "nx_x509_trust_store.nx"
16import "nx_trust_store_load_from_certdata.nx"
17import "nx_https_fetch_follow.nx"
18import "nx_fetch_any.nx"
19import "nx_docportal_lib.nx"
20const K_MAGIC_4194304: i64 = 4194304
21const K_MAGIC_8388608: i64 = 8388608
22const K_MAGIC_8192: i64 = 8192
23const K_MAGIC_524288: i64 = 524288
24
25func ni_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
26
27func ni_putn(v: i64) -> i64 {
28 let t: *u8 = sys_mmap(32)
29 var m: i64 = v
30 var k: i64 = 0
31 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
32 if m == 0 { t[0] = 48 as u8; k = 1 }
33 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
34 let b: *u8 = sys_mmap(32)
35 var i: i64 = 0
36 while i < k { b[i] = t[k - 1 - i]; i = i + 1 }
37 sys_write(1, b, k)
38 return 0
39}
40
41// substring search in hay[0..hl) starting at `start`; returns index or -1.
42func ni_find(hay: *u8, hl: i64, start: i64, needle: *u8, nl: i64) -> i64 {
43 var i: i64 = start
44 while i + nl <= hl {
45 var j: i64 = 0
46 while j < nl {
47 if hay[i + j] != needle[j] { j = nl + 1 } else { j = j + 1 }
48 }
49 if j == nl { return i }
50 i = i + 1
51 }
52 return 0 - 1
53}
54
55// parse a base-10 integer prefix of s (stops at first non-digit / NUL).
56func ni_atoi(s: *u8) -> i64 {
57 var v: i64 = 0
58 var i: i64 = 0
59 var go: i64 = 1
60 while go == 1 {
61 let c: i64 = s[i] as i64
62 if c < 48 { go = 0 } else { if c > 57 { go = 0 } else { v = v * 10 + (c - 48); i = i + 1 } }
63 }
64 return v
65}
66
67// append NUL-terminated string s to buf at offset o; return new offset.
68func ni_scat(buf: *u8, o: i64, s: *u8) -> i64 {
69 var i: i64 = 0
70 while s[i] != (0 as u8) { buf[o + i] = s[i]; i = i + 1 }
71 return o + i
72}
73
74// append decimal v to buf at offset o; return new offset.
75func ni_ncat(buf: *u8, o: i64, v: i64) -> i64 {
76 let t: *u8 = sys_mmap(32)
77 var m: i64 = v
78 var k: i64 = 0
79 if m == 0 { t[0] = 48 as u8; k = 1 }
80 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
81 var i: i64 = 0
82 while i < k { buf[o + i] = t[k - 1 - i]; i = i + 1 }
83 return o + k
84}
85
86func ni_build_url(buf: *u8, size: i64, page: i64) -> i64 {
87 var o: i64 = 0
88 o = ni_scat(buf, o, "https://data.nist.gov/rmm/records?size=" as *u8)
89 o = ni_ncat(buf, o, size)
90 o = ni_scat(buf, o, "&page=" as *u8)
91 o = ni_ncat(buf, o, page)
92 buf[o] = 0 as u8
93 return o
94}
95
96// Ingest one record chunk out[recstart..recend). Prepends a clean "<title>" line (extracted from the
97// record's "title":"..." field) + a provenance line, then the raw NERDm JSON, so the search title reads
98// well and every structured field is BM25-indexed. Returns 1 if ingested, 0 if skipped.
99func ni_ingest_record(domain: *u8, out: *u8, hl: i64, recstart: i64, recend: i64, doc: *u8, cidp: *i64) -> i64 {
100 var dl: i64 = 0
101 // title (search-first): "title":" ...value... "
102 let tpos: i64 = ni_find(out, recend, recstart, "\"title\":\"" as *u8, 9)
103 if tpos >= 0 {
104 var ti: i64 = tpos + 9
105 var go: i64 = 1
106 while go == 1 {
107 if ti >= recend { go = 0 } else {
108 if out[ti] == (34 as u8) { go = 0 } else { doc[dl] = out[ti]; dl = dl + 1; ti = ti + 1 }
109 }
110 }
111 doc[dl] = 10 as u8; dl = dl + 1
112 }
113 dl = ni_scat(doc, dl, "NIST -- National Institute of Standards and Technology, data.nist.gov (Standard Reference / public data, DCAT/NERDm).\n" as *u8)
114 // raw record JSON (every field: description, keyword, theme, DOI, landingPage)
115 var ri: i64 = recstart
116 while ri < recend { doc[dl] = out[ri]; dl = dl + 1; ri = ri + 1 }
117 if dl <= 0 { return 0 }
118 dp_ingest(domain, DP_VIS_PUBLIC, doc, dl, cidp)
119 return 1
120}
121
122func main(argc: i64, argv: *i64) -> i64 {
123 if argc < 3 {
124 ni_puts("usage: nx_nist_ingest <domain> <pages> [size]\n" as *u8)
125 return 2
126 }
127 let domain: *u8 = argv[1] as *u8
128 let pages: i64 = ni_atoi(argv[2] as *u8)
129 var size: i64 = 100
130 if argc > 3 { size = ni_atoi(argv[3] as *u8) }
131
132 let r: i64 = nx_trust_store_load_from_certdata("data/mozilla_certdata.txt" as *u8, 512, K_MAGIC_4194304)
133 if r <= 0 { ni_puts("STORE-FAIL: data/mozilla_certdata.txt not loadable (wrong CWD?)\n" as *u8); return 1 }
134 let store: *TrustStore = r as *TrustStore
135
136 let out: *u8 = sys_mmap(K_MAGIC_8388608)
137 let att: *u8 = sys_mmap(K_MAGIC_8192)
138 let status: *i64 = sys_mmap(16) as *i64
139 let empty: *u8 = sys_mmap(8); empty[0] = 0 as u8
140 let urlbuf: *u8 = sys_mmap(512)
141 let doc: *u8 = sys_mmap(K_MAGIC_524288)
142 let cidp: *i64 = sys_mmap(16) as *i64
143
144 var total: i64 = 0
145 var p: i64 = 1
146 while p <= pages {
147 ni_build_url(urlbuf, size, p)
148 status[0] = 0
149 let n: i64 = nx_fetch_any(urlbuf, store, out, K_MAGIC_8388608, 6, status, empty, 0, att, K_MAGIC_8192)
150 if n > 0 {
151 let rd: i64 = ni_find(out, n, 0, "ResultData" as *u8, 10)
152 if rd >= 0 {
153 var recstart: i64 = ni_find(out, n, rd, "{\"@context\"" as *u8, 11)
154 var pcount: i64 = 0
155 while recstart >= 0 {
156 let nextrec: i64 = ni_find(out, n, recstart + 11, "{\"@context\"" as *u8, 11)
157 var recend: i64 = nextrec
158 if recend < 0 { recend = n }
159 pcount = pcount + ni_ingest_record(domain, out, n, recstart, recend, doc, cidp)
160 total = total + 1
161 recstart = nextrec
162 }
163 ni_puts("page " as *u8); ni_putn(p); ni_puts(" status=" as *u8); ni_putn(status[0])
164 ni_puts(" records=" as *u8); ni_putn(pcount); ni_puts("\n" as *u8)
165 } else {
166 ni_puts("page " as *u8); ni_putn(p); ni_puts(" NO-ResultData status=" as *u8); ni_putn(status[0]); ni_puts("\n" as *u8)
167 }
168 } else {
169 ni_puts("page " as *u8); ni_putn(p); ni_puts(" FETCH-FAIL (see cascade attempts)\n" as *u8)
170 }
171 p = p + 1
172 }
173 ni_puts("TOTAL-INGESTED=" as *u8); ni_putn(total); ni_puts("\n" as *u8)
174 return 0
175}