code wiki / (root) / nx_toolsapi_bodycap_gate.nx

nx_toolsapi_bodycap_gate.nx

buildroot/runtime/nx_toolsapi_bodycap_gate.nx

18751 B342 linesdepth 8pulls 15 transitivereach 0 importersview sourcekind gate/proof
docsdependenciesstructsconstsfunctions

about

nx_toolsapi_bodycap_gate.nx -- END-TO-END referee for the tools-API request-body fix (2026-08-23). THE DEFECT IT GUARDS: nx_tools_api_serve read a request with ONE sys_read of 64 KiB and handed the partial bytes to the edge; every tools/call above that answered 413, and the laptop wire rendered the 413 as a bare {} -- a whole-file source write read as "dropped" and nobody could say why. Behind it, the argv scratch in ta_mcp_call was a second 64 KiB constant that kept "what fit": a file content above 65535 bytes was written as its prefix with an OK receipt. HOW IT PROVES IT: forks the SUBJECT daemon (default: the STAGED ./nx_tools_api_serve.sov.elf.new; argv[1] `live` = the serving-root binary; any other argv[1] = an explicit elf path) with CWD = /tmp/<gate>/ carrying a fixture cap secret and a one-row allowlist (argecho, the estate's multi-arg witness organ), mints a cap for that secret in-process with the estate's own capt_issue, and drives REAL loopback POST /mcp tools/call requests whose "name" key comes LAST -- after the payload -- so a truncated body can never name the tool. T1 8 KB argv echoed whole (positive control: fits one read on the old and the new binary) T1b the 8 KB reply is BYTE-IDENTICAL to the LIVE daemon's reply on a second port (behaviour control) T2 63 KB argv echoed whole (the measured failing case: the request exceeds 65536 bytes on the wire) T3 120 KB argv echoed whole (over the old window, under TA_CAPTURE_CAP so the echo itself is not cut) T4 neg-control: a request declaring Content-Length above EDGE_REQ_MAX answers 413 naming the limit T5 no reply is the bare {} nor the old "64KiB edge limit" text T6 fixture reached the condition: the T2 request really was > 65536 bytes T7 the subject child is gone after the gate (its port refuses again) -- a probe that leaves a daemon is a second instance A free port is PROVEN free (connect refused) before the subject binds it. license_tier: ORIGINAL expect_exit: 0 GREEN | 1 RED | 3 SKIP

dependencies 5 imports · 0 importers

nx_syscalls.nx nx_connect.nx nx_http_server.nx nx_cap_token.nx nx_gate_verdict.nx nx_toolsapi_bodycap_gate.nx

imports: nx_syscalls.nxnx_connect.nxnx_http_server.nxnx_cap_token.nxnx_gate_verdict.nx

imported by: nobody (leaf or entry point)

call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown

main gv_head gv_puts sys_write gv_ctr sys_mmap nxa_die sys_write ↻ sys_exit nxa_lock_take nxa_lock_addr sys_write ↻ nxa_lock_give nxa_lock_addr ↻ nxa_report_overrun sys_write ↻ nxa_dump_printable sys_write ↻ nxa_dump_sizes sys_write ↻ bg_atoi gv_puts ↻ gv_num sys_mmap ↻ sys_write ↻ sys_munmap sys_openat_rd sys_close bg_connect sys_mmap ↻ nx_http_server_addr_loopba nx_http_server_make_sockad sys_socket nx_connect_bounded nx_fcntl sys_connect sys_mmap ↻ sys_poll sys_munmap ↻ sys_close ↻

structs

none

consts

30const BG_FIX_DIR: *u8 = "/tmp/nx_toolsapi_bodycap_gate"
31const BG_STAGED: *u8 = "/volume1/homes/elderwesto/nishihost/nx_tools_api_serve.sov.elf.new"
32const BG_LIVE: *u8 = "/volume1/homes/elderwesto/nishihost/nx_tools_api_serve.elf"
33const BG_ARGECHO: *u8 = "/volume1/homes/elderwesto/nishihost/nx_tool_argecho.elf"
34const BG_SECRET: *u8 = "bodycap-gate-fixture-secret-not-a-production-key"
35const BG_PORT_SUBJECT_DEFAULT: i64 = 18877 // argv[2] overrides; verified FREE before use; refused-again after the kill
36const BG_PORT_CONTROL_DEFAULT: i64 = 18878 // argv[3] overrides
37const BG_SOCK_TMO_S: i64 = 20 // a reply that does not arrive in 20 s is a hang, reported as one -- never a parked gate
38const BG_KB: i64 = 1024
39const BG_PAYLOAD_SMALL: i64 = 8 * BG_KB
40const BG_PAYLOAD_EDGE: i64 = 64 * BG_KB // the measured failing class: with the envelope the request exceeds 65536 on the wire
42const BG_PAYLOAD_BIG: i64 = 120 * BG_KB // over the old window; under TA_CAPTURE_CAP (163840) so the ECHO is whole
43const BG_OLD_WINDOW: i64 = 65536
44const BG_OVER_LIMIT_DECLARED: i64 = 2 * 1048576 // above EDGE_REQ_MAX (= EDGE_RESP_CAP = 1 MiB)
45const BG_RESP_CAP: i64 = 2097152 // reply reserve: an echo of BG_PAYLOAD_BIG plus envelope, announced if it fills
46const BG_BOOT_TRIES: i64 = 100 // 100 x 100 ms = 10 s for the child to reach listen
47const BG_BOOT_STEP_MS: i64 = 100
48const BG_CONNECT_MS: i64 = 3000
49const BG_SIGKILL: i64 = 9
50const BG_CAP_TTL_S: i64 = 600

functions

51func bg_atoi(s: *u8) -> i64 { var v: i64 = 0; var i: i64 = 0; while s[i] != (0 as u8) { let c: i64 = s[i] as i64; if c >= 48 { if c <= 57 { v = v * 10 + (c - 48) } } i = i + 1 } return v }
called by 1: main
53func bg_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
called by 2: bg_hasmain
54func bg_cat(d: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; var p: i64 = o; while s[i] != (0 as u8) { d[p] = s[i]; p = p + 1; i = i + 1 } d[p] = 0 as u8; return p }
called by 2: bg_buildmain
55func bg_catn(d: *u8, o: i64, v: i64) -> i64
called by 3: bg_spawnbg_buildmain calls 1: sys_mmap
68func bg_has(hay: *u8, hn: i64, needle: *u8) -> i64
called by 1: main calls 1: bg_slen
82func bg_xrun(b: *u8, n: i64) -> i64
called by 1: main
92func bg_write_file(path: *u8, data: *u8, n: i64) -> i64
101func bg_connect(port: i64) -> i64
111func bg_spawn(elf: *u8, port: i64) -> i64
134func bg_wait_listen(port: i64) -> i64
145func bg_build(req: *u8, plen: i64, cap: *u8) -> i64
called by 1: main calls 3: sys_mmapbg_catbg_catn
161func bg_roundtrip(port: i64, req: *u8, rn: i64, out: *u8, cap: i64, filled: *i64) -> i64
180func bg_head(b: *u8, n: i64) -> i64
called by 1: main calls 1: sys_write
189func bg_kill(pid: i64) -> i64
called by 1: main calls 3: nx_killsys_mmapsys_wait4
198func main(argc: i64, argv: *i64) -> i64