code wiki / _hdl_build / nx_job_run.nx
nx_job_run.nx
buildroot/runtime/_hdl_build/nx_job_run.nx
about
nx_job_run.nx v3 -- GUARDED ASYNC JOB RUNNER. Eats the async-fetch >15s edge-timeout debt the
good-form way: not a cron workaround, not an arbitrary executor.
SAFE BY CONSTRUCTION (rule 26 never-brick / rule 12 boundary-defense) -- FOUR fail-closed gates:
(1) CLASS: deny-by-class via nx_job_launch_bound -- WIDENED 2026-07-31, and this is exactly the
"filed rung" the previous line named for itself. The old rule launched ONLY organs whose
name contained *research_fetch*, which meant the ecosystem had NO route to run a general
organ and every generator fell back to shell. Now: generators, rollups, censuses and page
emitters are launchable; DAEMONS and SERVERS are refused (they never exit, so an async
launch wedges the slot forever) and so is the CONTROL PLANE (deploy/promote/restart/kill/
mgmt/hostctl/supervisor). Fail-closed on an empty name. Deny-by-class, not allow-by-one-
substring: a new generator is launchable the day it is allowlisted with no edit here, while
a new daemon is refused by default -- a rule nothing must remember beats a list somebody
must maintain. Proven by nx_job_launch_bound_gate 15/15 GREEN, including back-compat teeth
for every organ the old *research_fetch* rule allowed.
(2) VETTED: the name must resolve to a GREEN elf in tool_allowlist.conf -- the SAME trust
boundary nx_plan_run uses; NEVER an arbitrary caller-supplied path.
(3) NO-PIN-BYPASS: refuses pinned rows -- can never run a pinned elf with non-pinned args.
(4) SCRATCH-ONLY: out-file confined to /tmp/ with no '..' -- writes land only on disposable scratch.
Mechanism (mirrors nx_hostctl cmd_buildrun): fork -> child redirects stdout+stderr to out-file via
sys_dup3 + execve the resolved elf with the caller's [args...]; PARENT returns IMMEDIATELY with a
job handle (does NOT wait4) -> child is orphaned + reaped by init, runs to completion async while
/mcp already answered -> a slow research-fetch no longer times out the edge.
nx_job_run <tool-name(*research_fetch*)> <out-file:/tmp/...> [args...] -> JOB-STARTED pid=.. elf=.. out=..
license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
dependencies 3 imports · 0 importers
imports: nx_syscalls.nxnx_itoa_lib.nxnx_job_launch_bound.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
| 28 | const JR_MAGIC_1024: i64 = 1024 |
| 30 | const JR_OMODE: i64 = 0x1a4 |
| 31 | const JR_STDOUT: i64 = 1 |
| 32 | const JR_STDERR: i64 = 2 |
| 33 | const JR_MAXARGV: i64 = 32 |
| 34 | const JR_NL: i64 = 10 |
| 35 | const JR_TAB: i64 = 9 |
| 36 | const JR_HASH: i64 = 35 |
functions
| 38 | func jr_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } |
| 39 | func jr_puts(s: *u8) -> i64 { sys_write(JR_STDOUT, s, jr_slen(s)); return 0 } |
| 40 | func jr_werr(s: *u8) -> i64 { sys_write(JR_STDERR, s, jr_slen(s)); return 0 } |
| 45 | func jr_putn(v: i64) -> i64 { nxi_out(v); return 0 } |
| 46 | func jr_slice_eq(buf: *u8, start: i64, end: i64, s: *u8) -> i64 called by 1: main |
| 58 | func jr_contains(hay: *u8, needle: *u8) -> i64 |
| 72 | func jr_is_tmp(p: *u8) -> i64 called by 1: main |
| 80 | func jr_has_dotdot(p: *u8) -> i64 called by 1: main |
| 89 | func main(argc: i64, argv: *i64) -> i64 |