Processes & jobs
The process model — a PID table with status tracking, job control, ps/top/kill, and a systemd-lite service manager.
Lifo models processes like Linux does: a table of entries with PIDs, parents,
status, and a way to signal them. The ProcessRegistry (on kernel.processRegistry)
owns it.
The process table
interface Process {
pid: number;
ppid: number; // parent (shell is PID 1)
command: string;
args: string[];
status: "running" | "sleeping" | "stopped" | "zombie";
controller: AbortController; // how it's killed
// …start time, etc.
}- PID 1 is the shell; spawned processes get PIDs from 2 up.
- Spawning —
processRegistry.spawn(opts)registers a process and returns its PID. When its promise settles it auto-transitions tozombie(awaiting reap), mirroring Unix. - Killing — each process carries an
AbortController; killing it aborts the signal the command is watching (ctx.signal), so well-behaved commands stop promptly.
Inspecting and controlling
The usual tools are built in and operate on the registry:
ps— list processes (PID, PPID, status, command).top— a live process view.kill— signal / abort a process by PID.watch— re-run a command on an interval.
await sandbox.commands.run("ps");
await sandbox.commands.run("kill 7");Job control
Foreground and background jobs are tracked by the shell's JobTable. A trailing
& backgrounds a pipeline; jobs, fg, and bg manage them, and Ctrl-Z /
stop transitions map onto the stopped status.
Services: a systemd-lite
For long-running processes (dev servers, daemons), the kernel includes a
ServiceManager driven by unit files and exposed through systemctl:
await sandbox.commands.run("systemctl start my-service");
await sandbox.commands.run("systemctl status my-service");Unit files are parsed by parseUnitFile (UnitFile), and the manager tracks
each service's ServiceInfo (state, PID, restarts). This is how a box can bring
up background services declaratively rather than juggling raw & jobs.
Note
The process model is cooperative: "killing" aborts an AbortSignal rather than
tearing down an OS process. Commands that honor ctx.signal (all the built-ins
do) stop cleanly; the Node runtime wires the same signal
into a script's process and its async work.