Skip to content

Agent Environments

An environment is where an agent’s files live and where its commands run. When an agent writes a file, runs a shell command, or executes a snippet of Python, that happens inside an environment. Every org has one by default and never has to think about this page.

You choose an environment per agent, and you can override it for a single chat from the composer. Environments are set up under Settings → Environments.

Built-in sandbox. Our own cloud container: a working directory, a terminal, Python and Node already installed, and internet access. Nothing to set up and nothing to run. This is what every agent uses unless you say otherwise.

Your own container image. A container image you build and publish, which we pull and run on our infrastructure. Use this when your agents need tooling that does not come as a quick install — a cloud CLI, a compiled binary, an internal command-line tool. You get your tools without running a machine.

A machine you own. Our small agent daemon installed on your own laptop, server or build box. The agent runs there, as you, with your permissions — nothing is sandboxed and nothing is blocked, but every command it runs is visible in the chat. Use this when the work has to happen somewhere we cannot reach: inside your network, against a checkout on your disk, with credentials that never leave your machine.

The rest of this page is about the second one.

Start from our published base image and add your tooling. This is a complete, working example — it is how our own reference image with the Azure CLI is built:

FROM ghcr.io/devdepot-ai/sandbox-base:1
USER root
RUN apk add --no-cache libffi openssl \
&& PIP_USER=0 pip install --no-cache-dir --break-system-packages azure-cli \
&& az version
USER node

Build it for Linux on x86-64 and push it somewhere public:

Terminal window
docker buildx build --platform linux/amd64 -t ghcr.io/acme/sandbox:v1 --push .

Then paste ghcr.io/acme/sandbox:v1 into Settings → Environments → New environment → Custom image.

  • Build FROM ghcr.io/devdepot-ai/sandbox-base:1. This is the one requirement that is not negotiable. Our base image carries a marker that container images inherit automatically through FROM, and we check for it — so “it must build on our base” is something we can verify rather than merely ask for. An image without the marker is rejected with exactly that one-line fix. The tag is the contract version, not a release number: an image built on contract 1 keeps working when we publish contract 2.
  • It must be public. We pull with no credentials at all, so private registries are not supported yet. We pull from Docker Hub, GitHub Container Registry (ghcr.io), Google Artifact Registry (gcr.io, pkg.dev), Azure Container Registry (azurecr.io), Amazon ECR Public (public.ecr.aws) and Quay (quay.io) — subdomains included, so myorg.azurecr.io is fine. Always include the host: acme/sandbox on its own is refused rather than quietly assumed to mean Docker Hub.
  • It must be linux/amd64. Every machine that runs sandboxes is x86-64. An image built on an Apple-silicon laptop defaults to arm64, starts perfectly well, and then fails every single command — so we reject it up front instead. --platform linux/amd64 is the whole fix.
  • It must be 5 GB or smaller. Your plan may allow less; it can never allow more. The ceiling is disk on the machines that run your agents, not policy. Very large images can also time out while being pulled even when they are under the limit.

Your image runs as user 1000 with a read-only root filesystem. Whatever USER your image declares, we start it as uid 1000 — so anything you install has to be readable and runnable by that user, and an image whose tooling only works as root is rejected during validation rather than failing mysteriously mid-conversation. Only /workspace (the agent’s working directory) and /tmp are writable at run time; everything else is read-only. Install what you need at build time and it will be there.

That read-only root filesystem is also why the example above says PIP_USER=0. Our base image configures pip to install into the user’s home directory, so that an agent can pip install something mid-conversation and have it work. That directory is temporary storage at run time — so a plain pip install in your Dockerfile appears to succeed at build time and then is not there when the agent starts. PIP_USER=0 puts the packages in the system location, where they survive. Installing through your distribution’s package manager (apk add) is unaffected.

The image has outbound internet, but no reach into private networks. Downloading a file, calling a public API and talking to a cloud provider all work. Reaching a private address — something on our internal network, or on yours — does not, by design. If your agent needs to reach something private, that is what “a machine you own” is for.

What happens after you paste the reference

Section titled “What happens after you paste the reference”

We pull the image, then run it through a few dozen checks: the base marker, the platform, that it starts and stays running as uid 1000, that the working directory is writable, that the root filesystem really is read-only, and that every file operation and command the agent will actually use — write, read, edit, search, shell, Python, snapshot — works end to end inside your image. It is the real machinery, not an approximation of it, so an image that passes here works in a real conversation.

Pulling and checking a large image takes minutes. The environment sits in Validating while it happens; you can leave the page.

If it is rejected, the report names the fix. Each failed check is written to say what breaks and what to do about it, not just what was missing — “missing python3” is not something you can act on, “file listing and artifact delivery break on every turn” is. Fix it, push again, and press Update.

Some findings are warnings rather than rejections. They appear on images that passed and are in use, and they mean something is degraded rather than broken — the most common one is that your image was built on an older version of our base image, which is a nudge to rebuild, never a reason we stop running it.

We pin the image, so a new push changes nothing on its own

Section titled “We pin the image, so a new push changes nothing on its own”

This is the one behaviour worth reading twice.

When validation succeeds, we record the exact image we checked and your agents run that, permanently. We do not follow your tag. If you rebuild and docker push to ghcr.io/acme/sandbox:v1 again, your agents keep running the version we validated — the new one reaches nobody until you ask for it.

Asking for it is the Update button on the environment. It pulls the reference again, re-runs every check, and — if it passes — switches your agents to the new image. Until it passes, they run nothing: Update lets go of the current pin the moment you press it, because it is pointing at something we have not looked at yet.

This is deliberate. A tag is a moving pointer, and following one would mean the image under a running agent could change at any moment, without anybody deciding and without anything having checked the new version.

Re-validate, next to it, is the one that moves nothing. It re-runs the checks against the image we pinned — the exact bytes your agents are running — and never looks at your tag. Pressing it cannot pull in a push you have not asked for, and it keeps your current image and report in place until the new run finishes, so re-checking a working environment cannot leave you with nothing.

That makes it the button to press after we publish a new version of our base image: it tells you how the image you are actually running scores against today’s checks, which is what decides whether a rebuild is worth your time. If it is, Update is how you ship it.

One edge: an environment that failed before we ever got as far as pinning a digest has nothing to re-check. There, Re-validate pulls your reference again — the only thing it could do, and how you get a fresh verdict once you have fixed whatever went wrong.

A broken environment fails, it does not fall back

Section titled “A broken environment fails, it does not fall back”

If an environment cannot start — the image was rejected, or something goes wrong at run time — the agent’s turn fails with a message naming the environment, and we are alerted. It does not quietly run in the built-in sandbox instead.

That is on purpose. An agent that silently loses your tooling looks like it is working: it answers, it runs commands, and every command that needed your CLI fails for reasons nobody can explain for a week. A failed turn you can see beats a downgrade you cannot. So if your agent is behaving oddly, a silent switch back to our built-in sandbox is never the explanation.

An image is only useful if the agent knows to reach for what is in it. So when we validate your image, we work out what it carries that our base image does not — every command-line tool on the path, minus everything ours already has — and put that list in the agent’s own context, along with the Python packages your image has installed.

The practical effect: install az and an agent asked to check a subscription reaches for az on its own, instead of explaining that it has no way to talk to Azure. Without this, a tool can be installed, on the path, working, and still never used.

You can see the same list yourself on the environment, under the details we collected while validating it.