How it works

Run AI agents in an isolated, fully controlled environment. An agent gets access to nothing unless you explicitly grant it — and everything it is granted is monitored and logged.

In Contained you set up an agent once — which agent, which of your folders it may see, which servers it may reach, which API key it uses — and then start it with one click. The agent runs in a container (a sandbox) where:

1. Define a sandbox

A guided form in the application — or the same thing as one YAML document. Choose the agent; grants are the rest of the story: folders, servers, keys, limits.

2. Start the agent

The sandbox gets an internal network with no route outside. Its only way out is a proxy that enforces the grants. Granted folders pass through a logging filesystem layer the agent cannot bypass or see behind.

3. Work — and watch

Use the agent as you always do. Beside it, the live log shows what it reads, writes and where it tries to connect — the denied attempts included. Export the sandbox as YAML and reproduce it on any computer.

Isolation is structural, not cooperative: the agent cannot opt out.

Ready-made settings come with the application for Aider, Claude Code, Gemini CLI, OpenAI Codex CLI and OpenCode (see Supported agents); any other command-line agent can be set up by hand. The agents are not part of the download: the application installs them from their official sources into container images on this computer, on the first start of a sandbox that needs them. Every agent works with your own account at its provider.

New here? Follow Getting started — from the download to a running agent, step by step.

Getting started — step by step

From the download to a running agent that sees only your project folder — about fifteen minutes, most of it waiting for downloads.

What you need
  • Windows 10 or 11. On Linux, Contained is a command-line tool only — see CLI reference.
  • About 10 GB of free disk space for Docker and the agent.
  • An account with the provider of the agent you want to use, and an API key from the provider's developer console (step 3). Signing in to the agent with a subscription through a browser is not supported.
  1. Install Docker Desktop

    Agents run in Linux containers, which Docker Desktop provides. Download it from docker.com, install it with the WSL2 backend it suggests, start it once and accept its terms.

    After that you do not need to start it yourself: Contained starts Docker Desktop in the background when it needs it. If Docker Desktop is missing, Contained says so and links to the download.

  2. Install AxioLogic Contained Agents

    Download the installer, Contained-Setup-1.3.2.exe, from the Download page of the website and run it. It installs for your user only — no administrator rights — and adds a Start Menu entry. It checks for Docker, installs the Microsoft Edge WebView2 runtime if Windows lacks it, and offers to add the contained command-line tool to your PATH.

    A newer version replaces the installed one in its folder — it is never installed next to it — and keeps your sandboxes, keys, logs and chat history. The installer says which version it replaces: Ready to Update, from the installed version to the new one. About, at the top of the window, shows the version that runs.

  3. Get an API key

    Create an API key at the provider of your agent and copy it — for example OpenAI (Codex CLI): platform.openai.com → API keys; Anthropic (Claude Code, and Aider and OpenCode as set up here): the Anthropic Console → API keys; Google (Gemini CLI): Google AI Studio → Get API key.

    The key is stored encrypted on your computer and handed only to the agent. It never appears in exported configurations or in logs.

  4. Create a sandbox

    Start AxioLogic Contained Agents from the Start Menu. If Docker Desktop is not running yet, a blue banner shows until it is ready — a cold start takes up to a minute. Click New sandbox: a guide of four pages opens, and most of it is filled in for you.

    • Agent — the first agent installed on this computer is chosen; choose another in the list if you like. Paste your API key.
    • Folders — click Choose… and pick your project folder. The agent sees it and nothing else of your computer; the sandbox is named after it.
    • Network and Limits — already set for your agent.

    Click Save. If something is missing, the guide says what and where. Every page is described in The sandbox editor, page by page.

  5. Start the agent

    In the sandbox's row click Start agent. The first start installs the agent into its container image, which takes a minute or two; later starts take seconds. The agent's console opens at the bottom of the window — work with the agent there as in any terminal. Agents with a conversation interface (so far Claude Code) can also be used in the Chat view.

  6. Watch what it does

    Click Show log. Next to the console you see every file the agent opens, creates, changes or deletes in your folder, every server it tries to reach — allowed or blocked — and every error the application reported for the sandbox. Choose DENY only to see just what was blocked.

  7. Allow more, if needed

    If the log shows the agent was blocked from a server it really needs — for example registry.npmjs.org to install packages — click Edit, go to the Network page, tick the matching common need or add the server under Other servers, click Save, then Stop and Start agent. Nothing is reachable unless you allow it.

  8. Keep the setup (optional)

    Export YAML saves the sandbox as a short document — without the key — that you can review, keep in version control and apply on another computer with contained apply -f (see YAML and infrastructure as code).

