Ingram Cloud

Documentation

Sandbox: a filesystem and a shell

Sandbox: a filesystem and a shell

Enable the hosted tool run_command and a smith gets a disposable Linux box for the duration of one run: a writable /workspace, a shell, and network access. It is created when the run first needs it and destroyed when the run ends.

Reach for it when the work is composed rather than called — grep -rn … | head, then a script the agent just wrote, then curl, then check and go again. That loop is what file-manipulation tools can't express at any number of round trips, and it keeps intermediate results out of the model's context.

Enable it

run_command is a hosted tool, off by default. Add it to an agent's enabled_hosted_tools (or a smith's config):

# Authorization: tenant-admin token (server-side only)
curl -X PATCH https://api.cloud.ingram.tech/v1/agents/agt_123 \
  -H "Authorization: Bearer $IC_TOKEN" \
  -H "IC-Api-Version: 2026-06-01" \
  -H "Content-Type: application/json" \
  -d '{"enabled_hosted_tools": ["run_command", "web_fetch"]}'

Confirm it is available on your deployment — the catalog lists only what can actually run:

# Authorization: tenant-admin token (server-side only)
curl https://api.cloud.ingram.tech/v1/tenant/hosted_tools \
  -H "Authorization: Bearer $IC_TOKEN" \
  -H "IC-Api-Version: 2026-06-01"

What the agent gets

One tool, run_command, taking a shell command line and returning stdout, stderr and return_code — the field names Anthropic's hosted shell uses — plus truncated and duration_ms. Commands share a filesystem and run in /workspace. The image carries git, gh, ssh, curl, jq, ripgrep, sed, awk, find and coreutils.

/cache is the one path that outlives the run. See Cache.

There is no TTY. Anything that opens an editor or a pager blocks until the command times out, so the image sets GIT_EDITOR, PAGER, GIT_PAGER, CI and DEBIAN_FRONTEND to their non-interactive values, and git has a default identity so git commit never fails for want of one. Override any of them per box with a secret.

The box has full network access — cloning, fetching and pushing all work. This is the one place we differ from both OpenAI's and Anthropic's hosted sandboxes, which have no internet at all. Egress is what makes a shell useful for back-office work; it is also what makes the secrets advice below load-bearing.

A call that could not run returns the same shape with return_code: 126 and an error_code:

error_code
invalid_tool_inputThe command was empty
too_many_requestsYour tenant already has the maximum number of boxes running
execution_time_exceededThe command passed the per-command ceiling and was killed
unavailableNo box could be started

A command that runs and fails is not an error: it returns its own non-zero return_code and whatever it wrote, and the agent reads it and decides again.

Secrets

A box carries the environment you give it. Set a secret once; every box your tenant launches gets it, and the value is never echoed back:

# Authorization: tenant-admin token (server-side only)
curl -X PUT https://api.cloud.ingram.tech/v1/tenant/sandbox_secrets/GIT_TOKEN \
  -H "Authorization: Bearer $IC_TOKEN" \
  -H "IC-Api-Version: 2026-06-01" \
  -H "Content-Type: application/json" \
  -d '{"value": "ghp_..."}'

List the names (GET /v1/tenant/sandbox_secrets) or remove one (DELETE …/{name}). Names are uppercase letters, digits and underscores. Secrets are encrypted at rest and decrypted only when a box launches — they exist in that box's environment and nowhere else: not in a tool result, not in the run's events.

Scope each credential to one purpose and keep it short-lived. The commands that run there are written by a model, and a run that reads the open web can be told what to do by what it reads. A deploy key limited to one repository is the right shape; an organization-wide token is not.

A deploy key is an SSH key, so store it base64-encoded — a secret is one line — and unpack it in the box:

mkdir -p ~/.ssh && umask 077
echo "$REPO_DEPLOY_KEY_B64" | base64 -d > ~/.ssh/id_ed25519
ssh-keyscan -t ed25519 github.com >> ~/.ssh/known_hosts 2>/dev/null
export GIT_SSH_COMMAND='ssh -i ~/.ssh/id_ed25519 -o IdentitiesOnly=yes'
git clone --depth 1 git@github.com:you/your-repo.git /workspace/repo

Limits

CPU1 vCPU
Memory4 GB
Disk20 GB, on / and /workspace
/cache, carried between runs256 MB, then not saved
Box lifetime20 minutes
One command2 minutes, then killed and reported as 124
Boxes per tenant at once2
stdout / stderr per command64 KB, then truncated: true

These are fixed — there is no per-request knob for them. Output over the cap is truncated rather than discarded, so a find / still tells the agent something.

A run with a shell also gets a larger tool-calling budget than an ordinary run, because shell work spends steps quickly.

The box is destroyed when the run ends. Nothing in /workspace survives, so keep state where you can read it later — a git repository, a vector store, /v1/files, or /cache for what you'd rather rebuild than store. A run that pauses for approval keeps its box and resumes into the same filesystem.

Cache

/workspace is per run. /cache is per smith: whatever a run leaves there is restored into the next run of the same smith, up to 256 MB. Nothing else survives, and nothing enables it — a smith that never writes to /cache stores nothing.

It exists for state that is expensive to rebuild and not worth committing: an index over a corpus, a parsed dataset, a warmed dependency tree. Treat it strictly as a cache. It can be empty on any run — after a rebuild, an eviction, or the first run of a new smith — so an agent that reads it must be able to rebuild what it finds missing.

A checkout does not belong there. Clone into /workspace every run and let the git remote be the durable record. /cache deliberately isn't the working tree: a tree carries paths that something reads or runs without anyone choosing to — .git/hooks, .git/config, AGENTS.md, Makefile — and persisting those would carry a command a run was talked into writing forward into every later run of that smith. Keep /cache to data your agent reads on purpose.

Deleting a smith deletes its cache.

Knowing what ran

When a box comes up it emits sandbox.started carrying image_digest — the digest the image reference resolved to when it was pulled, not the tag it was launched with. A tag moves when the image is rebuilt; the digest does not, so this is what ties a run months later to the exact userland that served it. image and task_definition record what was asked for, which is the other half of the answer when the two disagree.

Subscribe a webhook to it, or read it back from the event log.

In the console

Tools lists run_command with the other built-in tools; enable it per agent. Sandbox secrets are API-only today.

Costs

A box bills nothing today beyond the model tokens the run spends. Shell-driven runs use markedly more tokens than a single tool call — the loop reads output and decides again — so measure a handful of runs on usage before scaling one up.