Run an untrusted process inside a kernel-enforced cage — no root, no daemon, one static binary.
oring restricts itself, then hands control to your command. It applies Landlock
(filesystem tiers, per-direction TCP, IPC scope), installs a seccomp-bpf denylist,
sets NO_NEW_PRIVS, builds a clean environment, and execve()s the target. The
child inherits every restriction irrevocably and can never widen it — so a
compromise is contained by the kernel, not by trust.
oring --profile balanced --workdir /work/repo -- cargo test # build/test; TCP :443 to any host, not a firewall
oring --profile strict --workdir /work/scratch -- ./sample # untrusted binary; no TCP egress (UDP/DNS open), W^X
oring --policy /etc/oring/rig.json -- ./fuzzer # explicit JSON policy
oring --profile balanced --workdir /work/repo --explain # preview the plan, run nothingoring is honest about what it is not: not an orchestrator, a network-policy engine,
or a VM boundary, and it does not author host MAC — those stack underneath it. The
residuals it cannot close are written down plainly in
docs/DESIGN.md §2, not glossed over.
Two baked-in profiles, tested against the shipping binary on a real kernel.
balanced is for a coding agent that builds code — toolchain caches granted,
workdir write_exec, :443 egress to any IP (port-only, not a domain/IP firewall).
strict is for an untrusted binary — nothing
granted, workdir write_noexec (W^X), deny-all TCP egress (UDP/DNS still open),
JIT blocked. Both block
reading outside their tiers (SSH keys, shell history), writing system paths, the
privilege-escalation syscalls, and abstract AF_UNIX connect(). Anything in between
is a small JSON --policy (schema in docs/DESIGN.md §7).
Wrapping an agent: balanced grants the toolchain caches but deliberately not
an agent's own state directory (~/.claude, ~/.config), and the child's environment
is an allowlist (secrets withheld unless you --env-keep NAME them). So -- claude
won't start as-is; decide the credential trade on purpose — and know the honest limits.
Grant ~/.claude so the agent can run and the API key inside it is readable by the
(possibly prompt-injected) agent: confinement buys blast-radius reduction, not credential
protection. Two residuals oring cannot close for you: egress is port-only to any IP
(DNS/UDP open), so a host firewall or filtering proxy is a required companion control,
not optional; and the agent has full write over its workdir — .git/hooks, Makefile,
build.rs, package.json scripts all run on the next git/build/test you run outside
the cage, so treat a touched workdir as untrusted and run the first build inside oring.
Full story in docs/DESIGN.md §2/§9.
- Ring 1 — Landlock: four filesystem tiers, per-direction TCP allowlists, IPC
scoping. Hard floor kernel 6.15 / ABI V7 — below it oring refuses rather than
run a partial cage. The V9
RESOLVE_UNIXright (wallsconnect()to a pathname AF_UNIX socket likedocker.sockor ssh-agent) is optional hardening behind--features resolve-unix; without it that residual is reported loudly at seal. - Ring 2 — seccomp: a denylist removing the known escape launchpads (io_uring, userfaultfd, bpf, ptrace, the mount and namespace family), with a mandatory x32-ABI guard so the filter can't be re-entered through the compat ABI.
When a denial leaves you with a bare EACCES, --explain shows the effective
policy and --denials <pid> reconstructs what the kernel blocked.
Linux ≥ 6.15 with Landlock enabled (grep landlock /sys/kernel/security/lsm),
x86_64.
git clone https://github.com/hexproofdev/oring && cd oring
make build # static-musl binary with an embedded auditable SBOM
make check # the gate: fmt + clippy + test (make help lists the rest)oring depends on the published landlock
crate — nothing vendored. The optional V9 resolve-unix hardening builds against an
out-of-tree fork (upstream
rust-landlock#135);
see .cargo/config.toml.example.
For a batteries-included agent sandbox, reach for one of these — they do things oring deliberately doesn't:
- sandbox-runtime
(Anthropic) — cross-platform (macOS/Linux/Windows), domain-level network egress
through a proxy, and masks files like
.git/hooksinside your workdir. Needs Node, bubblewrap, and socat. - clampdown — container-based, with a credential-brokering auth proxy and per-API egress. Needs a container runtime and a privileged sidecar.
oring is the minimal end of that spectrum: one unprivileged static binary, no runtime, no daemon, no user namespaces (Landlock, not bubblewrap), and a confinement small enough to formally verify. It runs where those can't — distroless images, userns-disabled kernels, a bare CI step — and works as a defense-in-depth layer inside them.
v0.1.0. Design and threat model: docs/DESIGN.md. How it's
proven: docs/VERIFICATION.md. Attack catalogue:
docs/ATTACKS.md. Release history:
CHANGELOG.md. Contributors and agents start with
CLAUDE.md. Found a way past the cage? See
SECURITY.md — that's exactly what we want to hear.
BSD-3-Clause — see LICENSE.