Rune Box docs

Govern your agents

Rune Box puts policy, approvals, budgets, a sandbox and a signed audit trail around agents you already run; it does not pick models or plan tasks.

Connecting agents

Agent surface Connection
MCP-capable agent or IDE runeward mcp
Python or TypeScript framework runeward or @runeward/sdk, see adapters.md
Custom orchestrator REST API or the SDK client
Parallel peer workers runeward cohort and /v1/cohorts, see agent-groups.md
Live or replayable worker output /v1/agent-sessions/{id}/stream
Browser IDE (experimental) [ide] + ticketed proxy, see Browser IDE

Give each agent the smallest policy (Charter) that works. A denial is final; an approval-required action pauses until an operator decides. In an agent group (Cohort), task claims return a signed, expiring lease_token that complete and fail must present, so one agent cannot finish another's task.

Subagents

A subagent is a child delegated by a parent agent, not an agent group.

  1. Route the parent's and every child's tool calls through Rune Box.
  2. Give each child its own sandbox (Citadel) when workspaces or evidence must be isolated.
  3. Pass parent_citadel, run_id, agent, provider and model when creating the child.
  4. Give each child its own actor identity in the parent's tenant, with the parent's policy or a narrower one. Rune Box rejects a child that changes the tenant or whose policy it cannot prove is no broader (see Charter subsumption).
  5. Never auto-retry a denial or bypass an approval gate in a child.

Lineage is written to the signed audit trail (Chronicle) and a durable Run record. GET /v1/runs, runeward_list_runs and the dashboard recovery view show the run tree after teardown.

Charter subsumption

A delegated child (parent_citadel on POST /v1/citadels or runeward_create_citadel) or a fork (POST /v1/citadels/{id}/fork, runeward_fork_citadel) may use a different policy than its parent only when Rune Box can prove it grants no more power. The check is conservative: it may refuse a policy that is in fact narrower, but never accepts a broader one. The parent's unchanged policy passes on a fast path (exact).

Dimension The child is admitted when
[[policy]], policy_default Every action's verdict is no broader than the parent's (deny < require-approval < allow), compared exactly over tools, arguments and match_argv outcomes, honouring rule order. Unset policy_default uses RUNEWARD_POLICY_DEFAULT.
CEL, Rego, policy bundles Identical configuration (bundles pinned by @sha256:), or the child's built-in rules deny everything, or the parent's allow everything.
[network] The parent is unrestricted, or the child admits no host, port or IP range the parent refuses, keeps enforce = "strict" and is not unrestricted itself. Hostname rules that could match IP literals are only accepted when identical.
[[endpoint]], [[credential]] Each child credential is the parent's; each parent-inspected endpoint has a child match with the same protocol, enforce mode, credential and health_path, a subset of binaries, no larger budgets, every deny rule and a non-empty prefix of the parent's ordered allow rules, with effects at least as strict.
Containment [isolation] minimum at least the parent's; host settings unchanged; copy_from adds nothing; read-only root stays read-only; [filesystem] grants beneath the parent's with Landlock no weaker; [process] identity unchanged.
Features [workspace] persist, [schedule] self_service and [ide] only if the parent has them.
[rationing] Every parent cap set and no higher; loop detection at least as sensitive.
[[env]], [[file]] Every entry is one of the parent's.
[chronicle] Same sink, redaction not disabled, every parent scrub pattern kept.

Name, prompt, capabilities, packages, [cohort] and the workspace idle timeout are not compared.

  • A refusal is 403 naming the dimension and rule; a refused delegation is audited on the parent as tool citadel, action delegate, verdict deny.
  • An admitted child's Run record and sandbox create (or citadel fork) event carry charter_lineage (exact, subsumed or admin-override for an administrator's fork), parent_charter and charter_proof.
  • Limits: match_argv globs are compared only by identity; CEL and Rego are never compared semantically; a parent forked with smaller resource overrides is compared by its policy's limits.

Codex, Claude Code and GitHub Copilot

The repository's .mcp.json, .vscode/mcp.json, Cursor configuration, Copilot custom agent and dist/codex-plugin/runeward package all invoke runeward mcp without credentials. Export configuration in the process that launches the agent:

export RUNEWARD_CONFIG_DIR="$PWD/examples"
export RUNEWARD_STATE_DIR="$HOME/.cache/runeward/agent"
export RUNEWARD_AUTHZ_FILE="$HOME/.config/runeward/authz.json"
export RUNEWARD_MCP_DEFAULT_TOKEN="$(security find-generic-password -w -s runeward-agent)"

Or save a short-lived OIDC credential, which agent group CLI and stdio MCP use when no environment token is set:

runeward auth login --issuer https://id.example.com \
  --client-id runeward-cli --audience runeward
