Expand description
Stage execution isolation — the sandbox primitive extracted from
[noether_engine::executor::isolation] for consumers that want
isolation without pulling in the composition engine.
The noether-engine crate re-exports this module verbatim, so
existing callers see no API change. Downstream consumers
(agentspec, the
standalone noether-sandbox binary) depend on this crate
directly.
Wraps subprocess execution in a sandbox that restricts what the
stage can read, write, and call. Closes the gap documented in
SECURITY.md: a user-authored Python stage has host-user
privileges by default; with isolation it runs in a bounded
filesystem + network namespace.
Phase 1 (v0.7) backends:
IsolationBackend::None— legacy pass-through. Emits a warning unless the user opts in with--unsafe-no-isolation/NOETHER_ISOLATION=none.IsolationBackend::Bwrap— bubblewrap wrapper. Linux-only. Requires thebwrapbinary inPATH.
Phase 2 (v0.8) will add IsolationBackend::Native — direct
unshare(2) + Landlock + seccomp syscalls, no external binary.
See docs/roadmap/2026-04-18-stage-isolation.md.
§Policy derivation
An IsolationPolicy is derived from a stage’s declared
EffectSet. Phase 1 surfaces exactly one axis from the effect
vocabulary — Effect::Network toggles whether the sandbox
inherits the host’s network namespace. Every other effect
variant (Pure, Fallible, Llm, NonDeterministic, Process,
Cost, Unknown) produces the same baseline policy: RO
/nix/store bind, a sandbox-private /work tmpfs,
--cap-drop ALL, UID/GID mapped to nobody, --clearenv with a
short allowlist.
§TLS trust store — dual path
When network=true, the sandbox binds /etc/ssl/certs
(via --ro-bind-try) for non-Nix-aware clients that expect the
system trust store (curl, openssl). Nix-built code uses
NIX_SSL_CERT_FILE / SSL_CERT_FILE (both in the env
allowlist) pointing into /nix/store, which is always bound.
So TLS works whether the stage resolves certs through the
filesystem path or the env-pointer path; NixOS hosts without
/etc/ssl/certs fall through to the env path automatically.
§Filesystem effects — not yet expressible
The v0.6 Effect enum has no FsRead(path) / FsWrite(path)
variants, so there is no way for a stage to declare “I need to
read /etc/ssl but nothing else.” The sandbox compensates by
allowing nothing outside /nix/store, the executor’s cache
dir, and the nix binary. That is the strictest sane default —
but it means stages that legitimately need a specific host path
cannot run under isolation today. Planned for v0.8: extend
Effect with FsRead / FsWrite path variants, then expand
from_effects to translate them into bind mounts. Tracked in
docs/roadmap/2026-04-18-stage-isolation.md.
Structs§
- Isolation
Policy - What the sandbox does and doesn’t let a stage reach.
- RoBind
- A single read-only bind mount. Named-struct rather than a tuple
so the JSON wire format stays readable for non-Rust consumers:
{"host": "/nix/store", "sandbox": "/nix/store"}instead of the earlier["/nix/store", "/nix/store"]. The latter was terser but gave external language bindings no schema hint about which path was which. - RwBind
- A single read-write bind mount. The exact counterpart of
RoBindfor therw_bindsfield — same wire shape, same ergonomics, sameFrom<(PathBuf, PathBuf)>convenience.
Enums§
- Isolation
Backend - Which isolation backend to use for a stage execution.
- Isolation
Error - Error from the isolation layer itself — policy misconfiguration or backend unavailable. Stage-body errors come back as the usual execution error on the inner command.
Constants§
- NOBODY_
GID - NOBODY_
UID - Conventional “nobody” UID/GID on Linux. bwrap maps the invoking user to this identity inside the sandbox so the stage cannot observe the real UID of the caller.
- TRUSTED_
BWRAP_ PATHS - Root-owned locations where
bwraplives on a correctly-provisioned Linux host. Order matters: NixOS system profile first (nix hosts almost always have this), then the Determinate / single-user nix profile, then distro-packaged/usr/bin, then manual installs.
Functions§
- build_
bwrap_ command - Build a
bwrapinvocation that runscmdinside a sandbox. - find_
bwrap - Locate the
bwrapbinary.