The sandbox editor, page by page

A sandbox is one configured agent: the agent program plus everything it is allowed to do. New sandbox creates one and Edit changes it; both open the same editor, a guide of four pages: Agent Folders Network Limits. The panel on the left lists them — click a page to go there — and Next and Back move through them in order. Save (top right, and at the end of the last page) works from any page; Cancel leaves everything as it was.

If something is missing when you click Save, the editor goes to the page in question, says what is missing and marks the page with a red dot. Nothing is saved until everything is in order.

Page 1: Agent

Page 2: Folders

The agent sees only the folders listed here — nothing else of your computer. The first is the project folder: the agent starts in it.

+ Add another folder adds one; Remove takes one away. Without any folder the agent sees nothing of your computer and starts in an empty folder.

Page 3: Network

The agent can reach only the servers allowed here; every other connection is blocked and appears in the log as DENY.

The log records host, port, verdict and time of every request that reaches the sandbox's proxy — never the content.

Page 4: Limits

The lists end at what Docker has for all sandboxes together, which the page shows; with Docker Desktop that is usually less than the computer has. A value set in YAML that is not in a list is kept and shown as it is.

Advanced settings

Folded, on the Agent page. For the agents in the list, everything here is filled in when the agent is chosen. It opens by itself for an agent of your own and when a sandbox's settings differ from what the list fills in.

FieldWhat it meansWhat to enter
ImageThe Docker image the sandbox runs — the agent program and its tools. Contained builds the images of the agents in the list itself (contained/agent-…) on the first start.Leave as set. For an agent of your own: an image that already exists in Docker.
CommandThe program the sandbox starts, with its options, separated by spaces; quotes keep spaces inside one option. This program is what you see in the console.Leave as set (claude, codex, …). To pass an option the agent supports, add it, for example claude --model opus.
Working directoryThe folder inside the sandbox in which the agent starts. With Start in the project folder ticked, it follows the project folder's place.Leave ticked. Otherwise a path starting with /.
Fixed settingsEnvironment variables passed to the agent, one NAME=value per line. They are part of the configuration and appear in an exported YAML.Leave as set. Never put keys here — keys belong in the key fields.
KeysFor an agent of your own: each row is a variable the agent reads a key from, and the key. For an agent from the list: keys it needs besides its API key (usually none).The variable's name as the agent expects it, for example MY_API_KEY, and the key. Stored encrypted, never exported.

Changing an agent (Edit)

  1. Open the editor

    Click Edit in the sandbox's row. The same four pages open with the current settings; the name is fixed.

  2. Change what you need

    Go straight to the page in the panel on the left. A stored key shows stored and an empty field: leave it empty to keep the key, type a new one to replace it, or click Remove the stored key.

  3. Save

    Click Save, from any page. Cancel leaves everything unchanged.

  4. Restart a running agent

    A running agent keeps its old settings until it restarts; its row shows config changed — restart. Click Stop, then Start agent.

Choosing a different agent in Edit brings the new agent's key field and servers; the servers only the previous agent needed go, and your own servers, folders and limits stay. Keys of variables that are no longer used are deleted on Save. The agent's home folder in the sandbox — its settings and history — is kept.

Recipes