runeward auth status
Agent Setup
Codex Install dist/codex-plugin/runeward, or use .mcp.json. Its skill requires identity and policy discovery, least privilege, Live chat, hard pauses for approvals, run lineage and teardown.
Claude Code Reads .mcp.json. Start it from an environment with the variables above.
GitHub Copilot VS Code reads .vscode/mcp.json. The custom agent excludes approval resolution and agent group administration. Cloud agents cannot reach a local Docker socket: connect them to runeward serve's /mcp endpoint over HTTPS with a dedicated principal. Remote MCP is authorized per request; do not add a shared proxy token in front of RBAC.
  • Set GITHUB_TOKEN or OPENAI_BASE_URL only when the policy imports them. Host gh auth login credentials are not passed in; use op = "env://GITHUB_TOKEN" with a least-privilege token and allow only the needed GitHub hosts.
  • Give each human, agent, CI job and worker a distinct principal. Collaborators share a tenant.

Warning: Never commit tokens or put them in .mcp.json or MCP arguments. Static tokens on a non-loopback listener should use at least 32 random characters.

Live chat

The dashboard's Live chat tab is a read-only, secret-scrubbed feed of turns your harness publishes with runeward_publish_conversation, POST /v1/citadels/{id}/conversation, or the SDKs' publish_conversation(...) / publishConversation(...):

{"sandbox": "citadel-id", "role": "assistant", "content": "I found the failing test and am applying the fix.", "run_id": "optional-run-id"}
  • Codex, Claude and Copilot do not forward transcripts automatically.
  • Publish to the same process that serves the dashboard (its /mcp endpoint or REST). A separate runeward mcp stdio process cannot populate it.
  • Observers use a conversation ticket on /v1/citadels/{id}/conversation/stream, which rejects input.

Bring your own model

The byo-model-gateway policy is deny-by-default and allows exactly one model gateway host. It sets OPENAI_BASE_URL and OPENAI_API_BASE, which OpenAI SDK, LangChain and LiteLLM agents read.

OPENAI_BASE_URL=http://host.docker.internal:11434/v1 \
OPENAI_API_KEY=local \
runeward --config-dir examples serve

curl -sS -X POST http://127.0.0.1:8080/v1/citadels \
  -H 'content-type: application/json' \
  -d '{"profile":"byo-model-gateway"}'

Example OPENAI_BASE_URL ports (path /v1): Ollama 11434, vLLM 8000, llama.cpp 8080, LM Studio 1234, TGI 8080, LiteLLM 4000.

  • Use host.docker.internal for a host runtime on Docker. On Kubernetes, use its Service DNS name and update the [[network.rule]] hostname.
  • For non-OpenAI-compatible runtimes, front them with LiteLLM, or fork the policy: keep default = "deny" and enforce = "strict", change the hostname and inject variables via op = "env://...". A missing env:// source fails sandbox creation closed.
  • Egress is pinned to the gateway's resolved IPs for the sandbox's lifetime. If DNS changes, create a new sandbox.

Full policy: examples/byo-model-gateway.toml.

Escape-denial demo

The demo runs Codex CLI in a hardened sandbox, streams its output, then probes isolation. The agent tries to read a host-only marker, find the Docker socket, write under /root and reach https://example.com, with a /workspace write as the positive control.

  • The agent launch and probes pass through policy, budgets and the audit trail.
  • Commands the agent starts are not intercepted one by one. The non-root container, read-only root, private workspace, resource limits and egress sidecar constrain them.
  • Model output is not the proof. The script repeats every probe via the governed shell API under an allow policy and fails on any leak, successful escape or failed positive control.

Warning: A container is not VM-grade isolation. For hostile workloads set a gVisor or Kata runtime_class; see security.md.

Needs Docker or OrbStack, cargo (the script builds runeward with cargo build --release), curl, jq, asciinema, an OpenAI key in ~/.runeward-openai.key (mode 0600) and the ghcr.io/runeeward/runeward-agent:latest and ghcr.io/runeeward/runeward-egress:latest images.

./scripts/demo-agent-session-escape.sh record      # RUNEWARD_DEMO_PAUSE=1 to open the dashboard first
./scripts/demo-agent-session-escape.sh run         # without asciinema
jq '.probes[] | {probe, verdict, exit_code, stderr}' \
  docs/assets/demos/agent-session-escape/escape-probes.json
./target/release/runeward evidence verify docs/assets/demos/agent-session-escape/evidence.json

Watch in Agent groups → Agent sessions → View. The script writes the .cast in docs/assets/demos/ (the .mp4 there is a curated asset, regenerated separately with an asciicast renderer), plus a redacted transcript, probe results and signed evidence under docs/assets/demos/agent-session-escape/. A failed run keeps docs/assets/demos/agent-session-escape.failed.cast and does not replace the last good recording.

Note: Redaction is defense-in-depth. Review artifacts before committing and add policy scrub_patterns for your credential formats.

Written from docs/agent-harness.md, docs/agent-integrations.md, docs/byo-model.md, docs/agent-session-escape-demo.md at commit 1ee29ff4ab36. Rune Box is open source under Apache-2.0.