intentic
Create your workspace
Run a sandbox

Docker setup

A sandbox is a Docker container on your own machine. This page is the anatomy behind every install path: what gets created, where your data lives, what the container is and isn't allowed to do, and how to update or remove it.

On this page(14 sections)

Install commands are on Quickstart

Looking for the commands that bring one up? Those are on Quickstart, which covers all four routes: desktop app, setup command, Docker Compose, and plain docker run. Everything below is what those routes have in common.

Requirements

  • Docker Engine on Linux, WSL2, or macOS. That's the only dependency.
  • Enough headroom for your workload: agent turns, dev servers, and builds run on your hardware, so a sandbox uses what your work uses. There are no imposed CPU or memory caps.

Installing Docker

The desktop app and the setup command both install Docker for you if it's missing, and both ask first: it's a root-level change beyond the sandbox itself, so it is never silent. On Linux that's get.docker.com. In an unattended run set INSTALL_DOCKER=1 to pre-consent, or install Docker yourself beforehand and every path will use it.

Windows needs more than Docker

Docker's Linux engine on Windows runs inside WSL2, so a Windows PC has a short chain of requirements rather than one: hardware virtualization enabled in firmware, the Virtual Machine Platform and Windows Subsystem for Linux features turned on, a current WSL2 kernel, and then Docker Desktop itself. Windows 10 version 21H2 or newer, 64-bit Intel or AMD.

