Cladding lets you run an agent in a constrained container environment where network access is intentionally narrow:
- The agent runs as a standalone
--network nonecontainer named<name>-agent-instance. - Optional delegated sandboxes run as standalone
--network nonecontainers named<name>-nw-sandbox-instanceand<name>-fs-sandbox-instance. - HTTP(S) egress is mediated by the
<name>-proxypod. Execution containers reach it through scoped Unix-domain socket mounts and localsocatloopback bridges, and Squid uses separate listeners to distinguish agent traffic from network-sandbox traffic. - The agent can reach
host.containers.internalonly on ports listed inconfig-template/agent/host_ports.lst. <name>-nw-sandbox-instanceand<name>-fs-sandbox-instanceeach servemcp-runon a mounted Unix socket and execute commands only when allowed by their Rego policy modules under.cladding/config/.- Proxy allow-lists come from:
.cladding/config/agent/domains.lst(template:config-template/agent/domains.lst).cladding/config/nw_sandbox/domains.lstwhen network sandboxing is enabled (template:config-template/nw_sandbox/domains.lst)
In short: the agent cannot freely access the network; users can run sandbox commands from the host with cladding run-with-scissors, while commands running inside the agent can delegate to sandbox containers with run-in-nw-sandbox or run-in-fs-sandbox.
-
Install Podman
-
Install the
claddingbinary:podman run --rm -it \ -v $HOME/.local/bin:/target/bin \ rust:latest \ cargo install --git https://github.com/dstoc/cladding cladding --root /target --forceAlternative (install Rust locally first):
# install Rust curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # or on macOS with Homebrew brew install rust # from this repo cargo install --path . --bin cladding # or install directly from git cargo install --git https://github.com/dstoc/cladding cladding
-
Initialize local config:
Config and mounts are stored in a
.claddingdirectory.cladding init # or override generated name cladding init myproject -
Edit files under
.cladding/config/:.cladding/cladding.json(generated bycladding init).cladding/config/agent/domains.lst(template:config-template/agent/domains.lst).cladding/config/agent/host_ports.lst(template:config-template/agent/host_ports.lst).cladding/config/nw_sandbox/domains.lstand Rego policies whennw_sandboxis enabled (template dir:config-template/nw_sandbox/).cladding/config/fs_sandbox/Rego policies whenfs_sandboxis enabled (template dir:config-template/fs_sandbox/)
Generated proxy scripts and Squid runtime config are refreshed under
.cladding/runtime/scripts/bycladding up; they are not user-editable config. -
Build images and refresh host-mounted binaries (
mcp-run,run-remote, and sandbox helper wrappers) in.cladding/tools/bin:cladding build
-
Start the environment:
cladding up
-
Run commands in the agent container (workdir follows your host
cwdrelative to the directory containing.cladding):cladding run codex --yolo cladding run --env GEMINI_API_KEY gemini
-
Temporarily publish a TCP port from the agent container to the host while the project is running:
cladding expose 3000 cladding expose 3000 9000
cladding exposeruns in the foreground. Stop it with Ctrl-C. -
Temporarily make one host-reachable TCP endpoint available on agent localhost while the project is running:
cladding inject 11434 cladding inject 5432 15432 cladding inject db.internal:5432 15432
cladding inject <host-endpoint> [containerport]runs in the foreground and stops with Ctrl-C. A bare port such as11434maps agent127.0.0.1:11434to host127.0.0.1:11434. A second port changes only the agent-side listener, socladding inject 5432 15432maps agent127.0.0.1:15432to host127.0.0.1:5432. An explicithost:portis resolved from the host and should be treated as a deliberate temporary exception for that one endpoint, not general host networking.
cladding.json supports a mounts list. Each entry has:
mount(required, absolute path in the container)hostPath(optional, host bind mount; relative paths are resolved from.cladding/)volume(optional, named volume; mutually exclusive withhostPath)readOnly(optional, defaultfalse; ignored forvolumemounts and forcedtruefor empty mask mounts)targets(optional; explicit list ofagent,nw-sandbox,fs-sandbox)ignore(optional, defaultfalse; when true, removes an existing default mount at the samemountpath instead of replacing it)
If neither hostPath nor volume is set, a managed empty runtime volume is mounted read-only - this is intended for masking or hiding underlying files.
Mounts apply to the components named in targets. When targets is omitted, the mount applies to the agent and to nw-sandbox when it is enabled. fs-sandbox only receives custom mounts that explicitly target fs-sandbox.
Example:
{
"agent": {
"image": "cladding-agent:local"
},
"nw_sandbox": {
"enabled": true,
"image": "cladding-sandbox:local"
},
"mounts": [
{ "mount": "/home/user/workspace/.cache/npm", "volume": "npm-cache" },
{ "mount": "/opt/data", "hostPath": "../data", "readOnly": true },
{ "mount": "/tmp/isolated" },
{ "mount": "/opt/nw-sandbox-only", "hostPath": "../nw-sandbox-data", "targets": ["nw-sandbox"] },
{ "mount": "/opt/fs-sandbox-only", "hostPath": "../fs-sandbox-data", "targets": ["fs-sandbox"] }
]
}Default mounts for the agent and nw-sandbox (as if expressed via mounts):
{
"mounts": [
{ "mount": "/opt/config", "hostPath": "config", "readOnly": true },
{ "mount": "/opt/tools", "hostPath": "tools", "readOnly": true },
{ "mount": "/home/user", "hostPath": "home" },
{ "mount": "/home/user/workspace", "hostPath": ".." },
{ "mount": "/home/user/workspace/.cladding" }
]
}The fs-sandbox default mount set is narrower: /opt/config and /opt/tools are mounted read-only, plus an internal run socket under /run/cladding/run/fs-sandbox. It does not receive /home/user, /home/user/workspace, or the .cladding workspace mask unless custom mounts explicitly target fs-sandbox.
Default mounts may be overridden by adding an entry with the same mount value, or removed by adding an entry with the same mount and "ignore": true.
Current runtime shape:
<name>-proxyis the only Podman pod. It contains<name>-proxy-instanceand<name>-proxy-bridge.<name>-agent-instance,<name>-nw-sandbox-instance, and<name>-fs-sandbox-instanceare standalone execution containers with--network none.- Execution containers communicate through scoped Unix-domain socket mounts under
.cladding/runtime/sockets. use_runsc, when enabled, applies only to the standalone execution containers.
flowchart TB
subgraph C["standalone: <name>-agent-instance"]
CA[agent process]
CAP[socat 127.0.0.1:3128]
end
subgraph H[volumes]
WS[.cladding/..]
HOME[.cladding/home]
end
subgraph S["standalone: <name>-nw-sandbox-instance"]
SAP[socat 127.0.0.1:3128]
SA[mcp-run on /run/cladding/run/nw-sandbox/run.sock]
end
subgraph F["standalone: <name>-fs-sandbox-instance"]
FA[mcp-run on /run/cladding/run/fs-sandbox/run.sock]
end
subgraph P["pod: <name>-proxy"]
PB[proxy bridge sidecar]
PX1[Squid listener 127.0.0.1:3128]
PX2[Squid listener 127.0.0.1:3129]
end
WS --> CA
WS --> SA
WS --> FA
HOME --> CA
HOME --> SA
HOME --> FA
CA -- run-remote over UDS --> SA
CA -- run-remote over UDS --> FA
CA -- HTTP(S) proxy env --> CAP
CAP -- proxy/agent/proxy.sock --> PB
SAP -- proxy/nw-sandbox/proxy.sock --> PB
PB --> PX1
PB --> PX2
SA -- HTTP(S) proxy env --> SAP
PX1 -- listener identity, allowlisted domains only --> NET[(Internet)]
PX2 -- listener identity, allowlisted domains only --> NET
The filesystem sandbox has no proxy socket mount and no proxy environment by default. It can run allowed commands through mcp-run, but it is not given HTTP(S) egress.
cladding init [name] # initialize .cladding and config
cladding check # verify required paths/images
cladding ps # list running cladding projects
cladding run [--env KEY[=VALUE] ...] [cmd] # run a command in the agent container
cladding run-with-scissors [--target nw-sandbox|fs-sandbox] [--env KEY[=VALUE] ...] [cmd] # run a command in an enabled sandbox container
cladding expose <containerport> [hostport] # block while forwarding localhost hostport to agent containerport
cladding inject <host-endpoint> [containerport] # block while forwarding agent localhost containerport to a host-reachable endpoint
cladding reload-proxy # reconfigure squid after domain-list edits
cladding logs [agent|proxy|nw-sandbox|fs-sandbox] [podman logs args...] # view container logs
cladding down # stop managed containers and the proxy pod
cladding destroy # force-remove running containers
cladding up # starts the containers
cladding logs proxy -f # follow proxy logs
cladding logs nw-sandbox -f # follow network sandbox (mcp-run) logs
cladding logs fs-sandbox -f # follow filesystem sandbox (mcp-run) logsInside the agent container, use run-in-nw-sandbox -- <cmd> [args...] or run-in-fs-sandbox -- <cmd> [args...] to ask an enabled sandbox to run an allowlisted command. These wrappers call run-remote over the component-specific Unix socket injected into the agent environment.