You want toIn the editor
Find out what else the agent needsStart it, click Show log, choose DENY only. Each row shows a host and port the agent tried to reach. If it should reach it: Edit → Network → add it under Other servers (or tick the matching common need) → Save → Stop, Start agent.
Let the agent install npm packagesNetwork → Common needs: Install JavaScript packages (npm).
Let the agent install Python packagesNetwork → Common needs: Install Python packages (pip).
Let the agent clone from GitHubNetwork → Common needs: Download code from GitHub (git clone).
Give it a second folder to readFolders → + Add another folder → Choose…; leave Allow changes unticked.
Protect the project from changesFolders: untick Allow changes for the project folder.
Use a different model or agent optionAgent → Advanced settings → Command: add the option, e.g. claude --model opus.
Replace an expired keyEdit → Agent: type the new key into the API key field → Save → restart the agent.

When Save refuses

Save checks every page first. What is missing is named on its page, in a red box, and the page gets a red dot in the panel on the left; nothing is saved until everything is in order. The most common:

The editor saysPageFix
Give the sandbox a name.AgentType a name.
Choose an agent.AgentChoose one in the list — or, for an agent of your own, fill in Image and Command under Advanced settings.
A sandbox named … already exists.AgentChoose another name.
Advanced settings: …AgentOpen Advanced settings and correct the field it names.
… choose a folder.FoldersClick Choose…, or remove the folder.
… does not exist on this computerFoldersThe folder was moved or deleted: choose it again.
… is used by the sandbox itselfFoldersThe sandbox uses /proc, /sys, /dev, /run/contained, /home/node and /usr/local/bin/contained; choose another place inside the sandbox, such as /work or /docs.
… are at the same place inside the sandboxFoldersOne place lies inside another (/work and /work/docs): give each folder its own.
… enter only the server's nameNetworkOnly the name, e.g. api.openai.com — no https://, no path.
… ports are numbers from 1 to 65535NetworkWhole numbers, separated by commas, e.g. 443.

The same rules apply to contained apply, which names the field in the YAML instead: filesystem.grants[0] is the first folder, network.grants[1] the second server, and so on.

Starting and the console

Each sandbox row has these buttons:

ButtonWhat it does
Start agent / Connect agent / Disconnect agentStarts the sandbox and opens the agent's console at the bottom of the window; reconnects to it or hides it. Hiding never stops the agent. One agent runs at a time: while one runs, the others' Start agent is grey.
StopStops the sandbox. Enabled only while it runs.
ChatConversation view with the agent — Claude Code sandboxes, while the agent runs.
Show log / Hide logThe live log of this sandbox, next to the console.
EditChange the settings — see Changing an agent.
Export YAMLSaves the configuration as a YAML file, for backups or another machine (YAML and IaC). Keys are not included.
DeleteDeletes the sandbox (only when stopped): its configuration, its containers and the agent's home folder. Its logs and chat history stay.

Badges next to the status: config changed — restart — settings were edited while the agent runs; API key missing — the agent cannot start until you enter its key under Edit, on the Agent page; cannot start — the configuration cannot be used as it is on this computer, usually because a granted folder no longer exists (hover for the reason, correct it under Edit); audit problem — log records could not be stored (hover for the reason, which is also in the log under errors; the records are kept and stored once the cause is gone).

Copy in the console: select text, then Ctrl+C or right-click. Without a selection Ctrl+C interrupts the agent, as in any terminal. If the agent's screen uses the mouse (Codex does), hold Shift while selecting. Paste: Ctrl+V, or right-click with nothing selected.

The agent's home folder inside the sandbox (its settings and conversation history) is kept between starts and deleted with the sandbox.

Chat

For Claude Code sandboxes, Chat opens a claude.ai-style conversation with the agent working in the sandbox: streamed answers with formatted code, every tool call shown as an expandable chip (file edits as red/green diffs), duration and cost per turn. The sidebar lists past conversations — switch, continue or delete them. Stop interrupts a turn; paste images straight into the message box.

The chat runs Claude Code inside the same sandbox — same folders, same network grants, same log. Its permission prompts are skipped: the sandbox is the containment.

A link in an answer opens in your web browser, never in the Contained window. The window loads nothing an answer names: a picture appears as a link (🖼 and its name). So nothing in an answer reaches the internet past the sandbox unless you open one of its links.

