code wiki / (root) / nx_acquire_lib.nx

nx_acquire_lib.nx source

↩ module page · 240 lines · 13223 B

1// nx_acquire_lib.nx -- THE LIBRARIAN'S DOOR: what may we do with an EXTERNAL artifact? 2// 3// WHY THIS EXISTS. The estate already holds every primitive for taking in a third-party research 4// artifact: nx_research_fetch mirrors the bytes and emits a provenance line, nx_canon_cid content- 5// addresses them (cid_of_file), nx_asset_provenance signs the binding and already ships a 6// prov_class_downloaded, nx_mirror_orchestrator verifies a digest FAIL-CLOSED and derives an 7// idempotent CAS key, and nx_licgate_lib decides commercial rights. NOTHING JOINS THEM, so an 8// external work has never had a record. This lib is that record's DECISION half. 9// 10// THE MEASUREMENT THAT SHAPED IT, taken 2026-08-25 against the mirrored listing at 11// https://europe.naverlabs.com/research/code/ (knowledge/fetched/cmp_librarian_naverlabs_code.html, 12// 522716 bytes, sha256 deeb6ab8..5592): of the 26 releases on page 1, exactly 2 carry ANY licence 13// string -- 77 permil -- and BOTH are prose parentheticals inside a description, not a field. The 14// markup has no licence element at all. 24 of 26 would require opening the linked repository. 15// => A LIBRARIAN THAT READS THE LICENCE OFF A LISTING PAGE IS WRONG 923 TIMES IN 1000. 16// => EVIDENCE IS ITS OWN AXIS. "the page asserted Apache-2.0" and "we hold the LICENSE file and read 17// it" are DIFFERENT FACTS and must never collapse into one column. An unread licence ABSTAINS. 18// This is the same law nx_licgate_lib's `verified` column encodes -- and which, until 2026-08-25, it 19// parsed and never consulted, so an admittedly-UNCONFIRMED row returned full SHIP_OK. 20// 21// THE FOUR VERDICTS ARE DELIBERATELY INDEPENDENT, because they fail apart in practice. Collapsing 22// them is exactly how a project gets mirrored for study and then accidentally republished: 23// may_mirror -- may we keep a copy at all. THE ANTI-ROT RIGHT. 24// may_redistribute -- may we serve that copy onward to third parties. 25// may_learn_from -- may we study it, and use it as an ORACLE or a BENCHMARK, taking no code. 26// must_attribute -- an OBLIGATION, not a right: it takes the MAX and DEFAULTS TO YES. 27// 28// REVIEW BLOCKS. THE ONLY VERDICT THAT PERMITS IS YES. A consumer that reads REVIEW as permission 29// has misread this lib -- REVIEW means "a human must settle a term a machine may not", which is the 30// third state, not a soft pass. Encoded in the SAME order as nx_licgate_lib (0 NO < 1 REVIEW < 2 YES) 31// so "most restrictive wins" stays a plain MIN and the two rulers can never disagree on direction. 32// 33// PURE BY CONSTRUCTION: aq_decide touches no file and no clock. Its whole input is its arguments, so 34// its gate needs no fixture and cannot go vacuous through a missing one. 35// license_tier: ORIGINAL 36import "nx_syscalls.nx" 37 38// EVIDENCE CLASS for a licence claim -- the axis the estate did not have. 39const AQ_EV_NONE: i64 = 0 // no licence seen anywhere. The 24-of-26 NAVER case. 40const AQ_EV_DECLARED: i64 = 1 // an upstream page ASSERTED one; we have NOT read its text. 41const AQ_EV_READ: i64 = 2 // we hold the licence text in our own mirror and read it. 42 43// RIGHT / OBLIGATION ENCODING -- ordered, identical to nx_licgate_lib's. 44const AQ_NO: i64 = 0 45const AQ_REVIEW: i64 = 1 46const AQ_YES: i64 = 2 47 48// res[] slots. 49const AQ_R_MIRROR: i64 = 0 50const AQ_R_REDIST: i64 = 1 51const AQ_R_LEARN: i64 = 2 52const AQ_R_ATTRIB: i64 = 3 53const AQ_R_SLOTS: i64 = 4 54const AQ_WORD: i64 = 8 55 56// OVERALL CODES -- deliberately the SAME integers nx_licgate_lib's lg_rc returns, so a caller that 57// already branches on a licgate exit code branches identically here instead of learning a second 58// vocabulary. A second verdict vocabulary for one domain is a duplicate ruler made of integers. 59const AQ_RC_OK: i64 = 0 60const AQ_RC_REVIEW: i64 = 3 61const AQ_RC_REFUSE: i64 = 4 62 63func aq_res() -> *i64 { return sys_mmap(AQ_R_SLOTS * AQ_WORD) as *i64 } 64 65// THE DECISION. Inputs are facts, never intentions: 66// ev -- evidence class above, how we know the licence at all 67// redist -- the licence's redistribution right (nx_licgate_lib weights_redist), 0/1/2 68// verified -- the licence row's own verified column: 1 = its text was read, 0 = transcribed only 69// Returns AQ_RC_OK / AQ_RC_REVIEW / AQ_RC_REFUSE and fills res[AQ_R_*]. 70func aq_decide(ev: i64, redist: i64, verified: i64, res: *i64) -> i64 { 71 // ATTRIBUTION IS THE DEFAULT, AND IT IS AN OBLIGATION SO IT NEVER ABSTAINS. Every licence in the 72 // estate's rights table -- MIT, Apache-2.0, BSD, CC-BY, CC-BY-SA, CC-BY-NC, OpenRAIL, fair-ai, 73 // Coqui CPML -- requires notice retention or credit. Assuming the obligation costs one line in a 74 // NOTICE file; missing it is a breach. So the safe direction is YES, and under NO evidence at all 75 // it STAYS YES rather than degrading to REVIEW: "I could not look" must never lighten a duty. 76 res[AQ_R_ATTRIB] = AQ_YES 77 78 // LEARNING FROM PUBLISHED WORK IS NOT THE ACT A COPYRIGHT LICENCE GOVERNS -- copying and 79 // distributing are. Reading a public paper, running a published benchmark and measuring ourselves 80 // against it take nothing. This is what makes the artifact usable as an ORACLE, which is the whole 81 // point of taking it in. The separate question -- may we SELL what we built after learning -- is 82 // nx_licgate_lib's out_commercial and is deliberately NOT re-answered here. One ruler per question. 83 res[AQ_R_LEARN] = AQ_YES 84 85 // NO EVIDENCE => ABSTAIN ON THE COPYING RIGHTS. Not REFUSE, not ALLOW. 86 // REFUSE would make the librarian useless, because 923 permil of upstream rows arrive exactly like 87 // this, and a permanently-refusing door teaches every caller to route around it. ALLOW would be a 88 // fabricated permission. REVIEW is the honest third state AND it names the work that clears it: 89 // go and read the licence. Note this is not merely theoretical -- the StarDrinks row on that very 90 // page reads "To download this dataset you need to submit a request online", so a librarian that 91 // mirrored on silence would have breached a gated dataset on its first run. 92 // METADATA ABOUT A WORK IS NOT THE WORK: the record (name, url, description) is always safe to 93 // keep, so an artifact can be preserved against rot even while its BYTES stay unmirrored. 94 if ev == AQ_EV_NONE { 95 res[AQ_R_MIRROR] = AQ_REVIEW 96 res[AQ_R_REDIST] = AQ_REVIEW 97 return AQ_RC_REVIEW 98 } 99 100 // DECLARED BUT UNREAD CANNOT EXCEED REVIEW, however permissive the declared id looks. A listing 101 // page saying "Apache 2.0" is an upstream ASSERTION about a repository we have not opened; the 102 // repo may carry a different LICENSE file, a dual licence, or none. Same rule for a rights-table 103 // row whose own verified column is 0 -- DECLARED MUST NEVER BE SERVED AS MEASURED. 104 if ev == AQ_EV_DECLARED { 105 res[AQ_R_MIRROR] = AQ_REVIEW 106 res[AQ_R_REDIST] = AQ_REVIEW 107 return AQ_RC_REVIEW 108 } 109 if verified == 0 { 110 res[AQ_R_MIRROR] = AQ_REVIEW 111 res[AQ_R_REDIST] = AQ_REVIEW 112 return AQ_RC_REVIEW 113 } 114 115 // READ AND VERIFIED. We hold the licence text and its row was confirmed, so we may state the 116 // rights. Keeping our own copy of a work we lawfully obtained is the weakest act available and is 117 // granted here; passing it ON is the act the licence actually governs, so it takes the table's 118 // redistribution value verbatim -- including 0, which is a real REFUSE and must not be softened 119 // into REVIEW. "I looked and it is forbidden" is a finding; "I could not look" is an abstention. 120 res[AQ_R_MIRROR] = AQ_YES 121 res[AQ_R_REDIST] = redist 122 if redist == AQ_NO { return AQ_RC_REFUSE } 123 if redist == AQ_REVIEW { return AQ_RC_REVIEW } 124 return AQ_RC_OK 125} 126 127// name of a right value, for output that a human reads and a gate can assert on. 128func aq_right_name(v: i64) -> *u8 { 129 if v == AQ_YES { return "YES" as *u8 } 130 if v == AQ_REVIEW { return "REVIEW" as *u8 } 131 if v == AQ_NO { return "NO" as *u8 } 132 return "UNKNOWN" as *u8 133} 134 135func aq_ev_name(ev: i64) -> *u8 { 136 if ev == AQ_EV_READ { return "READ" as *u8 } 137 if ev == AQ_EV_DECLARED { return "DECLARED" as *u8 } 138 if ev == AQ_EV_NONE { return "NONE" as *u8 } 139 return "UNKNOWN" as *u8 140} 141 142func aq_rc_name(rc: i64) -> *u8 { 143 if rc == AQ_RC_OK { return "CLEAR" as *u8 } 144 if rc == AQ_RC_REVIEW { return "REVIEW" as *u8 } 145 if rc == AQ_RC_REFUSE { return "REFUSE" as *u8 } 146 return "UNKNOWN" as *u8 147} 148 149// parse an evidence word from a record row. Unknown text is NOT quietly NONE: it returns -1 so the 150// caller REFUSES a malformed row instead of silently downgrading it to the abstaining case, which 151// would read as an honest abstention and hide a broken producer. 152func aq_ev_parse(s: *u8) -> i64 { 153 var i: i64 = 0 154 let r: *u8 = "READ" as *u8 155 let d: *u8 = "DECLARED" as *u8 156 let n: *u8 = "NONE" as *u8 157 var mr: i64 = 1 158 while r[i] != (0 as u8) { if s[i] != r[i] { mr = 0 } i = i + 1 } 159 if mr == 1 { if s[i] == (0 as u8) { return AQ_EV_READ } } 160 i = 0 161 var md: i64 = 1 162 while d[i] != (0 as u8) { if s[i] != d[i] { md = 0 } i = i + 1 } 163 if md == 1 { if s[i] == (0 as u8) { return AQ_EV_DECLARED } } 164 i = 0 165 var mn: i64 = 1 166 while n[i] != (0 as u8) { if s[i] != n[i] { mn = 0 } i = i + 1 } 167 if mn == 1 { if s[i] == (0 as u8) { return AQ_EV_NONE } } 168 return 0 - 1 169} 170 171// ============================ FIXITY: IS THIS ALL OF THE BYTES? ============================ 172// 173// A DIGEST ANSWERS "ARE THESE THE BYTES I HASHED". ONLY A LENGTH ANSWERS "ARE THESE ALL THE BYTES". 174// When a body is truncated in transit and the digest is computed on what ARRIVED, the integrity check 175// PASSES BECAUSE IT WAS COMPUTED ON THE DAMAGE. Researched Aug-2026: neither in-toto's DigestSet nor 176// SPDX's verifiedUsing carries a byte length anywhere, so this failure is invisible to both. Our own 177// provenance row already carries `bytes` beside `sha256` -- and a census proved that field was written 178// and read by NOTHING. This is the reader. 179// 180// THE HEX LENGTH IS DERIVED FROM THE ALGORITHM, NEVER A HAND-PICKED CONSTANT. Measured in the field: 181// the Croissant specification's own canonical FileObject examples put SIX values in a property named 182// `sha256`, of which FOUR are 32 hex characters (an MD5 length) and one is 40 (SHA-1). A validator 183// that recomputes SHA-256 fails the specification's own examples. A field named for one hash routinely 184// carries another, so the NAME is a claim and the LENGTH is the check. 185const AQ_ALG_UNKNOWN: i64 = 0 186const AQ_ALG_MD5: i64 = 1 187const AQ_ALG_SHA1: i64 = 2 188const AQ_ALG_SHA256: i64 = 3 189const AQ_HEXLEN_MD5: i64 = 32 190const AQ_HEXLEN_SHA1: i64 = 40 191const AQ_HEXLEN_SHA256: i64 = 64 192 193// THE THREE STATES A FIXITY CHECK MUST HAVE. "I compared and they differ" and "I had nothing to 194// compare against" take OPPOSITE remedies -- one is a corrupted artifact, the other is an unrecorded 195// one -- so they must never arrive in the same word. UNAVAILABLE is not a soft pass. 196const AQ_FIX_MATCH: i64 = 0 197const AQ_FIX_UNAVAILABLE: i64 = 3 198const AQ_FIX_MISMATCH: i64 = 4 199 200func aq_alg_hexlen(alg: i64) -> i64 { 201 if alg == AQ_ALG_MD5 { return AQ_HEXLEN_MD5 } 202 if alg == AQ_ALG_SHA1 { return AQ_HEXLEN_SHA1 } 203 if alg == AQ_ALG_SHA256 { return AQ_HEXLEN_SHA256 } 204 return 0 - 1 205} 206 207// Does a digest STRING have the shape the algorithm it is filed under would produce? 208// An unknown algorithm returns 0: we cannot certify a shape we have no expected length for, and 209// guessing one would be the fabricated-fact defect wearing a checksum. 210func aq_digest_shape_ok(alg: i64, hexlen: i64) -> i64 { 211 let want: i64 = aq_alg_hexlen(alg) 212 if want < 0 { return 0 } 213 if hexlen != want { return 0 } 214 return 1 215} 216 217// THE FIXITY VERDICT. Both legs must agree, and BOTH must be present to say MATCH. 218// declared_bytes / declared_hexlen -- what the record says we should be holding 219// actual_bytes -- what we measured on the bytes in hand 220// digest_equal -- 1 iff the recomputed digest equals the recorded one 221// A negative declared_bytes or a negative declared_hexlen means the record never carried it, which is 222// UNAVAILABLE -- the in-toto / SPDX case. It is NOT a pass, because it is precisely the state in which 223// a truncated body verifies clean. 224func aq_fixity(alg: i64, declared_bytes: i64, actual_bytes: i64, declared_hexlen: i64, digest_equal: i64) -> i64 { 225 if declared_bytes < 0 { return AQ_FIX_UNAVAILABLE } 226 if declared_hexlen < 0 { return AQ_FIX_UNAVAILABLE } 227 // A MALFORMED DIGEST IS A MISMATCH, NOT AN ABSTENTION. The record does carry a value; it is the 228 // wrong shape for the algorithm it claims, so the record is WRONG rather than missing. 229 if aq_digest_shape_ok(alg, declared_hexlen) == 0 { return AQ_FIX_MISMATCH } 230 if declared_bytes != actual_bytes { return AQ_FIX_MISMATCH } 231 if digest_equal != 1 { return AQ_FIX_MISMATCH } 232 return AQ_FIX_MATCH 233} 234 235func aq_fix_name(v: i64) -> *u8 { 236 if v == AQ_FIX_MATCH { return "MATCH" as *u8 } 237 if v == AQ_FIX_MISMATCH { return "MISMATCH" as *u8 } 238 if v == AQ_FIX_UNAVAILABLE { return "UNAVAILABLE" as *u8 } 239 return "UNKNOWN" as *u8 240}