Start here

Quickstart

Install Warden, confine a first command, add precise authority and preserve MIR plus execution evidence.

Warden runs one command under a policy you write, and hands you back proof of what that command was actually allowed to do. This page takes you from an empty shell to a confined run with evidence on disk.

The idea in one picture

ARCHITECTUREFrom your intent to verifiable evidence
    • Policywhat the command may touch
    • Canonical MIRcompiled, portable decision
    • Backendthe OS mechanism that enforces it
    • Confined runsupervised start to exit
    • Receiptwhat was enforced, provable after the fact

You describe the boundary once. Warden compiles it, picks a backend that can enforce every part of it, supervises the process, and records the result. If no backend can enforce the policy, the run is refused rather than weakened.

Each stage has a name you will meet again throughout these docs. A policy is your intent. MIR is that intent compiled into a canonical, platform-independent decision. A backend is the concrete operating-system mechanism — a sandbox profile, a namespace, a microVM — that actually blocks a forbidden read or connection. A receipt is the record of which backend enforced which decision.

Install Warden

PLATFORMInstall the current release

macOS on Apple Silicon

Seatbelt needs macOS 15 or newer. Every method below installs the same binaries.

The tap now publishes bottles, so this downloads a prebuilt binary instead of compiling.

brew install maelys-dev/tap/maelys-warden
maelys-warden --version

Pick whichever method you already trust. Homebrew and the distribution packages keep Warden upgradable in place; the archives pin one exact release; building from source is the path when you need to read or change the code before you run it.

Which backends you get

A base install compiles in two backends. The rest are optional driver packages you install only when you need OCI images or microVM isolation.

BackendPlatformShips inEnforces
posixanybase installnothing — no confinement
seatbeltmacOS, Apple Siliconbase installfiles, network, process tree
bubblewrapLinuxbase installfiles, network, process tree, seccomp
apple-containermacOS 26, Apple Siliconoptional driverOCI image in a VM
apple-container-bubblewrapmacOS 26, Apple Siliconoptional driverOCI image, plus recursive rules inside
krunmacOS, Apple Siliconoptional driverOCI image in a libkrun microVM
krun-bubblewrapmacOS, Apple Siliconoptional drivermicroVM, plus recursive rules inside

Two consequences are worth internalising now. Bubblewrap is Linux-only and never ships on macOS, so on a Mac, Seatbelt is your only confining backend without an optional driver. And posix applies no confinement at all: it exists for hosts where nothing stronger is available, must be selected explicitly, and is never chosen for you over a backend that actually enforces something.

Confine a first local command

CODE
maelys-warden run --ro --workspace "$PWD" -- codex
maelys-warden run --rw --workspace "$PWD" -- npm test -- --runInBand

run starts one supervised execution. --ro makes the workspace readable but not writable; --rw permits writes there. Everything after -- is the workload command, forwarded unchanged — in the second example the first -- belongs to Warden and the second is passed through to npm test.

Both flags do more than the workspace itself. They also grant the minimal runtime and the exact executable as readable, a private temporary directory as writable, set the network to none, and make every descendant process inherit the same confinement. Nothing else on the host is reachable, including your home directory.

Run a coding agent under Warden

Coding agents ship their own approval prompts and their own sandbox. Once Warden confines the process, that second layer is redundant and its prompts only ask about actions Warden has already decided. Turn it off and let Warden be the single boundary.

The catch is that an agent still needs its own credentials and session state, and those live outside the workspace — so a default --ro run cannot even authenticate. Grant that directory explicitly, keep the workspace root read-only, and open write access only where the agent is supposed to edit:

CODE
maelys-warden run --ro \
  --workspace "$PWD" \
  --allow-read "$HOME/.claude" \
  --allow-write "$PWD/src" \
  --allow-write "$PWD/tests" \
  --net api.anthropic.com:443 \
  --net registry.npmjs.org:443 \
  -- claude --dangerously-skip-permissions \
       -p "Fix the failing unit tests, then stop."

--dangerously-skip-permissions turns off Claude Code's permission prompts. Its own Bash sandbox is already off by default, so there is nothing else to disable.

CODE
maelys-warden run --ro \
  --workspace "$PWD" \
  --allow-read "$HOME/.codex" \
  --allow-write "$PWD/src" \
  --net api.openai.com:443 \
  --net registry.npmjs.org:443 \
  -- codex --yolo

--yolo is the documented alias for --dangerously-bypass-approvals-and-sandbox. Codex ties approvals and sandboxing to one switch, so a single flag disables both. That flag is only safe inside an already-hardened external sandbox — which is exactly what Warden provides here.

Grant $HOME/.claude and $HOME/.codex read-only, as shown, unless you want the agent to persist session history across runs; then grant write instead. What you are not granting matters just as much: SSH keys, cloud credentials, ~/.npmrc, ~/.netrc and ~/.git-credentials all stay outside the boundary, so a prompt injection in the repository cannot reach them. The API hosts above are examples — check what your agent version actually contacts and grant only those.

Add only the authority the tool needs

CODE
maelys-warden run --ro \
  --workspace "$PWD" \
  --allow-write "$PWD/build" \
  --net registry.npmjs.org:443 \
  -- npm ci