The log

Show log in a sandbox's row shows its log next to the console; Logs at the top of the window shows all sandboxes. Tick errors, files and network in any combination. More filters: sandbox, DENY only / ALLOW only (network) and a path search (files).

Records older than 30 days are deleted automatically by the application (the CLI deletes nothing). Chat history is kept.

Supported agents

The Agent list shows every agent below. Those installed on this computer — found on the PATH or in the usual npm, nvm and pipx locations — come first and can be chosen; the others are shown in grey. A sandbox made elsewhere keeps its agent even when it is not installed here.

AgentAPI keyServers it needsUsed through
Claude CodeAnthropic (ANTHROPIC_API_KEY)api.anthropic.com, platform.claude.comconsole and Chat
OpenAI Codex CLIOpenAI (OPENAI_API_KEY)api.openai.comconsole
Gemini CLIGemini (GEMINI_API_KEY)generativelanguage.googleapis.comconsole
OpenCode (Anthropic models)Anthropic (ANTHROPIC_API_KEY)api.anthropic.com, models.devconsole
Aider (Anthropic models)Anthropic (ANTHROPIC_API_KEY)api.anthropic.comconsole

Docker

While Docker is not usable, the sandbox list shows one banner and the buttons that need Docker are grey:

BannerMeaning
Starting Docker Desktop… N sContained started Docker Desktop — at launch, or because an agent is being started — and waits for it. Usually under a minute.
Docker Desktop is installed but not runningIt was quit while Contained was open. Click Start Docker Desktop, or just start an agent — that starts Docker first.
Docker Desktop did not startNot ready after 3 minutes. Open Docker Desktop to see what it waits for (first-run setup, an update, an error), then Try again.
Docker Desktop is not installedDownload Docker Desktop, install it, then Check again. Contained does not need a restart.

What Contained guarantees — and what it does not

AreaGuaranteedNot guaranteed / by design
NetworkNo route out except the Contained proxy. Every request that reaches the proxy is logged with verdict, host, port, time.The recorded host is the one the agent asks the proxy for (the CONNECT target), not read from the encrypted connection; contents are not inspected — no TLS interception. A direct attempt around the proxy fails but is not logged.
FilesystemOnly granted paths are visible. In audited folders every open (with read/write intent), create, delete, rename, mkdir/rmdir, truncate is logged with path and result.Reads and writes are recorded as the intent when a file is opened, not byte by byte. Ungranted paths do not exist in the sandbox — there is no "denied attempt" event for them. Directory listings are not logged.
PrivilegesThe agent runs unprivileged (uid 1000); containers drop all Linux capabilities except the minimal set the audit layer needs; no-new-privileges is set.v1 uses containers, not virtual machines. Docker and your computer remain the trust boundary.

YAML and infrastructure as code

Every sandbox is one YAML document; the form and the YAML are two views of the same configuration with the same checks. Export YAML saves it; contained apply reproduces it on any machine — byte-identical. Keys are not part of it: enter them again on the other machine.

apiVersion: contained/v1
kind: Sandbox
metadata:
  name: claude-dev                     # Agent → Name
spec:
  image: contained/agent-claude-code:latest  # Advanced settings → Image
  agent:
    command: ["claude"]                # Advanced settings → Command
    workdir: /work                     # Advanced settings → Working directory
    env:
      set:                             # Advanced settings → Fixed settings
        CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1"
      secrets: ["ANTHROPIC_API_KEY"]   # the API key's variable (names only, never values)
  filesystem:
    grants:                            # Folders
      - hostPath: "C:/Projects/MyProject"
        mountPath: /work
        mode: rw
        log: full
  network:
    default: deny
    grants:                            # Network
      - host: api.anthropic.com
        ports: [443]
        log: metadata
      - host: platform.claude.com
        ports: [443]
        log: metadata
  limits:                              # Limits
    cpu: "2"
    memory: 4Gi
  onViolation: log                     # reserved; no effect in v1
