nx_tool_exec_allow.nx
buildroot/runtime/nx_tool_exec_allow.nx
about
nx_tool_exec_allow.nx -- R1 of the executable-API rung: the EXECUTION allowlist that gates R0's raw
exec+capture primitive (nx_tool_run) so /mcp tools/call can only ever run VETTED, gate-GREEN organs.
This is the never-brick + no-confused-deputy boundary: a caller passes a tool NAME (already capability-
authorized upstream in ta_mcp_call); this layer maps that name -> an absolute ELF path ONLY if the name
matches an explicit allowlist row whose gate-status is the literal "GREEN". A caller can NEVER supply a
path, a "..", or a shell string -- those simply do not resolve.
SEPARATE from nx_tool_registry.nx (the DISCOVERY registry that feeds /api/tools + tools/list): discovery
answers "what tools exist"; this answers "which of them may be EXECUTED, and from which exact ELF". Kept
apart on purpose -- listing a tool must never imply it is runnable. Data-driven (rule 11): the operator
curates `tool_allowlist.conf`, so vetting a tool for execution is a config edit + a gate run, not a code
change. Missing file / missing row / non-GREEN row -> refused (fail-closed).
Row format (TAB-separated; '#' comment lines and blank lines ignored):
<tool_name>\t<absolute_elf_path>\t<gate_status>
license_tier: ORIGINAL
dependencies 1 imports · 6 importers
imports: nx_tool_run.nx
imported by: nx_cr_exec_proof.nxnx_tea_pinned_gate.nxnx_tool_exec_allow_gate.nxnx_tools_api.nxnx_vizsla_mcp_gate.nxnx_vizsla_notify_mcp_gate.nx
structs
| none |
consts
| 17 | const TEA_MAGIC_4096: i64 = 4096 |
| 18 | const TEA_MAGIC_262144: i64 = 262144 |
| 20 | const TEA_CONF: *u8 = "tool_allowlist.conf" as *u8 // relative to the server CWD (same convention as cap_revoked.list) |
| 23 | const TEA_OK: i64 = 1 // allowlisted AND gate-GREEN -> out_path filled, runnable |
| 24 | const TEA_BLOCKED: i64 = 0 // allowlisted but gate-status != GREEN -> present, NOT runnable |
| 25 | const TEA_NOTFOUND: i64 = 0 - 1 // no such tool in the allowlist |
| 26 | const TEA_NOCONF: i64 = 0 - 2 // allowlist file absent/unreadable (fail-closed) |
| 128 | const TEA_EXEC_TIMEOUT_MS: i64 = 120000 |
| 149 | const TEA_EDGE_WINDOW_MS: i64 = 15000 |
| 150 | const TEA_EXEC_TIMEOUT_SYNC_MS: i64 = TEA_EDGE_WINDOW_MS - 1000 |
functions
| 29 | func tea_field_eq(buf: *u8, s: i64, e: i64, key: *u8) -> i64 |
| 40 | func tea_field_eq_n(buf: *u8, s: i64, e: i64, name: *u8, nlen: i64) -> i64 |
| 50 | func tea_name_ok(name: *u8, nlen: i64) -> i64 |
| 70 | func tea_resolve_from(conf: *u8, name: *u8, nlen: i64, out_path: *u8, out_cap: i64) -> i64 |
| 114 | func tea_resolve(name: *u8, nlen: i64, out_path: *u8, out_cap: i64) -> i64 calls 1: tea_resolve_from |
| 156 | func tea_run_from(conf: *u8, name: *u8, nlen: i64, arg: *u8, out: *u8, out_cap: i64, outlen: *i64, rc: *i64) -> i64 |
| 165 | func tea_run(name: *u8, nlen: i64, arg: *u8, out: *u8, out_cap: i64, outlen: *i64, rc: *i64) -> i64 calls 1: tea_run_from |
| 174 | func tea_run_argv_from(conf: *u8, name: *u8, nlen: i64, argv: *i64, out: *u8, out_cap: i64, outlen: *i64, rc: *i64) -> i64 |
| 184 | func tea_run_argv(name: *u8, nlen: i64, argv: *i64, out: *u8, out_cap: i64, outlen: *i64, rc: *i64) -> i64 calls 1: tea_run_argv_from |
| 197 | func tea_read_conf(path: *u8, out_len: *i64) -> *u8 |
| 218 | func tea_tokenize(s: *u8, argv: *i64, base: i64, scratch: *u8, scap: i64) -> i64 |
| 240 | func tea_resolve_pinned_from(conf: *u8, name: *u8, nlen: i64, out_path: *u8, out_cap: i64, out_pin: *u8, pin_cap: i64, out_haspin: *i64) -> i64 |
| 301 | func tea_run_pinned_from_to(conf: *u8, name: *u8, nlen: i64, callerargv: *i64, out: *u8, out_cap: i64, outlen: *i64, rc: *i64, tmo: i64) -> i64 |
| 321 | func tea_run_pinned_from(conf: *u8, name: *u8, nlen: i64, callerargv: *i64, out: *u8, out_cap: i64, outlen: *i64, rc: *i64) -> i64 |
| 324 | func tea_run_pinned_to(name: *u8, nlen: i64, callerargv: *i64, out: *u8, out_cap: i64, outlen: *i64, rc: *i64, tmo: i64) -> i64 |
| 327 | func tea_run_pinned(name: *u8, nlen: i64, callerargv: *i64, out: *u8, out_cap: i64, outlen: *i64, rc: *i64) -> i64 |