--allow-read and --allow-write extend the filesystem decision. Each --net HOST:PORT adds one mediated destination, meaning the connection is proxied and recorded by Egress rather than opened directly by the workload. Warden then selects the strongest eligible backend and fails closed if none can enforce the result.

For ordinary tools, --network-frontend auto selects the Egress proxy whenever a mediated destination exists. Use --network-frontend fd4 only for a Maelys-aware program that implements the fd 4 protocol itself.

Keep the decision and the evidence

CODE
maelys-warden run --ro \
  --workspace "$PWD" \
  --emit-mir /tmp/reviewed-policy.mir \
  --receipt /tmp/execution.json \
  -- /usr/bin/true

--emit-mir writes the canonical MIR before the process starts, so the exact decision can be reviewed, hashed or replayed later. --receipt writes the terminal execution receipt after Warden has waited for the workload. Workload stdout stays separate from both files.

A receipt is what a completed run leaves behind:

CODE
{
  "schema": "maelys.execution-receipt/v1",
  "invocationId": "build-2026-08-24-001",
  "decisionDigest": "94d14d6aece6e3a0cdf1409d9a92695b8591fe7016ae56ebad221fcd49ba8bc3",
  "planDigest": "cf14f5b4eff7f425b140948c9c1d53f905c73e7ad6504aaa1d3e04490f812535",
  "mediator": "maelys-netd/proxy-v1",
  "backend": {"name": "bubblewrap", "version": "0.9.0"},
  "capabilities": {"required": 16415, "backend": 24575},
  "network": {"mode": "mediated", "frontend": "netd-proxy"},
  "startedUnixMs": 1787541910254,
  "forcedStopAttempted": false,
  "completed": true,
  "outcome": {"exitCode": 0, "termSignal": 0}
}

planDigest and decisionDigest are content-addressed identities: hashes of the compiled policy and of the decision Warden enforced. Recompiling the same policy reproduces the same digest, which is how you prove after the fact exactly what a run was allowed to do. backend names the mechanism that enforced it, and capabilities compares what the plan required against what that backend offered. network.mode says whether traffic was blocked, direct or mediated through Egress.

Correlate network activity

One run produces one execution receipt, but it can open many connections, so those are recorded separately. Add --network-receipts FILE and Egress appends one JSON object per line as each connection finishes — ten connections, ten lines.

CODE
run_id='ci-4821-attempt-2'

maelys-warden run --ro \
  --workspace "$PWD" \
  --net github.com:443 \
  --invocation-id "$run_id" \
  --receipt execution.json \
  --network-receipts connections.jsonl \
  -- curl https://github.com/

You now have two files describing one run, and --invocation-id is what ties them together: Warden writes that value into the execution receipt, Egress writes it into every connection line. Without it a tool reading both files would have to guess at the pairing from filenames or timestamps; with it, execution.json and every line of connections.jsonl carry the same ci-4821-attempt-2. It is a correlation label only — not a secret, and not a grant of any kind.

A single line, pretty-printed here for readability:

CODE
{
  "invocationId": "ci-4821-attempt-2",
  "principal": "maelys",
  "host": "github.com",
  "port": 443,
  "outcome": "closed",
  "bytesFromClient": 812,
  "bytesToClient": 64213
}

Refused connections are recorded too. If the workload tries to reach a destination the policy never allowed, Egress closes the stream and still emits a line for the attempt, with an outcome marking the refusal — an empty file means no connection was attempted, never that something slipped through unseen. Lines carry destination, outcome, byte counts and policy identity; never payloads.

The file stays empty when the network is none, and also under --net direct, since ambient direct networking bypasses Egress entirely and therefore cannot be recorded. Exact field names are illustrative pending a published schema for this file; the execution receipt above is the one with a normative schema today.

Going further with policy

The flags used above — --ro, --rw, --allow-read, --allow-write, --net — are Warden's shorthand, and they resolve most single-command runs:

CODE
maelys-warden run --ro --workspace "$PWD" --allow-write "$PWD/dist" -- make build

That shorthand builds a MIR for you. Authoring policy directly buys you what it cannot express: explicit DENY rules that override a broader grant, view mappings that show a process a different path than the real one, per-rule literal or recursive scope, and one reviewed policy file shared across many commands and machines instead of a shell line rewritten each time.

Warden accepts such a policy as --policy file.json or as already-compiled --plan file.mir. The language, its compiler and the full authoring reference are a product of their own: see policy.maelys.dev.

Verify a downloaded release

CODE
sha256sum --check maelys-warden-0.41.0-linux-x86_64.tar.gz.sha256
gh attestation verify maelys-warden-0.41.0-linux-x86_64.tar.gz \
  --repo maelys-dev/maelys-warden

Tagged releases publish macOS arm64 and Linux amd64/arm64 archives, .deb and .rpm packages, SHA-256 checksums and provenance attestations. See release integrity for signatures, package names and the complete verification model.

Choose the next guide

This quickstart gets you to a first successful confined run. The tutorials go further: they walk the JSON policy to MIR to receipt pipeline end to end, add mediated networking, and cover the platform-specific backends.