nx_proc_spawn.nx source
↩ module page · 166 lines · 7651 B
1// nx_proc_spawn.nx -- HIGH-LEVEL spawn-with-capability-gate layer above
2// the FULLY_WIRED nx_proc.nx primitive (fork/exec/wait4 syscall wrappers).
3//
4// AUDIT-FIRST CORRECTION (2026-05-20, post-creation): user caught that I
5// built this without checking for existing prior art. nx_proc.nx already
6// ships with nx_proc_fork / nx_proc_execve / nx_proc_wait4 / wait-status
7// inspectors. This primitive does NOT duplicate those -- it adds the
8// capability-token + priv-class + log-redirect layer ABOVE them. When
9// graduated, nx_proc_spawn_execute will CALL nx_proc_fork + nx_proc_execve
10// internally; it is the supervised wrapper, not a competing implementation.
11// Per feedback-no-temporary-third-party-scaffolding-when-substrate-arc-exists
12// audit-first rule: ls nxc2/runtime/ before authoring new primitives.
13//
14// Composes with the existing nx_supervisor state-machine primitive (which
15// decides what to do); THIS primitive does the actual process management.
16//
17// module: nishi-core.ops.proc_spawn
18// depends: nishi-core.perception.profile + nishi-core.ops.supervisor +
19// nishi-core.io.syscalls
20// disk_kb: 5
21// capability: OPS
22// wired_status: PARTIAL_WIRED
23//
24// MISSING_CAPABILITIES:
25// - FORK_EXEC_SETSID (sys_fork + sys_execve + sys_setsid; sys_fork is
26// proven via nishi_video_room.nx, sys_execve glue needed)
27// - CAPABILITY_TOKEN_VERIFY (content-addressed capability proof that
28// replaces /etc/sudoers.d/* NOPASSWD entries with cryptographic
29// capability bearer tokens; composes with nx_ed25519 + nx_blake2b
30// which are FULLY_WIRED per status doc)
31// - PRIV_DROP (after fork, child drops privilege per NX_SUPV_PRIV_*
32// declared class before execve)
33// - FD_HYGIENE (close-on-exec all inherited fds except declared ones)
34// - STDIO_REDIRECT (per-child stdout/stderr to substrate-managed log files)
35//
36// license_tier: PUBLIC_NISHI_SUBSTRATE
37// genealogy_id: feedback-no-temporary-third-party-scaffolding-when-substrate-arc-exists_2026 +
38// feedback-substrate-does-heavy-lifting-user-is-partner-not-gate +
39// nx_supervisor (existing state-machine primitive) +
40// nx_daemon_health_audit
41//
42// SCAFFOLDING_REPLACED_BY: this primitive replaces:
43// - nohup spawn pattern in install-video.sh + install-voxels.sh + every
44// other install-NAME.sh
45// - Synology DSM init-script daemon launch
46// - /etc/sudoers.d/nishi-engine-deploy NOPASSWD blanket entries (replaced
47// by per-capability-class tokens)
48// - bench/ops/run_if_dead.sh pattern when it exists
49//
50// Reuse set: every nx_* daemon spawn + every install-NAME.sh executor +
51// dog toy embedded firmware boot + livestock-gateway service startup +
52// every production deployment in any Nishi product.
53
54import "nx_syscalls.nx"
55import "nx_perceptual_profile.nx"
56import "nx_supervisor.nx"
57
58// ===== Spawn verdicts ============================================
59
60const NX_PSPAWN_OK: i64 = 0
61const NX_PSPAWN_SPAWNED: i64 = 1
62const NX_PSPAWN_FAIL_FORK: i64 = 2
63const NX_PSPAWN_FAIL_EXEC: i64 = 3
64const NX_PSPAWN_FAIL_BAD_BINARY: i64 = 4 // path missing / not exec
65const NX_PSPAWN_FAIL_CAPABILITY_REFUSED: i64 = 5 // requested priv exceeds
66 // declared class
67const NX_PSPAWN_FAIL_PRIV_DROP: i64 = 6 // child couldn't drop priv
68const NX_PSPAWN_FAIL_DEPENDENCY_MISSING: i64 = 7 // PARTIAL_WIRED default
69
70func nx_pspawn_verdict_name(v: i64) -> *u8 {
71 if v == NX_PSPAWN_OK { return "OK" }
72 if v == NX_PSPAWN_SPAWNED { return "SPAWNED" }
73 if v == NX_PSPAWN_FAIL_FORK { return "FAIL_FORK" }
74 if v == NX_PSPAWN_FAIL_EXEC { return "FAIL_EXEC" }
75 if v == NX_PSPAWN_FAIL_BAD_BINARY { return "FAIL_BAD_BINARY" }
76 if v == NX_PSPAWN_FAIL_CAPABILITY_REFUSED { return "FAIL_CAPABILITY_REFUSED" }
77 if v == NX_PSPAWN_FAIL_PRIV_DROP { return "FAIL_PRIV_DROP" }
78 if v == NX_PSPAWN_FAIL_DEPENDENCY_MISSING { return "FAIL_DEPENDENCY_MISSING" }
79 return "UNKNOWN_PSPAWN_VERDICT"
80}
81
82// ===== Privilege class (matches nx_supervisor sealed enum) =======
83
84const NX_PSPAWN_PRIV_USER: i64 = 1
85const NX_PSPAWN_PRIV_NET_BIND_LOW_PORT: i64 = 2
86const NX_PSPAWN_PRIV_FS_WRITE_PROTECTED: i64 = 3
87const NX_PSPAWN_PRIV_DEVICE_ACCESS: i64 = 4
88const NX_PSPAWN_PRIV_ROOT_FULL: i64 = 5 // audit-gate
89
90func nx_pspawn_priv_class_name(p: i64) -> *u8 {
91 if p == NX_PSPAWN_PRIV_USER { return "USER" }
92 if p == NX_PSPAWN_PRIV_NET_BIND_LOW_PORT { return "NET_BIND_LOW_PORT" }
93 if p == NX_PSPAWN_PRIV_FS_WRITE_PROTECTED { return "FS_WRITE_PROTECTED" }
94 if p == NX_PSPAWN_PRIV_DEVICE_ACCESS { return "DEVICE_ACCESS" }
95 if p == NX_PSPAWN_PRIV_ROOT_FULL { return "ROOT_FULL" }
96 return "UNKNOWN_PRIV"
97}
98
99// ===== Spawn request struct ======================================
100
101struct NxPSpawnRequest {
102 binary_path_ptr: *u8
103 binary_path_len: i64
104 argv_serialized_ptr: *u8 // NUL-separated argv vector
105 argv_serialized_len: i64
106 envp_serialized_ptr: *u8 // NUL-separated KEY=VAL env
107 envp_serialized_len: i64
108 declared_priv_class: i64 // NX_PSPAWN_PRIV_*
109 capability_token_ptr: *u8 // ed25519-signed capability proof
110 capability_token_len: i64
111 cwd_path_ptr: *u8
112 cwd_path_len: i64
113 stdout_log_path_ptr: *u8
114 stdout_log_path_len: i64
115 stderr_log_path_ptr: *u8
116 stderr_log_path_len: i64
117 detach_session: i64 // 0/1; 1 = setsid for daemon
118}
119
120// ===== Top-level entry stubs =====================================
121
122// nx_pspawn_execute -- spawn one process per request. Returns child PID
123// on success (positive), error verdict on failure (negative or zero).
124
125func nx_pspawn_execute(req_ptr: *NxPSpawnRequest) -> i64 {
126 if req_ptr == 0 as *NxPSpawnRequest { return NX_PSPAWN_FAIL_BAD_BINARY }
127 // PARTIAL_WIRED: capability verify + fork + priv-drop + exec queued.
128 return NX_PSPAWN_FAIL_DEPENDENCY_MISSING
129}
130
131// nx_pspawn_kill_pid -- TERM with grace fallback to KILL. Composes with
132// nx_supervisor restart-policy decisions.
133
134func nx_pspawn_kill_pid(pid: i64, grace_seconds: i64) -> i64 {
135 if pid <= 0 { return NX_PSPAWN_FAIL_BAD_BINARY }
136 if grace_seconds < 0 { return NX_PSPAWN_FAIL_BAD_BINARY }
137 return NX_PSPAWN_FAIL_DEPENDENCY_MISSING
138}
139
140// nx_pspawn_pid_is_alive -- inspector.
141
142func nx_pspawn_pid_is_alive(pid: i64) -> i64 {
143 if pid <= 0 { return 0 }
144 return 0
145}
146
147// nx_pspawn_verify_capability_token -- check a capability token grants
148// the declared priv class. Replaces /etc/sudoers.d/* lookups with
149// cryptographic verification.
150
151func nx_pspawn_verify_capability_token(token_ptr: *u8, token_len: i64,
152 requested_priv_class: i64,
153 binary_path_hash_ptr: *u8,
154 binary_path_hash_len: i64) -> i64 {
155 if token_len <= 0 { return 0 }
156 if binary_path_hash_len != 32 { return 0 }
157 if requested_priv_class < NX_PSPAWN_PRIV_USER { return 0 }
158 if requested_priv_class > NX_PSPAWN_PRIV_ROOT_FULL { return 0 }
159 return 0
160}
161
162// nx_pspawn_get_last_verdict -- inspector.
163
164func nx_pspawn_get_last_verdict() -> i64 {
165 return NX_PSPAWN_FAIL_DEPENDENCY_MISSING
166}