nx_content_get_client_gate.nx
buildroot/runtime/nx_content_get_client_gate.nx
about
nx_content_get_client_gate.nx -- THE GATE FOR THE SOVEREIGN DOWNLOAD CLIENT, 2026-09-04.
SUBJECT: the BUILT nx_content_get_client elf, forked in `dryrun` mode so the JSON-RPC envelope it
would put on the wire becomes a deterministic artifact with no socket involved.
WHY dryrun IS THE RIGHT SUBJECT, and it is the sibling's reasoning inherited rather than re-derived:
a malformed envelope is rejected by the SERVER, and the server's refusal names neither the client nor
the real fault -- it reports a schema or argument error about a request nobody can see. The upload
client's first draft carried a DOUBLED params key with a four-brace tail that balanced against itself:
syntactically valid JSON of entirely the wrong SHAPE, which no live run would have named. T2 is here
because this client copies that constant's structure and could copy its defect.
THE ASYMMETRY WORTH STATING: the upload gate carries an EXTERNAL SHA-256 KAT, because its client
hashes the local file before begin and the digest it sends is checkable against the FIPS 180-2
published value for "abc". THIS CLIENT HASHES NOTHING AT BEGIN -- in a get the digest is DECLARED BY
THE SERVER and the client's job is to verify its reassembly against it. So there is no KAT to run
here, and inventing one would be a tooth that tests nothing. That absence is declared rather than
quietly omitted, because a reader comparing the two gates should be able to see WHY they differ.
T3 IS THE TOOTH THE UPLOAD GATE CANNOT HAVE, and it is the one that guards this client's whole
premise. In a get the client knows NEITHER the total size NOR the whole-file digest -- both are the
server's, read from its begin receipt. So the begin argv must contain EXACTLY TWO elements: the verb
and the source path. If some future edit ever has this client compute a size or a digest and send it,
the argv grows and this tooth fires. That is the "the chunk size is the server's, never computed here"
rule made mechanical instead of merely written in a comment.
T6/T7 ARE THE REFUSAL PAIR, on the sibling's precedent. T6 requires a source carrying a double-quote
to be REFUSED rather than escaped, because a hand-rolled JSON escaper is wrong in exactly the cases
nobody tests. On its own T6 would pass on a client that refused EVERYTHING, so T7 requires a safe
source to be ACCEPTED. Neither is evidence without the other.
Teeth, in order:
T1 the envelope's braces and brackets balance.
T2 "params" appears EXACTLY ONCE (the doubled-key defect that shipped in the sibling's first draft).
T3 the begin argv carries EXACTLY TWO elements -- the client sends no size and no digest of its own.
T4 the remote source appears verbatim in the envelope.
T5 the capability token appears verbatim (a client that dropped it would 401 for the wrong reason).
T6 NEG-CONTROL: a source containing a quote is REFUSED (exit 4), never escaped.
T7 POSITIVE CONTROL: a safe source is ACCEPTED (exit 0), so T6 cannot pass on a refuse-everything client.
T8 the argv array opens with the begin verb.
dependencies 3 imports · 0 importers
imports: nx_syscalls.nxnx_gate_verdict.nxnx_tool_run.nx
imported by: nobody (leaf or entry point)
structs
| none |
consts
| 47 | const CGG_S_A: *u8 = "buildroot/_build/nx_content_get_client.sov.elf" as *u8 |
| 48 | const CGG_S_B: *u8 = "_build/nx_content_get_client.sov.elf" as *u8 |
| 49 | const CGG_S_C: *u8 = "_offc/nx_content_get_client.elf" as *u8 |
| 50 | const CGG_S_D: *u8 = "nx_content_get_client.elf" as *u8 |
| 51 | const CGG_DIR: *u8 = "/tmp/cgcli" as *u8 |
| 52 | const CGG_CAPF: *u8 = "/tmp/cgcli/cap.txt" as *u8 |
| 53 | const CGG_NOCAP: *u8 = "/tmp/cgcli/no_such_cap.txt" as *u8 |
| 54 | const CGG_CAP: i64 = 65536 |
| 55 | const CGG_MODE: i64 = 420 |
| 56 | const CGG_SRC: *u8 = "runtime/nx_gate_bite.nx" as *u8 |
| 57 | const CGG_DEST: *u8 = "/tmp/cgcli/pulled.nx" as *u8 |
| 58 | const CGG_CAPTOK: *u8 = "tok~1~abcDEF.ghi-jkl" as *u8 |
| 59 | const CGG_URL: *u8 = "https://example.invalid/mcp" as *u8 |
| 60 | const CGG_LBRACE: i64 = 123 |
| 61 | const CGG_RBRACE: i64 = 125 |
| 62 | const CGG_LBRACK: i64 = 91 |
| 63 | const CGG_RBRACK: i64 = 93 |
| 64 | const CGG_COMMA: i64 = 44 |
functions
| 66 | func cgg_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } |
| 67 | func cgg_count(buf: *u8, n: i64, needle: *u8) -> i64 |
| 81 | func cgg_has(buf: *u8, n: i64, needle: *u8) -> i64 |
| 85 | func cgg_byte_count(buf: *u8, n: i64, b: i64) -> i64 |
| 93 | func cgg_argv_len(buf: *u8, n: i64) -> i64 |
| 111 | func cgg_write_file(path: *u8, buf: *u8, n: i64) -> i64 |
| 125 | func cgg_exists(p: *u8) -> i64 |
| 131 | func cgg_resolve() -> *u8 |
| 138 | func cgg_run(subj: *u8, src: *u8, capf: *u8, out: *u8, ol: *i64) -> i64 |
| 150 | func main(argc: i64, argv: *i64) -> i64 |