nx_content_get_client.nx
buildroot/runtime/nx_content_get_client.nx
about
nx_content_get_client.nx -- THE SOVEREIGN NAS->laptop DOWNLOAD CLIENT. Retires nx_content_get_client.py.
WHY THIS EXISTS (operator standing order, restated 2026-09-04: "we shouldnt be using ps1 or py or
anything else in our build path only as external tooling being liar killers and validators etc").
nx_content_get has been the sovereign SOURCE since 2026-09-03 and its only driver was a Python script
written on 2026-09-04 that shells out to a Python MCP wire. A SHIPPER IS THE BUILD PATH BY DEFINITION,
so it can never be one of the external oracles that order permits. This is the NishiLang driver.
Its outbound sibling nx_content_put_client.nx made exactly this move for the write direction and its
own first line says so; this file is the half that was missing.
nx_content_get_client [dryrun] <remote_src> <local_dest> <capfile> <url>
MEASURED COST OF THE GAP IT CLOSES (hostops debt 1788534921, 2026-09-04): with the NAS array degraded
and every NAS build refused, the laptop farm could compile and SHIP but could not BITE, because its
nx_gate_bite.nx was 39,842 bytes against the NAS 98,879 and nothing could refresh it without a human
copy or retyping bytes through an agent transcript. An outbound-only lane is a fallback that works
only until the thing you need is on the other side.
THE PROTOCOL IS THE SERVER OWN AND IT IS STATELESS, WHICH CHANGES THIS CLIENT SHAPE:
begin <src> -> CG-BEGIN src= total_bytes= sha256= chunk_raw= nchunks=
chunk <src> <index> -> CG-CHUNK OK ... chunk_sha256= b64=<data>
There is no transfer id and no commit: in a get the bytes accumulate on the CLIENT, so there is no
server-side state to open, resume or close. The put client id-threading and commit-unknown handling
are therefore ABSENT rather than mirrored -- carrying them would be machinery with no invariant to
protect, which the server own header names as the asymmetry a symmetric copy gets wrong.
NOTHING IS WRITTEN TO <local_dest> UNTIL THE WHOLE-FILE DIGEST MATCHES.
The server header states the exposure precisely: the SOURCE can change under a multi-call read, so
chunks reassembled from two generations give a franken-file that is byte-valid and semantically
nonsense. The per-chunk digest catches a CORRUPTED chunk and CANNOT catch a consistent read of a
CHANGED file; only the whole-file sha256 declared by begin can. So this client assembles in memory,
verifies, then writes ONCE. A partial or franken download must never be able to masquerade as a
refreshed source -- and a size check would not do, because a same-size rewrite passes it.
THE CHUNK SIZE IS READ FROM THE SERVER RECEIPT, NEVER COMPUTED HERE. nx_content_get derives it from
the RESPONSE capture cap (163,840) rather than the request cap, which is the one term a symmetric copy
of the upload client would get wrong. Recomputing it here would install a second ruler that drifts the
moment the server reserve changes, and the drift would surface as truncated downloads.
A SERVER DECISION IS NEVER RETRIED, AND IT HAS TWO VOCABULARIES.
dependencies 5 imports · 0 importers
imports: nx_syscalls.nxnx_base64.nxnx_sha256.nxnx_https_post_lib.nxnx_jobfollow_lib.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 62 | const CG_DIGEST: i64 = 32 |
| 63 | const CG_SHAHEX: i64 = 64 |
| 64 | const CG_RESP: i64 = 262144 |
| 65 | const CG_REQ: i64 = 262144 |
| 66 | const CG_NUMBUF: i64 = 32 |
| 67 | const CG_MODE_RW: i64 = 420 |
| 70 | const CG_POLLS: i64 = 40 |
| 71 | const CG_POLL_MS: i64 = 3000 |
| 76 | const CG_PRE: *u8 = "{\x22jsonrpc\x22:\x222.0\x22,\x22id\x22:1,\x22method\x22:\x22tools/call\x22,\x22params\x22:{\x22name\x22:\x22nx_content_get\x22,\x22arguments\x22:{\x22argv\x22:[" |
| 77 | const CG_TAIL: *u8 = "}}}" |
| 78 | const CG_QUOTE: i64 = 34 |
| 79 | const CG_BACKSLASH: i64 = 92 |
| 80 | const CG_SPACE: i64 = 32 |
| 81 | const CG_DEL: i64 = 127 |
functions
| 83 | func cg_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } |
| 84 | func cg_out(s: *u8) -> i64 { sys_write(1, s, cg_slen(s)); return 0 } |
| 85 | func cg_eo(s: *u8) -> i64 { sys_write(2, s, cg_slen(s)); return 0 } |
| 86 | func cg_num(v: i64) -> i64 |
| 95 | func cg_itoa(v: i64, out: *u8) -> i64 |
| 105 | func cg_hex_into(d: *u8, out: *u8) -> i64 called by 1: main |
| 118 | func cg_json_safe(s: *u8, n: i64) -> i64 |
| 132 | func cg_find(buf: *u8, n: i64, needle: *u8) -> i64 |
| 146 | func cg_int_after(buf: *u8, n: i64, key: *u8) -> i64 |
| 165 | func cg_b64ch(c: i64) -> i64 called by 1: main |
| 174 | func cg_put_arg(req: *u8, off: i64, val: *u8, vlen: i64, first: i64) -> i64 |
| 185 | func cg_put_lit(req: *u8, off: i64, lit: *u8) -> i64 |
| 192 | func cg_streq(a: *u8, b: *u8) -> i64 called by 1: main |
| 202 | func cg_decided(buf: *u8, n: i64) -> i64 |
| 211 | func cg_is_readcap(a: *u8) -> i64 called by 1: main |
| 221 | func main(argc: i64, argv: *i64) -> i64 |