Every install path checks that chain in one go before touching anything, shows you the whole list, and asks once. It then turns on what it can (the features, then a restart), installs Docker Desktop (through the Windows package manager where there is one, and straight from docker.com where there isn't), starts its engine and makes sure it is in Linux-container mode. What it cannot do it says so about: a firmware virtualization switch is changed in BIOS/UEFI before Windows boots, and a Windows running inside a virtual machine needs nested virtualization enabled on its host.

The same examination is available on its own, read-only: ic docker prepare --dry-run. See Troubleshooting for what each outcome means.

What gets created

Two containers, three named volumes, one network: all derived from your sandbox's slug, which is the first label of its public hostname (my-box in my-box.intentic.dev). Every install path derives these identically, which is what lets several sandboxes share a machine and lets one move between paths later.

ObjectNameWhat it is
Containerintentic-sandbox-<slug>The daemon, your agent, your workspace
Volumeintentic-workspace-<slug>/work: your repos and files
Volumeintentic-history-<slug>/history: version history and snapshots
Volumeintentic-docker-<slug>/var/lib/docker: the in-sandbox engine's own storage
Networkintentic-workspace-<slug>Where the connector reaches the sandbox by its alias
Look at yours
# Everything this machine's sandboxes created, by name.
docker ps -a  --filter name=intentic-sandbox
docker volume ls --filter name=intentic-
docker network ls --filter name=intentic-workspace

/history is a separate volume mounted outside /work, and that is deliberate: an agent rm -rf-ing its workspace cannot reach the history that would let you recover from it. Both survive a container being removed and recreated, which is why update and rebuild can swap the image under a sandbox without touching your files.

Backing them up

There is no intentic backup service, and there is nothing for one to do: these are ordinary named Docker volumes, so whatever you already use to back up a volume works here. intentic-workspace-<slug> is the whole workspace and intentic-history-<slug> is its history; intentic-docker-<slug> is a rebuildable cache and worth skipping. Your repos' own remotes are the other half of this, and the better half: anything pushed is already off the machine.

Ports: none inbound, one on loopback

Nothing is published to the network. The sandbox is reached over a private tunnel that its own agent dials outward, so there is no port to forward and nothing to expose. Inside the container the daemon serves the tunnel on 8787 and preview servers on 5173; neither is bound on your machine.

The one exception is a shortcut, not an opening. A browser on the same machine shouldn't have to travel out to the tunnel and back, so the container publishes a second listener on 127.0.0.1 only, at a port derived from your sandbox's own id. Nobody announces that address; your browser computes the same number from the token it holds. If something else holds the port, every install path retries without it and the sandbox works over its tunnel as usual.

What the container may do

The sandbox runs unprivileged, with two Linux capabilities and no others. Both are scoped to the container; neither is host access.

  • SYS_ADMIN: lets the daemon give each isolated agent turn its own mount namespace, so a conversation's worktree can stand in for /work. Without it, isolated turns quietly stop being isolated.
  • SYS_PTRACE: lets the daemon be diagnosed in the one place it ever misbehaves: production. When PID 1 stalls, the question is what it's blocked in, and answering it needs a stack.

The host's Docker socket is never mounted. That single fact is what bounds everything an agent can do: whatever it runs, it runs inside this container, and it has no handle on the machine outside it.

The isolated Docker engine

Some work needs Docker inside the sandbox: pnpm db:up, a docker compose stack, a local database. The image bakes its own Docker Engine, dormant until you add the docker capability, which flips a single privileged directive and wakes it. The agent's containers then live entirely inside the sandbox's own engine: still no host socket, so nothing it starts can touch the machine outside.

On a Compose-managed sandbox that privileged: true stays your own edit to the file. The rebuild flow recreates containers with docker run, and would fight Compose over ownership if it wrote it for you.

The nested engine also runs Ollama, for people who want that runtime specifically: docker run -d -p 11434:11434 ollama/ollama inside the sandbox (add --gpus=all once the docker capability's GPU option is on), then point a Model endpoint card at http://127.0.0.1:11434/v1. If you don't care which runtime serves the weights, the Local model card does all of this with one pick and no engine at all. See Models.

Environment overlays

A sandbox's tools aren't described in a prompt; they're installed in the image. When a job needs a library or CLI, the agent proposes a Dockerfile layer (RUN/ENV only, on a pinned base); nothing changes until you approve it, and the rebuild runs outside the container against the approved hash. So an agent can never rebuild its own environment without your sign-off. Typical additions: a database client like psql, a headless browser, or your language toolchain.

A few capabilities need more than a layer: WireGuard needs a tun device, the nested engine needs --privileged, GPU passthrough needs --gpus. Those ride in as allowlisted # intentic:runtime directive lines in the same approved overlay, so an overlay can't smuggle arbitrary docker flags. A host that can't satisfy an optional one (a machine with no NVIDIA runtime) starts the sandbox without it rather than failing the launch, and records that it couldn't.

Describing this sandbox's shape

The approved overlay is one section of a sandbox.toml definition, alongside repo remotes, connection shapes, secret names and non-default agent settings. Download it from Sandbox → Environment when you want a reviewable recipe for another sandbox; use a private bundle when you need the transcripts and unpublished state too.

The run contract

Every path composes the same docker run from one run contract that ships inside the image. The scripts don't hard-code it and neither should your tooling: ask the image, and a stale caller still starts a new image correctly.

Ask the image
# What every path runs, printed rather than guessed at.
tr '\n' '\0' < sandbox.env | docker run -i --rm --entrypoint intentic \
    ghcr.io/intentic/sandbox:stable sandbox run-command \
    --slug '<SLUG>' --image ghcr.io/intentic/sandbox:stable \
    --base-image ghcr.io/intentic/sandbox:stable --format sh

It reads your environment from stdin (NUL-framed, because a private key is multi-line), filters it to the vars a sandbox is allowed to receive, and prints the complete, correctly quoted command, or the raw argv with --format json. That is the supported way to run a sandbox from your own scripts; Quickstart has it as a worked example.

Update, rebuild, cleanup

The same vanity host that serves the setup script serves the lifecycle helpers:

Maintenance
# Remove a sandbox. Lists yours, asks before deleting its /work + /history.
curl -fsSL https://intentic.dev/cleanup | sh
curl -fsSL https://intentic.dev/cleanup | sh -s -- <SLUG>      # one, by slug
curl -fsSL https://intentic.dev/cleanup | sh -s -- --all -y    # every one, no prompt

# Update (pull :stable and recreate in place) and rebuild (apply an
# approved overlay). The platform hands you the exact command on the matching card.
curl -fsSL https://intentic.dev/update  | sh -s -- <SLUG>
curl -fsSL https://intentic.dev/rebuild | sh -s -- <SLUG> <SHA256>
  • Update pulls the latest :stable image and recreates the container in place, preserving /work and /history.
  • Rebuild applies an owner-approved environment overlay, with its hash as the trust anchor.
  • Cleanup removes a sandbox's containers, named volumes, and networks, which does delete its data, so it confirms first and never wipes everything unless you ask.

Each has a PowerShell twin for Windows: add .ps1 to the path (/update.ps1, /rebuild.ps1, /cleanup.ps1, /connect.ps1). The platform surfaces the exact update and rebuild commands on your Sandbox and Environment cards when they apply. In the desktop app they are buttons instead: a sandbox can never recreate its own container, and something has to do it on the host.

Moving between install paths

Because the names are derived rather than invented, a sandbox is not locked to how it was created. Bring one up with the setup command and later manage it with Compose, or the reverse: the containers are recreated, the three volumes reused, your workspace untouched. Stop the old container first (docker rm -f intentic-sandbox-<slug>) so the two managers aren't holding the same name.

Type to search every page, in the docs and the API reference.