Run coding agents unattended. Keep control of what leaves.
Give a coding agent a ready-to-use computer for every task, let it work unattended, and review the code, effects, checks, and behaviour before anything leaves. No Dockerfiles. No secret mounts. No cleanup.
Today, letting a coding agent work on a real repository means assembling a Dockerfile, a bind-mounted checkout, a devcontainer, API keys, SSH agent forwarding, a Docker socket, toolchains, caches, sidecars, cleanup scripts, and hope. And Docker still only understands a process in a container. It does not understand the piece of work the agent is trying to finish.
Rauha runs each agent task in its own zone — filesystem view, processes, network, resources, policy, and audit as one unit — and hands back one structured result: what ran, how it exited, what it produced, and what the boundary stopped. Docker runs the process. Rauha operates the work.
Rauha (Finnish) — peace. What you get when untrusted execution stays inside its boundary.
One command, one result:
rauha sandbox --image python:3.12 --repo-path . -- pytest tests/{
"task_id": "task-4348c0a8…",
"zone_id": "09c95680-6c4b-4f1e-8b8f-cfdd3e144ac7",
"status": "succeeded",
"exit_code": 0,
"stdout": "…",
"stderr": "",
"duration_ms": 418,
"admission": "audit",
"unavailable_controls": [],
"events": [],
"enforcement_events": [
{
"hook": "seccomp_notify",
"action": "zone.syscall.brokered.granted",
"decision": "allow",
"object": "path:/etc/hostname"
}
],
"receipt": { "sha256": "…", "…": "signed, verified by the CLI" }
}The daemon allocates a zone for the task, starts the container, waits, captures
stdout/stderr/exit code, collects lifecycle and enforcement events, signs a
receipt, and tears the zone down unless you pass --keep-zone. Strict
admission is the default: if a control your policy asks for cannot be
enforced on this host, the task is refused rather than run degraded. --audit
lets a temporary zone run anyway, and the result says exactly which controls
were missing. Every result carries two verifiable receipt forms — a legacy
Ed25519 signature and a DSSE in-toto envelope — checkable offline with
rauha receipt.
Output storage is bounded per stream. capture_issues reports truncation,
invalid UTF-8, and capture failures in both the result and its signed receipts.
The CLI prints a task ID before execution; rauha sandbox-result <task-id>
retrieves the saved result without executing again. Use --delete to discard
a retained result. Completed task containers release their raw log files.
Accepted tasks survive client disconnects. After a daemon crash, recovery stops
interrupted tasks and saves an explicitly uncertain signed result without
rerunning the command. New Linux tasks journal their lifecycle and bind its
prefix into the signed result: completed commands wait; interrupted tasks freeze.
Results are saved before output cleanup; see
durable task recovery for the boundaries.
Underneath, the same primitives are available directly:
rauha zone create --name frontend --policy policies/standard.toml
rauha run --zone frontend alpine:latest /bin/echo hello
rauha events # live zone + enforcement events
rauha zone verify frontend --json # is the boundary actually intact?See docs/architecture.md for the full control surface.
One line of policy flips a zone from ambient authority to capability-style handles:
[syscalls]
broker = ["openat", "openat2"]Brokered calls suspend in the kernel (seccomp SCMP_ACT_NOTIFY) and are
judged by the zone shim: read-only, confined opens are satisfied by the
broker opening the file — openat2 with RESOLVE_IN_ROOT/RESOLVE_BENEATH,
the caller's own resolve restrictions OR'd in, never weakened — and injecting
the fd. The workload never opens anything itself; symlinks, .., and magic
links cannot escape by construction. Denials answer honest errnos (EPERM for
policy, the kernel's own errno otherwise), so tools fail the way they would on
the host. Decisions are recorded in the container's bounded,
root-only broker.log, streamed live on rauha events and carried in the
sandbox result — zone.syscall.brokered.granted / .denied. See
policies/broker.toml for the canonical policy.
Any agent. Any environment. One Run. Docker made applications portable
across computers; Rauha makes agentic work portable across agents,
environments, and stages. The zone is the boundary. The Run is the
product: work, workspace, authority, journal, effects, behaviour, checks, and
receipt as one portable, supervised object. The canonical thesis is
docs/product-thesis.md.
rauha run -- claude -p "upgrade Postgres and fix the migration" # planned
rauha fork run-42 --agent codex # planned
rauha compare run-43 run-44 # planned
rauha accept run-44 # plannedRauha gives every agent a disposable computer, a set of trusted capabilities,
and a persistent supervisor that follows its work until it is genuinely
finished. Each run gets a copy-on-write workspace, brokered credentials it can
use but never read, proof gates, and a signed receipt of what was enforced.
Every run has a supervisor; an optional management layer makes it durable
across machines and teams. The Run's wire contract is docs/run-protocol-v0.md;
the market survey and hardening roadmap are in
docs/positioning-and-roadmap.md.
- The task is the unit, not the container. You reason about what the work did, not about a pile of container IDs.
- Authority can be held without being exercised. Brokered syscalls suspend in the kernel and are judged by the boundary: the agent receives granted handles, never ambient access. Capsicum's shape, on stock Linux.
- Nothing degrades silently. A requested control is enforced, audited, or the task is refused. The result always says which.
- The boundary explains itself. Logs, lifecycle, and kernel deny events
come back through one watch API and one stable event schema
(
rauha-evidence). Isolation you cannot observe is isolation you cannot trust. - Crash recovery keeps the boundary. Kill the daemon mid-task; on restart the same workload keeps its cgroup, kernel membership, and file ownership — probed on every Linux release.
- Same model on your laptop and your cluster. Linux builds zones from cgroups, namespaces, and an OCI rootfs, with Rauha's own eBPF-LSM programs enforcing in the kernel.
- Neutral. Claude, Codex, or your own agent — Rauha does not care which.
Four moving parts, one boundary:
rauha (CLI) ──gRPC──▶ rauhad ──spawns──▶ rauha-shim (one per zone) ──▶ crun ──▶ workload
│ │
│ ├── broker.log, stdout/stderr (root-only)
│ └── exec/attach IPC
├── redb: zones, containers, task recovery, saved results
└── evidence events ──▶ `rauha events`, sandbox results
The daemon is platform-agnostic on purpose. rauhad is one async
daemon (tokio, gRPC on [::1]:9876) behind a single IsolationBackend
trait; the rauha CLI and containerd-shim-rauha-v2 are thin clients.
Zone metadata lives in redb and is the source of truth on restart: on
boot the daemon reconciles — reloads every zone, rebuilds kernel state
(BPF maps, cgroups, network), then cleans up orphans.
One shim per zone, not per container. The zone — not the container —
is the isolation boundary; containers in a zone share namespaces, and
the sync, fork-safe rauha-shim supervises all of them (crun builds
each container; the shim holds pidfds, exec/attach IPC, logs, and the
syscall broker). Deliberately synchronous where it forks: fork() in a
multithreaded async runtime is UB, so the shim is not one.
Enrollment before execution — the security invariant. crun builds
the container (namespaces, mounts, capabilities) and parks init on its
exec fifo — outside the zone cgroup. The shim reads the PID, pidfd-opens
it, writes it into /sys/fs/cgroup/rauha.slice/zone-{name}/cgroup.procs,
and only then runs crun start, which execs the image entrypoint — inside
the boundary. No image code ever runs before it is enrolled; otherwise
kernel enforcement would not apply to it. (Enrollment is never an OCI
hook: crun runs those after pivot_root in the image's own rootfs.)
Three enforcement layers, each doing its own job. nftables owns L3/L4 — every bridge base chain defaults to drop, zones get connectivity only through their per-zone jump rules, and NAT masquerades the zone subnet. Rauha's eBPF-LSM layer owns the in-kernel, deny-before-it-happens decisions on file/exec/ptrace/signal/cgroup/capability. And for policy-marked syscalls, the seccomp-notify broker (above) owns capability-style judgment in userspace. Defense-in-depth, not redundancy: none replaces another.
Policy is admission-checked, never guessed. Policies are TOML
(policies/standard.toml): capabilities, resources, network mode and
egress, filesystem rules, devices, syscalls, cross-zone communication.
At zone creation every requested control is classified: enforced, audited
(with the degradation recorded and surfaced in zone verify and the
sandbox result), or the zone is refused outright. Nothing degrades
silently.
Observability is evidence, not logging. Lifecycle events, kernel deny
events, and broker decisions all normalize into one stable schema
(rauha-evidence) and reach one watch API. rauha zone verify --json
runs the named boundary self-checks (cgroup, BPF membership, inode
ownership, netns, veth, nftables) — the same names the security probes
key on.
Details, diagram, and crate map: docs/architecture.md.
Rauha creates the zones. Rauha's eBPF makes the Linux kernel respect them.
Kernel enforcement is Rauha's own today: seven BPF-LSM hooks
(file/exec/ptrace/signal/cgroup-attach/capability; the socket hook is
audit-only — nftables enforces network), BPF maps keyed by cgroup and inode,
ring-buffer deny events, per-hook counters, and privileged self-tests at
load. rauha-enforcer-api defines the same boundary as a backend-neutral
trait with a NoopEnforcer and a conformance harness every backend must
pass — the seam through which an external kernel-enforcement backend can
take over without touching the runtime above it. Today the in-repo Linux
eBPF backend is what the daemon runs; routing live enforcement entirely
through the trait is in progress.
- The run experience above is planned, not shipped. What ships is
rauha sandbox, the zone primitives, and the brokered-opens policy. Fork, compare, accept, and brokered credentials are roadmap; signed sandbox receipts ship today (Ed25519 + DSSE in-toto, verified by the CLI and offline byrauha receipt). - Brokered opens are read-only, v1-shape —
openat/openat2only, judged with kernel-faithful errnos. The brokerable set is a pinned contract (BROKERABLE_SYSCALLS) shared by daemon and shim with a drift test. One documented exposure remains: a sibling thread of the target canchroot/chdirthe shared filesystem between suspension and judgment — the open stays confined to a subtree of the container rootfs, which is the zone boundary. - Three policy controls are unsupported on Linux today and strict admission
refuses them:
filesystem.writable_paths,devices.allowed,syscalls.deny. The roadmap closes them with Landlock, cgroup device BPF, and seccomp. - Sandbox event capture is best-effort — enforcement events ride a daemon-wide broadcast and can be absent or partial; they are not an audit-complete log. Decisions and deny events are also recorded durably (container logs, the broker's decision log), but until broadcast drops are counted, the streamed picture cannot claim completeness — counting them is on the roadmap.
- A sandbox, not a hardware boundary — BPF-LSM is OS-level isolation and is additive-only: it can deny, but cannot override SELinux/AppArmor. Covert channels through shared kernel resources are out of scope.
- Linux-only — the former macOS VM backend was removed (it could confine but not observe, and had drifted to non-compiling); a future VM tier would run the same Linux stack inside a VM instead of a parallel macOS backend.
- Kubernetes integration requires containerd + RuntimeClass wiring; installation docs and examples are still being written.
Rauha runs on Linux. The workspace also compiles and its unit tests run on other platforms (e.g. macOS dev machines), but the daemon refuses to start there — there is no non-Linux backend.
- Linux — 6.1+ with
CONFIG_BPF_LSM=y,CONFIG_BPF_SYSCALL=y,CONFIG_DEBUG_INFO_BTF=y; boot parameterlsm=lockdown,capability,bpf; BTF at/sys/kernel/btf/vmlinux. The Linux daemon fails closed: it requires root and a working BPF-LSM kernel and refuses to start without enforcement. There is no degraded Linux mode. - nftables —
nf_tables+nf_natare hard requirements (the daemon refuses to start without them);nf_tables_bridgepowers cross-zone L2 filtering and degrades explicitly when absent — strict zones refuse network admission, audit zones recordnetwork:nftablesandzone verifyreports it.
Root directory: /var/lib/rauha (override with RAUHA_ROOT).
cargo build # all workspace crates
cargo test # all unit tests
cargo xtask build-ebpf --release # eBPF object + offsets sidecar for this kernel
RUST_LOG=rauhad=debug cargo run --bin rauhad # daemon on [::1]:9876Three layers of verification, each independent of the source:
- Oracle (
eval/oracle, 55 numbered gRPC cases against a running daemon):RAUHA_GRPC_ENDPOINT=http://[::1]:9876 cargo test - Linux integration and security gate (
tests/integration/,tests/security/linux-gate.sh; root + BPF-LSM kernel): lifecycle, isolation, networking, crash recovery, and adversarial host-impact probes. On GitHub the privileged workflow runs the gate directly on a self-hosted BPF-LSM runner. - Enforcer conformance: runs against
NoopEnforcerin ordinary tests; the real eBPF backend is opt-in on an isolated root host withRAUHA_RUN_EBPF_CONFORMANCE=1 cargo test -p rauhad linux_enforcer_passes_basic_conformance.
In order:
- Finish safe user-namespace support on a runtime/storage combination that can make the rootfs private after entering the target user namespace.
- Run Protocol v0 — the journal, reducer, lifecycle, ownership epochs, capability intents, checkpoints, forks, and adoption.
- Local custodian and tier-0 supervisor with three built-in services: workspace, credential/egress broker, witness/receipt.
- The
rauha runexperience: copy-on-write workspace, code diff plus behavioural diff, accept or discard. - Close the unsupported Linux controls (Landlock, cgroup device BPF, seccomp) and adopt the new mount API for rootfs assembly.
- Signed execution receipts as an in-toto predicate; an external
kernel-enforcement backend through
rauha-enforcer-api. - Optional management layer: durable runs across machines, fork/compare,
Kubernetes
agent-sandboxintegration.
Licensed under the Apache License, Version 2.0. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this work as defined in the Apache-2.0 license shall be licensed as above, without any additional terms or conditions.