contained plan  -f my-sandbox.yaml     # what would change: CREATE / UPDATE / no changes
contained apply -f my-sandbox.yaml     # create or update the sandbox
contained export my-sandbox            # print the stored YAML

Unknown fields are rejected, so a typo cannot silently weaken a sandbox. A definition from version 1.1 or earlier that runs claude on contained/agent-base:latest — where Claude Code used to be — is read with Claude Code's own image, contained/agent-claude-code:latest.

CLI reference

CommandPurpose
contained run -f file.yaml [-d]Store the configuration and start the sandbox; attaches to the agent's terminal unless -d.
contained stop <name> · contained stop -f file.yamlStop a sandbox.
contained secret set|unset <name> <VAR> · contained secret list <name>Manage the keys of a stored sandbox (value read from stdin, never from the command line).
contained login set <server> <user> · contained login unset <server> · contained login listManage the logins for network drives, one per server (password read from stdin). Without one, a drive is connected as guest.
contained logs [name] [-f] [-n N] [--type net|fs] [--verdict allow|deny] [--path substr]Show the log; -f follows it.
contained apply -f file.yaml · contained plan -f file.yamlCreate or update from YAML / show what would change.
contained export <name>Print the stored YAML.
contained versionPrint the version.

Deleting a sandbox is done in the application.

Linux (command-line only). Set hostPath in the example to a folder on this machine, then:

tar xzf contained-1.3.2-linux-amd64.tar.gz && cd contained-1.3.2-linux-amd64
./bin/contained apply -f examples/claude-dev.yaml             # store the configuration
./bin/contained secret set claude-dev ANTHROPIC_API_KEY       # paste the key, Enter
./bin/contained run -f examples/claude-dev.yaml               # start and attach to the agent

On Linux keys are stored unencrypted in the Contained database, protected only by the permissions of the data folder (created 0700).

Logging semantics (precise)

Security notes

Data locations

%USERPROFILE%\.contained\ (override with CONTAINED_DATA_DIR):

In Docker: one volume per sandbox, contained-home-<name>, holds the agent's home folder; the images contained/agent-* hold the agents. Uninstalling Contained leaves all of this in place.

Troubleshooting

What you seeWhat to do
Save does not save and names what is missingSee When Save refuses.
“the API key … is not set” when starting an agentEdit → Agent: enter the key, Save.
The agent asks you to sign in, or reports an invalid keyThe API key on the Agent page is missing or wrong. It must be an API key from the provider's developer console.
An agent is grey in the Agent listIt is not installed on this computer. Install it from its official source and open the editor again (Supported agents).
“No credits”, “quota” or “billing” errors from the agentThey come from the provider's account: the request reached the provider and was refused there.
The agent cannot reach a site it needsSee the first row of Recipes.
The agent stops by itself during heavy workIt probably ran out of memory: Edit → Limits → raise Memory.
The first start takes a minute or twoThe agent's image is being built. Later starts are quick; after an update an image may be rebuilt once.
Badge cannot startHover for the reason — usually a granted folder that no longer exists. Edit and choose the folder again on the Folders page.
Badge audit problemLog records could not be stored, for example because the disk is full. They are kept; free the cause and they are stored. Hover for the reason; it is also in Logs, under errors.
Text cannot be selected in the consoleThe agent's screen uses the mouse (Codex does): hold Shift while selecting.
Chat is greyChat is available for Claude Code sandboxes while the agent runs. Other agents are used through the console.
New sandbox and Start agent are greyDocker is not ready — see the banner and Docker. Start agent is also grey while another agent runs.
Docker Desktop's window flashes when Contained starts DockerDocker Desktop opens its dashboard when it starts (its own setting); Contained minimizes it at once — it stays on the taskbar. To stop it opening at all: Docker Desktop → Settings → General → turn off “Open Docker Dashboard when Docker Desktop starts”.
“the network drive … could not be connected” when starting an agentThe message says why. The server refused the login: Edit → Folders, enter the login for the drive's server. The server cannot be reached: is the NAS switched on and in this network? The folder does not exist on the drive: choose it again — or the login may not see it.