A sandbox is the runtime boundary where agent code executes. It is created by a
compute runtime and managed inside the workload by openshell-sandbox, the
sandbox supervisor.
Each sandbox workload has two trust levels:
| Process | Role |
|---|---|
| Supervisor | Starts as root inside the workload, prepares isolation, runs the proxy, fetches config, injects credentials, serves the relay socket, and launches child processes. |
| Agent child | Runs as an unprivileged user with filesystem, process, and network restrictions applied. |
The supervisor keeps enough privilege to manage the sandbox, but the agent child loses that privilege before user code runs.
- The compute runtime starts the workload with sandbox identity, callback endpoint, TLS or secret material, image metadata, and initial command.
- The supervisor loads policy and runtime settings from local files or the gateway, depending on mode.
- It prepares filesystem access, process restrictions, network namespace routing, trust stores, provider credential resolution, and inference routes.
- It starts the policy proxy and local SSH server.
- It opens a supervisor session back to the gateway for connect, exec, file sync, config polling, and log push.
- It launches the agent command as the restricted sandbox user.
OpenShell uses overlapping controls rather than a single sandbox primitive:
| Layer | Purpose |
|---|---|
| Filesystem policy | Landlock restricts the paths the agent can read or write. |
| Process policy | The child process runs as a non-root user with reduced privileges. |
| Seccomp | Blocks dangerous syscalls, including raw socket paths that bypass the proxy. |
| Network namespace | Forces ordinary agent egress through the local CONNECT proxy. |
| Policy proxy | Evaluates destination, binary identity, TLS/L7 rules, SSRF checks, and inference interception. |
The supervisor may enrich baseline filesystem allowances for runtime-required paths, such as proxy support files or GPU device paths when a GPU is present.
All ordinary agent egress is routed through the sandbox proxy. The proxy identifies the calling binary, checks trust-on-first-use binary identity, rejects unsafe internal destinations, and evaluates the active policy.
https://inference.local is special. It bypasses OPA network policy and is
handled by the inference interception path:
- The proxy terminates the local TLS connection with the sandbox CA.
- It detects known OpenAI, Anthropic, and compatible inference request shapes.
- It strips caller-supplied credentials and disallowed headers.
- It forwards through
openshell-routerusing the route bundle fetched from the gateway.
External inference endpoints that do not use inference.local are treated like
ordinary network traffic and must be allowed by policy.
Provider credentials are stored at the gateway and fetched by the supervisor at runtime. The supervisor injects resolved environment variables into the initial agent process and SSH child processes. Driver-controlled environment variables override template values so sandbox images cannot spoof identity, callback, or relay settings.
Credential placeholders in proxied HTTP requests can be resolved by the proxy when policy allows the target endpoint. Secrets must not be logged in OCSF or plain tracing output.
The supervisor runs an SSH server on a Unix socket inside the sandbox. The gateway reaches it through the outbound supervisor relay, not by dialing the sandbox workload directly. The relay supports:
- Interactive shell sessions.
- Command execution.
- Tar-based file sync.
- Port forwarding where supported by the CLI/TUI surface.
Sandbox logs are emitted locally and can also be pushed back to the gateway. Security-relevant sandbox behavior uses OCSF structured events; internal diagnostics use ordinary tracing.
When an L4 CONNECT is denied, the proxy emits a DenialEvent. The denial
aggregator batches these events and flushes summaries to the gateway every 10
seconds (configurable via OPENSHELL_DENIAL_FLUSH_INTERVAL_SECS). The gateway
runs them through the mechanistic mapper, which generates a pending
NetworkPolicyRule proposal visible under openshell rule get --status pending.
L7 denials (HTTP 403 from method/path rules) are intentionally excluded from
mechanistic mapping. L4 denials carry only host:port, which a deterministic mapper can handle.
L7 denials carry method, path, query, and body context. The agent loop reads
the structured 403 and authors the narrowest rule. Mechanistically mapping L7
would either over-broaden rules or require path-templating logic that rots
quickly.
- If gateway config polling fails, the sandbox keeps its last-known-good policy.
- If a live policy update is invalid, the supervisor rejects it and keeps the current policy.
- Existing raw byte streams are connection scoped. Dynamic policy changes apply to new connections or the next parsed HTTP request where the proxy can safely re-evaluate.
- If the supervisor relay drops, the sandbox can keep running, but connect and exec operations fail until the supervisor registers again.