Security model
What a session can reach.
The agent and its tools run inside a Substrate sandbox on the worker. This page lists what that sandbox enforces in 0.3.1, where credentials go, what is recorded, and what is outside the model.
01 / Trust
Trust the worker’s administrator.
The worker host is trusted. Host administration, including mantle worker ssh, runs outside every sandbox and can read every workspace and the Claude token file. The sandbox protects the host and other sessions from the agent; it is not a boundary against whoever administers the worker.
02 / Sandbox
Required before an agent starts.
Substrate reports facts about what it can enforce. mantle start refuses with FAILED_CAPABILITY and the missing names unless the worker reports all of these:
exec.argv_only: programs run from an argument vector, not a shell string.exec.no_egress: the sandbox has no network route of its own.exec.cgroup_limitsfor memory and processes, andexec.cgroup_killfor whole-tree termination.sessions.pty, and the egress aperture namedegress.- For Claude Code, also the
claudesecret slot and the installedclaudeexecutable; for Codex, the installedcodexexecutable.
Inside, /workspace is writable; /opt/mantle and the host’s /usr, /bin, /lib and /lib64 are read-only. The sandbox refuses Unix-domain sockets. CPU is not bandwidth-limited: Substrate 0.16.1 bounds cumulative CPU time only, and resources.cpu only sets CARGO_BUILD_JOBS. A storage quota applies only when the worker serves workspace quotas. Substrate’s full isolation floor is documented in the Substrate documentation.
03 / Network
Ten destinations, all port 443.
The only way out of the sandbox is the egress aperture to mantle-egress, a CONNECT proxy on the worker’s loopback at 127.0.0.1:3128, outside the sandbox. The service runs it with its built-in allowlist, shared by every session on the worker:
| For | Destinations |
|---|---|
| Claude Code | api.anthropic.com:443, platform.claude.com:443 |
| Codex | auth.openai.com:443, chatgpt.com:443 |
| GitHub | github.com:443, codeload.github.com:443, objects.githubusercontent.com:443 |
| crates.io | crates.io:443, index.crates.io:443, static.crates.io:443 |
- Hosts match exactly after normalization: no wildcards, subdomains, lookalikes or other ports.
api.openai.comis refused. - IP-literal targets are refused. Resolved addresses that are not public are discarded, and the dial is pinned to an admitted public address; a mixed DNS answer can connect only to its public member.
- Names are resolved fully qualified, so a resolver search list cannot turn an allowed name into another.
network.capabilitiesin a manifest does not narrow the list.- The gateway tunnels TLS without inspecting it: the list limits where data can go, not what is sent to an allowed destination.
The gateway’s refusals and address filtering are covered by native scenarios with controlled DNS, and were observed on a live worker for 0.1.2 (an unlisted host and direct egress refused).
04 / Credentials
Where the agent’s credential lives.
- Claude Code
- On the laptop,
claude.token_commandorclaude.token_filesupplies the token; error messages name the source, never the value.worker upandstartwrite it to/var/lib/mantle/secrets/claudeon the worker. Substrate passes it to the agent execution through theclaudesecret slot on an inherited descriptor; the launcher puts it in the agent’s environment. Tools the agent starts can inherit it. The worker token file remains afterstop. - Codex
- Device login in the agent terminal. The auth file and conversation files live in
/workspace/.mantle/home/.codex, readable by the agent’s tools.stopkeeps them;destroydeletes them. - Command sessions
- No agent credential: no
claudesecret slot, noCLAUDE_CODE_OAUTH_TOKEN, no Codex home and no Claude or Codex variables. A manifest withagent.authandkind: commandis refused. mantle exec- Gets no agent credential, but shares the workspace, including the Codex home.
- Git
- No Git credential broker. Repositories are cloned over HTTPS without credentials.
05 / Recording
Terminal output may be recorded.
Claude Code, Codex and command sessions use the same Substrate terminal transport, which may record terminal output. There is no end-to-end non-recording mode. Mantle’s own launcher keeps its replay buffer in memory, and Codex’s SQLite and text diagnostics are written to a tmpfs the launcher verifies. mantle status --json and the acceptance runner never fetch terminal output.
06 / Laptop state
Private by default.
Each state directory (default ~/.local/state/mantle, mode 0700) holds the SQLite records, the worker’s SSH key and its pinned host key. Ordinary commands accept the worker’s host key on first use and refuse a changed key afterwards (StrictHostKeyChecking=accept-new). doctor, JSON status and worker upgrade --check accept only an already pinned key. Your configuration file holds cloud identifiers and stays outside every repository.
07 / Outside the model
Not protected, not provided.
- A malicious or compromised worker administrator.
- Data an agent sends to an allowed destination, such as a push to GitHub with credentials it was given in the session.
- Recovery of a lost worker or data volume; snapshots.
- Network policy per session.
- Authenticated end-to-end qualification of either agent; see what is verified.