constle docs constle docs pre-1.0

Sandbox an AI coding agent

Run a CLI coding agent without a terminal inside a Constle sandbox: one allowed API host, only the variables you declare, a time limit, and a signed record of every connection it tried.

On this page

Source release coming soon

The source and installers are not public yet, so there is nothing to install today. This guide documents Constle as it behaves now. More in Project status.

What you'll getLink to this section

  • The agent and the code it works on in one container image, run with no default route to the internet.
  • One allowed host: your model provider's API. Every other destination is refused at the proxy and recorded.
  • Only the variables you declare cross from your shell; your other keys, tokens and the rest of your environment stay out.
  • A wall-clock limit enforced by the host, and the change the agent made printed as a patch when the run ends.

What the agent has to supportLink to this section

Constle runs any container image, so it works with an agent as a category rather than as a list of products. CLI coding agents such as Claude Code or Codex CLI are examples of the category. Before you build an image, check in your agent's own documentation that it can do all of this:

The agent must Because
Run without a terminal, from a task given on the command line or in a variable The sandbox starts it detached, with no terminal attached and nothing on standard input. Use its non-interactive mode.
Honour HTTPS_PROXY and HTTP_PROXY Constle sets the proxy variables in the sandbox, and the sandbox has no other route out. A client that ignores them cannot connect at all; it does not get around the proxy.
Work without DNS of its own The proxy resolves names. Inside the sandbox, nothing resolves.
Work on files in its image Constle mounts nothing from your machine. The agent sees what the image contains.
Print its result The agent's output, up to the first 4 MiB, is printed when the run ends.

Also list the hosts it calls. Agents often call more than the model API, for update checks or telemetry for instance; each of those is either declared in allowed_hosts or refused, and refusals are recorded, which makes them easy to find on a first run.

1. Build an image with the agent and the codeLink to this section

The image carries everything the agent needs, installed at build time, so nothing is fetched while it runs. A wrapper script receives the task, runs the agent once, and prints the result as a patch.

