Map: core / runtime
core/src/runtime/ is the OS plumbing the CBPV machine
dispatches into — command execution, pipeline orchestration, and the
per-child confinement choice. It re-enters evaluation only through
evaluator::machine::apply (a handler or alias arm’s thunk, from
command_call.rs) and evaluator::machine::evaluate (a re-exec’d stage’s
closure, from child_eval.rs) — stages carry closures, so the mutual
recursion is irreducible; the evaluator reaches it at
PipeNode::launch/join and, dispatching an Exec node, at
command_call::classify_command → run_base_frame / run_external, and the command redirect guards
(evaluator-runtime-split).
command_call.rs—run_call, the single site that resolves a head (env → handlers → external) and runs the chosen arm; the evaluator’s down-seam for a bare command. There is no builtin arm: a native table entry is anEnvhit on a native value, and a base-frame row is aBasehit on the handler stack’s base layer, run byrun_base_framewith the argv slice (a-name-is-a-value-or-it-is-handled, argv-is-a-list-of-strings).^nameskips the env, and therefore every native, but still consults handlers; a path-bearing head skips handlers too. Grant admission is an external-command property: only theExternalarm consultscapability::admits_headbefore any argument evaluates, refusing the head outright; the env, base, and handler arms pass through. Handler and alias thunks are lambdas — a unary{ |args| … }or a catch-all{ |name args| … }— with the calling convention fixed by surface position, not inferred from a value’s runtime shape (handlers-and-aliases-are-lambdas).command.rs— the External arm: vet the resolved identity, choose its exec image, wire stdio per the call’s redirects, spawn, and reap. Submodules:identity.rs(CommandIdentity, the classify-once name/shown/resolved triple),vet.rs(existence → argv shape → grant policy, yielding aSpawnPlanwith itsExecImage),process.rs,child.rs,stdio.rs,redirect.rs,foreground.rs,detach.rs, plusuutils.rsfor bundled coreutils.resolvedand the 126/127 verdict are two projections of onePATHwalk.identity::walk_pathcallspath::searchonce, storing thePathSearchon the identity;vet::check_existencepattern-matches it and never probes the disk itself, so walk and verdict cannot disagree. Both that walk andpolicy_names’ host-PATHbaseline anchor throughContext::search_cwd— thewithin [dir: …]override, else thecd-mutated cwd — so the grant gate judges the identity vet saw (one-walk-one-anchor).- The argv-shape step is one refused set read at two moments.
vet::reject_exec_argmaps each argument throughRefusedArg::of_value(core/src/types/exec_arg.rs) and carries the shape’s ownremedy; the checker maps the argument’s type throughRefusedArg::of_tybefore the run, so this is the backstop for what a type variable hid from it (exec-argv-is-words, exec-boundary-gated-statically). detach.rsis that same machinery —CommandIdentity,vet,build_command— up to the one act that differs: the child is born byLaunch::spawn_detached(io-process), so its pgid is never observed here and nothing can signal, await, or reap it. What replaces the handle is a first-order{pid, desc}receipt. The birthing frame’s projection is rendered into the launch exactly as for a child we keep, only withsandbox::Ownership::Surrendereddropping the parent-death tie, so the survivor’s authority is frozen as that frame left it and no later frame can widen it — it cannot name the process at all (capabilities). All three of its standard descriptors are/dev/null.- A bundled coreutils/diffutils/ripgrep head is an
ExecImage::BundledTool, always run as aral --ral-bundled-tool <tool>child — its inherited stdio, env, cwd, process group, and sandbox are the execution context, so it threads the same spawn/RunningChild/audit machinery as a host external, and behaves identically on Windows where no.exeexists to spawn (bundled-tools-as-exec-images, bundled-tools-always-reexec). foreground.rs’sForegroundDecision::for_standalonegates the terminal handoff on a held terminal lease (Shell::terminal_leaseisSome) plus top-level launch role and a terminal-bound sink with no shell-side pump, not on REPL interactivity — so a non-interactive script launched at a terminal holds aLeasedrun and foregrounds its interactive children, while an exarchDeniedtool run cannot construct the handoff at all (terminal-foreground-ownership). Parking a stopped child as a resumable job stays REPL-only.- Redirects install on the handler arm
(redirect-drop-on-handler-dispatch).
Child stdio routing — the
(Stdio, pump)plan for a spawned child’s stdout/stderr — is one shape,Sink::child_stdout/child_stderryielding the sharedChildStdioPlan(io-process), through which the standalone command, the direct pipeline stage, and the ral helper stage all route; the tty-inherit predicate (stdio::inherit_tty) is likewise shared. - The read door (
< file), the write door (> / >> / >|, settledcommitted/aborted/failedat frame teardown), and the exec door (Host andBundledToolcompletion) each build anObservation(core/src/types/observation.rs) and pass it toobserve(core/src/evaluator/audit.rs), the one fan-out door: it reports to the run’sMooringand to the open audit trail alike, judging neither’s interest — the host filters the rail. It judges only whether anything happened: a write onto the discard device is dropped there (audit). Core emits plainValue::Maps throughObservation::to_value; a host (exarch) decodes them back withObservation::from_value. The observation shapes and their card rendering live in io-surface (surface-reads-writes-execs).
pipeline/— pipeline planning and execution. A multi-stageCompKind::Pipelinesteps through aPipeNode:PipeNode::launchresolves the plan and spawns every stage into one process group, andjoin(collectthenfinish), called in the same rule, is what the arm meets; no frame is pushed, so an unwinding panic tears the group down throughPipelineGroup’sDroprather than through any undo of the machine’s.group(the pgid anchor, foreground guard, SIGINT relay) stays alive across bothcollectandfinishrather than dropped early (evaluator).resolve.rsfreezes each stage’s launch decision once asStageLaunch(Direct|HelperEval) from the head’s resolution, redirects, terminal ownership, and audit state, so launch reads a decision rather than re-deriving a dispatch gate. No route enters that classification: a stage’s dispatch may not depend on where its payload lives, or the choice would stop being observationally transparent. No route enters anywhere else, either: the single fact resolve carries isPipelinePlan::yields, the IR’s ownPipeYield, which the checker wrote and the runtime only reads.route.rs’sopen_stage_routesthen allocates every interior edge as an operating-system byte pipe from stage position alone (i + 1 < n), and derivesFinalValue::Reportfromi + 1 == ntogether with that one yield — the pipeline’s only value-transport question (pipes-are-positional-byte-wires). A non-final stage’s returned value is discarded, never serialised onto an edge. A multi-stage pipeline always launches its stages as subprocesses in one process group; ordinary application and bind do not enter this runtime.- A bundled (uutils) head routes
Directlike any external, itsral --ral-bundled-toolchild the image chosen bycommand::build_command— nothing in the pipeline distinguishes a bundled head from a host binary, so both classify asExternalcarrying the resolvedCommandIdentity. Value-style composition is evaluator application, not a helper route. The terminal-ownership decision (resolve_terminal_plan) likewise gates on a reachable terminal lease and a terminal-bound final sink, not on astartup_foregroundpredicate. launch.rs(PipelineBuild/PipelineResourcesown launch and gate-first abort teardown),group.rs(the pgid anchor is forked only for a multi-stage pipeline, since a single stage leads its own group on spawn),stage.rs(helper-stage launch + observe),collect.rs,helper.rs(the hidden--ral-pipeline-stage-helper/--ral-pipeline-anchor/--ral-bundled-toolchild entrypoints and their final-report helper), andprotocol/(common.rs,unix.rs/windows.rs,fallback.rs) for the ral⇄ral stage frames. The helper protocol carries gate and final-report frames; it carries no typed value between interior stages. On Windows the protocol pushes helper handles intoprocess::Launch, whose rawCreateProcessWbackend admits them withPROC_THREAD_ATTRIBUTE_HANDLE_LIST;PipelineGroupprepares Job Objects before spawn and registers only children already assigned before resume (windows-spawn-boundary).
- A bundled (uutils) head routes
- A
grantis a dynamic effect scope, not a process boundary, so the grant body always evaluates locally — the machine steps its body in process, with no router in between: nested grants compose by intersection over authority, an algebra of the evaluator’s dynamic context. Confinement happens elsewhere — the RAL-owned effects are decided in process bycapability::check_*(capabilities), and child-owned effects are kernel-backed at external dispatch: when a projection is active,command::build_commandobtains a confinedprocess::Launchviaprojection_enforceable/sandboxed_command— per-command Seatbelt / bwrap confinement on Unix; on Windows the LowBox token of the projection’s own AppContainer profile, carrying the per-path fs capability SIDs its projection names (path-derived-capability-sids) — and net/fs fail-closed fires when a child is actually spawned, not at grant-body entry (sandbox-external-children). Inside a guest,build_commandtakes neither projection branch:shell.guest_jail()marks every spawn as already confined by the spawn jail — a fresh unprivileged uid and a per-exec cgroup,process/jail.rs(io-process) — since bwrap needs the user namespaces the guest boot disables; the in-process gates apply unchanged, andchild.rstracks the per-execJailCgroup, so cancel and settle kill the whole tree throughcgroup.kill(asetsid’d grandchild cannot leave its cgroup) while the grace phase stays pgid-addressed (docs/SPEC.md§12.11). core/src/child_eval.rs(crate root, beside the wire layer it rides, not underruntime/) — the one re-exec’d-child eval runner the pipeline stage helper drives,run_child_eval(child-eval-unification). One request frame in, one response frame out: the child packs the body plus aWireShellsnapshot, rebuilds its shell throughsubprocess::bare_child_shellandinstall_wire_shell— the manifest first, so theWireDecoderre-links natives against it — evaluates the stage against its byte input, drains its audit fragment, and ships a singleChildEvalResponse. When the pipeline yields its last stage’s value,FinalValue::Reportasks this helper response to carry it; the final report remains helper-staged until a separate in-parent-tail decision. The response frame travels its own socketpair, never aliased with an interior pipe, so there is no upstream typed-value edge.
The Shell state these thread is shell-state; the serde
mirror and wire envelope they ride is transport.