code wiki / (root) / nx_mcp_body_gate.nx

nx_mcp_body_gate.nx

buildroot/runtime/nx_mcp_body_gate.nx

22844 B400 linesdepth 9pulls 36 transitivereach 2 importersview sourcekind gate/prooftopic mcp
docsdependenciesstructsconstsfunctions

about

nx_mcp_body_gate.nx -- LANE K 2026-08-23. Referee for the MCP-wire body-read root fix in nx_tools_api_serve.nx (tsv_serve_one). THE DEFECT: the daemon read the whole request with ONE sys_read into a 64 KiB buffer, so a body over ~64 KiB (or one arriving in >1 TCP segment) was parsed as a FRAGMENT -- trailing argv/fields silently vanished (measured: a 131072 B write landed as 42045 B) and every source push had to be split into anchored hunks. THE FIX: tsv_serve_one composes the estate's own nx_http_server_read_request (the Content-Length read loop the daemon never used) + a conf-driven named reserve (tools_req_reserve.conf, one owner via EDGE_REQ_MAX) + a 413 that ANNOUNCES limit+received. IT RUNS, IT DOES NOT MERELY COMPILE: it starts the SUBJECT binary on a throwaway port and POSTs REAL bodies to /mcp over a real socket. The proof that the WHOLE body was read is that the JSON-RPC `id` -- placed at the very END of a 150 KB body -- is ECHOED in the response; a single-read daemon never sees it. T_BITE spawns the BANKED PRE-FIX binary and shows the SAME probe does NOT echo the end marker -- the non-vacuity law made executable: the tooth fires on the bad binary and is silent on the good one. license_tier: ORIGINAL

dependencies 4 imports · 2 importers

nx_http_server.nx nx_connect.nx nx_gate_verdict.nx nx_tools_api.nx nx_mcp_body_gate.nx nx_job_connected_gate_t183.nx nx_route_period_gate_t184.nx

imports: nx_http_server.nxnx_connect.nxnx_gate_verdict.nxnx_tools_api.nx

imported by: nx_job_connected_gate_t183.nxnx_route_period_gate_t184.nx

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

main 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 ↻ mbg_cat mbg_catn mbg_post nx_http_server_addr_loopba nx_http_server_make_sockad nx_connect_bounded nx_fcntl sys_connect sys_mmap ↻ sys_poll sys_munmap mbg_cat ↻ mbg_catn ↻ gv_check gv_puts sys_write ↻ mbg_spawn mbg_catn ↻ mbg_has mbg_slen mbg_diag mbg_slen ↻ mbg_catn ↻

structs

none

consts