Dockerfiledockerfile
FROM node:22-slim
RUN apt-get update \
 && apt-get install -y --no-install-recommends git ca-certificates \
 && rm -rf /var/lib/apt/lists/*
# Install your agent's CLI here, at build time (placeholder name).
RUN npm install -g your-agent-cli
# The repository the agent works on, .git included, so `git diff` works.
COPY . /workspace
COPY run-agent /usr/local/bin/run-agent
WORKDIR /workspace
run-agentshell
#!/bin/sh
# The task arrives in AGENT_TASK, declared under credentials in the Agentfile.
set -eu
cd /workspace
base=$(git rev-parse HEAD)
your-agent --non-interactive "$AGENT_TASK"   # placeholder: your agent's headless mode
git add -A
git --no-pager diff --cached "$base"         # everything it changed, as one patch, in the run's output
shell
chmod +x run-agent
docker build -t coding-agent:0.1.0 .

Install dependencies, test tools and language runtimes in the image too. A package registry in allowed_hosts is a host the agent can send data to, so leave registries out at run time if the build can fetch what it needs.

2. Write the AgentfileLink to this section

agent.yamlyaml
apiVersion: constle.dev/v1alpha1
kind: AgentManifest

identity:
  name: coding-agent
  version: "0.1.0"

sandbox:
  image: coding-agent:0.1.0
  command: ["/usr/local/bin/run-agent"]
  memory_mb: 2048                 # the default is 512; exceeding the limit kills the workload
  network:
    allowed_hosts:
      - api.anthropic.com         # an example: your model provider's API host, and nothing else

capabilities:
  - read_file
  - write_file
  - external_api                  # sets the isolation floor to network

credentials:                      # the only host variables the sandbox receives
  - name: ANTHROPIC_API_KEY       # an example: the key your agent reads
  - name: AGENT_TASK

limits:
  max_duration_seconds: 900       # the host kills the run at 15 minutes

The provider host and key name are examples: use the ones your agent actually reads. command is exec form, so run-agent does the shell work inside the container, where $AGENT_TASK is expanded.

3. Give the agent an identityLink to this section

shell
constle identity create coding-agent [email protected]

Paste the printed did:key:… into the Agentfile under identity.did. From then on every audit entry is Ed25519-signed and hash-chained, and constle run refuses to start if the matching private key is missing. The key stays in ~/.constle/identities/coding-agent/ and never enters the sandbox. Not on Windows: limitation 7. More in Give each AI agent a verifiable identity.

4. ValidateLink to this section

shell
constle validate agent.yaml

It prints the resolved isolation (network, inferred from capabilities) and warns about anything declared that won't be enforced, and about credentials not exported on this machine.

5. Run itLink to this section

shell
export ANTHROPIC_API_KEY=...
export AGENT_TASK="Fix the failing date-parsing test in src/dates and explain the change."
constle run --backend=docker agent.yaml

The agent's output, ending with the patch, is printed when the run ends. Review it and apply it to your own checkout with git apply if you want it. Nothing the agent changed inside the container reaches your files any other way.

Why Docker here

The Docker backend runs the image you built. The Firecracker backend (isolation: kernel) does not pull an image: it boots a guest root filesystem installed on the host by its setup script. See Architecture.

6. See what it tried to reachLink to this section

shell
grep network ~/.constle/logs/coding-agent-$(date -u +%F).jsonl
constle audit verify --agentfile=agent.yaml ~/.constle/logs/coding-agent-$(date -u +%F).jsonl

Every allowed and refused connection is a network_allowed or network_blocked event with its host. A network_blocked line for a host the agent needs tells you what to add; one you didn't expect tells you something else. Reading and verifying the log in more depth: Verify what an AI agent did.

What Constle enforces here, and what it doesn'tLink to this section

Enforced from outside the agent Not covered
No route out except the proxy; undeclared hosts, raw IPs and internal addresses refused Anything sent to an allowed host. The model API receives whatever the agent sends it, including code from the image.
Only the declared variables cross from your shell The declared values themselves: the agent can read ANTHROPIC_API_KEY
The memory limit and the wall-clock limit Files inside the image: there is no filesystem policy, and nothing is gated on writes
One network policy for the whole sandbox Per-program rules: every process in the sandbox, including tests the agent runs, reaches the same hosts
A signed record of every connection attempt What the agent said over an allowed connection: Constle never intercepts its TLS

Spending through a model API is not metered, because that traffic is never decrypted (limitation 3); set a usage limit for the key with your provider if it offers one, and keep max_duration_seconds. The full list: Known limitations.

FAQLink to this section

Can I use the agent interactively inside the sandbox?Link to this section

No. The agent starts detached, with no terminal and nothing on standard input, so it has to run in a non-interactive mode. Human approval for specific tool calls happens at the host instead, through the MCP gate: Require human approval for MCP tool calls.

Does the agent see my repository or home directory?Link to this section

Only what you copy into the image. Constle mounts nothing from the host, and no variable from your shell crosses unless the Agentfile names it under credentials.

How do I get the agent's changes out?Link to this section

The simplest way is the one above: print a patch, review it, apply it yourself. An agent can also push to a code host if you add that host to allowed_hosts, but then that host is a place it can send anything, so weigh that before you allow it.

Which coding agents work?Link to this section

Any agent that meets the requirements in the table above. Constle does not integrate with a particular agent; it runs a container image. Check your agent's documentation for its non-interactive mode, its proxy settings and the hosts it calls.

Is Docker isolation enough for an agent that runs code it wrote?Link to this section

Docker gives the agent its own process and network namespaces on a shared host kernel. For a guest kernel behind KVM, declare isolation: kernel; the run then needs the Firecracker backend and refuses to fall back to Docker. See Architecture.