20const MBG_STAGED: *u8 = "/volume1/homes/elderwesto/nishihost/nx_tools_api_serve.sov.elf.new" as *u8
21const MBG_PREFIX: *u8 = "/volume1/homes/elderwesto/nishihost/knowledge/bank/nx_tools_api_serve.elf.pre-bodycap-d3b23e08" as *u8
22const MBG_PORT_OK: i64 = 18993
23const MBG_PORT_BAD: i64 = 18994
24const MBG_DEADPORT: i64 = 18995
25const MBG_SRVLOG_OK: *u8 = "/tmp/nx_mcp_body_srv_ok.out" as *u8
26const MBG_SRVLOG_BAD: *u8 = "/tmp/nx_mcp_body_srv_bad.out" as *u8
27const MBG_RESP_CAP: i64 = 1048576
28const MBG_FULL_BYTES: i64 = 150000 // > 64 KiB single-read window, < the 1 MiB ceiling: MUST fully read
29const MBG_SMALL_BYTES: i64 = 1024 // trivially fits: the fix must not regress small bodies
30const MBG_OVER_BYTES: i64 = 1300000 // > EDGE_REQ_MAX (1 MiB): must 413 naming limit + received
31const MBG_CEIL: i64 = 1048576 // EDGE_REQ_MAX / default reserve (edge plaincap-derived)
32const MBG_BIND_WAIT_MS: i64 = 4000
42const MBG_FIX1: *u8 = "/tmp/nx_mcp_body_gate_fx1" as *u8
43const MBG_FIX2: *u8 = "/tmp/nx_mcp_body_gate_fx2" as *u8
44const MBG_PORT_P1: i64 = 18996
45const MBG_PORT_P2: i64 = 18997
46const MBG_SRVLOG_P1: *u8 = "/tmp/nx_mcp_body_srv_p1.out" as *u8
47const MBG_SRVLOG_P2: *u8 = "/tmp/nx_mcp_body_srv_p2.out" as *u8
48const MBG_FX_WINDOW_MS: i64 = 3000 // fixture edge window (conf row); the promote decision = window - reserve
49const MBG_FX_RESERVE_MS: i64 = 1000
50const MBG_FX_SLEEP_MS: i64 = 4500 // > window: the worker is genuinely mid-run at the decision
51const MBG_FX_RELEASE_WAIT_S: i64 = 10 // after releasing the wall, how long a late reply may take
52const MBG_SYS_OPENAT: i64 = 257 // x86-64 openat (not an rv64 key: passes the translator through)
53const MBG_SYS_MKNODAT: i64 = 259 // x86-64 mknodat (the incumbent nx_autorun_daemon uses exactly this)
54const MBG_AT_FDCWD: i64 = 0 - 100
55const MBG_S_IFIFO_0644: i64 = 4516 // S_IFIFO (0o010000 = 4096) | 0644 (420)
56const MBG_O_RDONLY_NONBLOCK: i64 = 2048 // O_RDONLY(0) | O_NONBLOCK(0x800): release the wall without ever blocking the gate
57const MBG_DIRMODE: i64 = 493 // 0755
58const MBG_CAP_TTL_S: i64 = 3600
59const MBG_EEXIST: i64 = 0 - 17
61const MBG_CAP_TOKEN_BYTES: i64 = 1024 // a minted cap: allow~exp~nonce.sig, base64url -- far under 1 KiB
62const MBG_CALL_BODY_BYTES: i64 = 4096 // the tools/call JSON envelope + one short argv + the cap
63const MBG_REQ_HEADROOM_BYTES: i64 = 8192 // HTTP request line + headers on top of any body (a handful of lines)
64const MBG_ROW_BYTES: i64 = 4096 // one actlog row drained from the FIFO wall

functions

67func mbg_pid() -> i64 { return __syscall(172, 0, 0, 0, 0, 0, 0) }
called by 1: mbg_post_call
68func mbg_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
69func mbg_cat(d: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { d[o + i] = s[i]; i = i + 1 } return o + i }
70func mbg_catn(d: *u8, o: i64, v: i64) -> i64
78func mbg_has(buf: *u8, n: i64, pat: *u8) -> i64
called by 3: mbg_post_callmainr184_case calls 1: mbg_slen
92func mbg_spawn(elf: *u8, port: i64, logf: *u8) -> i64
called by 1: main calls 1: mbg_catn
111func mbg_kill(pid: i64) -> i64
called by 3: mainmainr184_case
126func mbg_spawn_cwd(elf: *u8, port: i64, logf: *u8, cwd: *u8) -> i64
called by 3: mainmainr184_case calls 1: mbg_catn
145func mbg_writefile(path: *u8, content: *u8) -> i64
called by 1: mbg_fixture calls 1: mbg_slen
151func mbg_path(d: *u8, a: *u8, b: *u8) -> i64 { var o: i64 = mbg_cat(d, 0, a); o = mbg_cat(d, o, b); d[o] = 0 as u8; return o }
153func mbg_fixture(root: *u8) -> i64
170func mbg_release_wall(root: *u8, out: *u8, cap: i64) -> i64
called by 1: main calls 1: mbg_path
189func mbg_post_call(port: i64, sleep_ms: i64, wait_s: i64, out: *u8, outcap: i64, out_ms: *i64, out_fd: *i64) -> i64
233func mbg_read_late(fd: i64, wait_s: i64, out: *u8, outcap: i64) -> i64
called by 1: main
252func mbg_post(port: i64, nbytes: i64, out: *u8, outcap: i64) -> i64
297func mbg_diag(tag: *u8, buf: *u8, n: i64) -> i64
called by 1: main calls 2: mbg_slenmbg_catn
305func main() -> i64