> ## Documentation Index
> Fetch the complete documentation index at: https://docs.auto.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Environments

> Field-by-field reference for environment specs — image presets or direct bases, build steps, per-session setup, caching, resources, env vars, and approvals.

An environment defines the sandbox a session runs in: the base image, extra image build steps, per-session setup commands, CPU and memory, environment variables, and the harness approval posture. Every agent must resolve to an environment after imports merge — it is one of the three required fields of a compiled agent, alongside `name` and `harness`. This page is the exhaustive reference for the environment spec; for how environments fit the bigger picture, see [Environments](/concepts/environments).

## Declaring an environment

Environments are declared **inline on the agent** — there is no standalone environment file. A `.auto/environments/` directory is rejected at apply time with an error telling you to define environments inline in `.auto/agents` YAML, using fragment imports under `.auto/fragments/environments/` for reused runtimes.

The agent's `environment` field takes one of two forms:

* **An inline object.** The compiler splits it into metadata (`name`, `labels`, `annotations`) and spec (everything else), and emits a generated environment resource. The agent's stored spec keeps only the environment's name.
* **A string name** referencing an environment generated elsewhere in the same bundle. A string overrides an inline object during import merge, and vice versa.

The conventional way to share one runtime across agents is a fragment:

```yaml .auto/fragments/environments/agent-runtime.yaml theme={null}
harness: claude-code
environment:
  name: agent-runtime
  image:
    kind: preset
    name: node24
  resources:
    memoryMB: 8192
  steps:
    - RUN apt-get update && apt-get install -y --no-install-recommends jq && rm -rf /var/lib/apt/lists/*
```

```yaml .auto/agents/staff-engineer.yaml theme={null}
name: staff-engineer
imports:
  - ../fragments/environments/agent-runtime.yaml
systemPrompt: |
  You are the staff engineer for acme/widgets.
```

Two rules govern reuse and merging:

* **Deduplication.** When several files generate an inline environment with the same name, the compiled specs must be identical (compared structurally, so key order does not matter). Identical copies dedupe into one resource; divergent content under the same name fails apply with a "Conflicting generated resource" error.
* **Merge semantics.** Across imports, inline `environment` objects deep-merge key-wise, but **arrays replace rather than concatenate** — a later import's `steps` or `setup` array overwrites the earlier one entirely. A fragment that needs "the base steps plus more" must restate the base steps.

## Field reference

The environment spec is strict: unknown keys fail validation.

<ParamField path="environment.name" type="string" required>
  Resource name for the generated environment. Trimmed, 1–128 characters, matching `[A-Za-z0-9_.-]+`. Required when declaring inline.
</ParamField>

<ParamField path="environment.labels" type="map of string to string">
  Free-form labels on the generated environment resource. `annotations` is accepted with the same shape.
</ParamField>

<ParamField path="environment.image" type="object" required>
  The base image. Choose exactly one strict shape:

  * `{ kind: "preset", name: <preset> }` resolves a name from the platform's runtime-preset registry.
  * `{ kind: "base", ref: <image> }` uses one Docker/OCI image reference directly. The trimmed reference is 1–512 characters and cannot contain whitespace, `#`, or `\`, or start with `-`.

  Presets currently resolve as follows:

  | Preset         | Base image              |
  | -------------- | ----------------------- |
  | `node24`       | `node:24-bookworm-slim` |
  | `node22`       | `node:22-bookworm-slim` |
  | `linux-node22` | `node:22-bookworm-slim` |
  | `default`      | `node:24-bookworm-slim` |

  An unknown preset name fails with `Unknown runtime preset`. Direct refs may include a registry, tag, or digest. Mutable tags can resolve to different bytes on later builds; auto preserves the authored reference and does not currently resolve and persist a digest, so pin a digest when reproducibility matters.

  On top of either arm, auto bakes its own bootstrap layers into every image before your `steps` run: base packages (`ca-certificates`, `curl`, `git`, `gpg`), platform-pinned versions of Claude Code and Codex plus auto's agent-bridge runtime, a non-root workspace user named `user` (home `/home/user`, workspace `/workspace`), and an offline copy of the auto docs at `/workspace/auto-docs`. `gh` is deliberately absent — agent GitHub API access goes through the brokered GitHub MCP surface and git auth comes from the credential helper (see [Mounts](/reference/mounts)).

  Direct bases additionally run the `debian-contract-v0` probe as root before that bootstrap. They must be Linux/amd64, expose `/bin/sh`, use glibc 2.28 or newer, and provide `git` or the current Debian/Ubuntu package path that installs it. auto synthesizes this single-stage build; it does not import a repository Dockerfile. Follow [Validate an environment image](/guides/environment-images) for the exact local Docker preflight and classified incompatible fixture.
</ParamField>

<ParamField path="environment.steps" type="array of string" default="[]">
  Dockerfile instructions appended to the generated image build, after auto's bootstrap layers. Each entry is a raw Dockerfile line — in practice `RUN …` commands installing system packages and global CLIs:

  ```yaml theme={null}
  steps:
    - RUN apt-get update && apt-get install -y --no-install-recommends postgresql-client jq && rm -rf /var/lib/apt/lists/*
    - RUN npm install -g tsx
  ```

  Steps are baked into the image, so they run at image build time — not per session — and their results are shared by every session using the environment. Pin versions (and verify checksums for downloaded binaries) to keep image builds reproducible.
</ParamField>

<ParamField path="environment.setup" type="array of object" default="[]">
  Named command groups that run **per session**, inside the sandbox, after git mounts are staged. Each entry:

  | Field      | Type            | Required | Notes                          |
  | ---------- | --------------- | -------- | ------------------------------ |
  | `name`     | string          | yes      | 1–128 chars, `[A-Za-z0-9_.-]+` |
  | `commands` | array of string | yes      | at least one non-empty command |
  | `cache`    | object          | no       | see below                      |

  Setup commands run in the session's working directory (the first mount's `mountPath` unless the agent sets `workingDirectory`), as the non-root `user` account, through `/bin/sh -lc` with `set -eu` prepended — the first failing command fails the step. Each setup entry has a 10-minute timeout.

  Sessions with no working directory — no mounts and no absolute `workingDirectory` — skip setup entirely.

  Use `setup` for workspace-dependent work such as dependency installs; use `steps` for slow-changing system tooling:

  ```yaml theme={null}
  setup:
    - name: install-workspace-deps
      commands:
        - npm ci --prefer-offline
      cache:
        key: npm-workspace-v1
        files:
          - package-lock.json
        paths:
          - /home/user/.npm
  ```
</ParamField>

<ParamField path="environment.setup[].cache" type="object">
  Declares the cacheable state behind a setup entry: the directories that hold its expensive artifacts and the workspace files that define their identity.

  | Field   | Type            | Default | Constraints                                         |
  | ------- | --------------- | ------- | --------------------------------------------------- |
  | `key`   | string          | —       | 1–128 chars, `[A-Za-z0-9_.-]+`                      |
  | `files` | array of string | `[]`    | relative paths, no `..` traversal or absolute paths |
  | `paths` | array of string | `[]`    | absolute paths under `/home/user`                   |

  Setup runs in full when the platform builds the environment's sandbox snapshot, and the snapshot persists everything the commands produced — including the directories under `paths`. Sessions reusing the snapshot re-run the setup commands against that warm state. The `cache` object is part of the snapshot cache key alongside the commands themselves (see `setupCache`), so bumping `key` is the deliberate lever to force a fresh snapshot build when an artifact must be rebuilt even though the commands did not change.

  In the example above, `npm ci --prefer-offline` reads the npm cache at `/home/user/.npm` that the snapshot build already filled, skipping the network fetch that dominates install time.
</ParamField>

<ParamField path="environment.setupCache" type="object">
  Controls how long the sandbox provider may reuse a cached sandbox snapshot that already ran this environment's setup. Shape: `{ ttl: <duration> }`, where `ttl` matches `[1-9][0-9]*(s|m|h|d)` — for example `30m`, `24h`, `7d`. When absent, the default reuse window is 7 days.

  The snapshot cache is keyed by the image build, the mounts, the setup commands, and the working directory, so changing any of those produces a fresh build regardless of TTL. Mounts are re-fetched per session even on a snapshot hit — the TTL only bounds staleness of the baked setup results, never of your code.
</ParamField>

<ParamField path="environment.resources" type="object">
  Sandbox sizing. At least one field is required when the object is present:

  | Field      | Type    | Constraints |
  | ---------- | ------- | ----------- |
  | `cpuCount` | integer | ≥ 1         |
  | `memoryMB` | integer | ≥ 128       |
</ParamField>

<ParamField path="environment.env" type="map" default="{}">
  Environment variables declared on the environment resource. Keys match `[A-Za-z_][A-Za-z0-9_]*`. Each value is either a literal string or a secret reference:

  ```yaml theme={null}
  env:
    NODE_ENV: production
    NPM_TOKEN:
      $secret: npm-publish-token
      optional: true
  ```

  `$secret` names a platform secret; apply-time validation checks that required secret references exist. `optional: true` marks a reference the runtime may resolve to nothing — a missing secret omits the variable instead of failing, and apply does not require the secret to be set. Agents declare env vars with the same value forms at the agent level (see the [agent file reference](/reference/agent-file) and [Secrets](/reference/secrets)).
</ParamField>

<ParamField path="environment.approvals" type="&#x22;bypass&#x22; | &#x22;prompt&#x22;">
  Harness approval posture for sessions running in this environment. Absent means `bypass`: every harness action is auto-approved, because the sandbox is the isolation boundary and unattended sessions must never park on approval questions. `prompt` opts back into the harness's own approval escalation for operator-attended use.

  Only the `codex` harness consults this setting today; `claude-code` always bypasses, so `prompt` under a claude-code agent is currently a no-op.
</ParamField>

## Complete example

A tooled coding runtime, shared by several agents through a fragment import:

```yaml .auto/fragments/environments/agent-runtime.yaml theme={null}
harness: claude-code
environment:
  name: agent-runtime
  labels:
    purpose: agents
  image:
    kind: preset
    name: node24
  resources:
    cpuCount: 2
    memoryMB: 8192
  steps:
    # Image-baked system tooling: shared by every session, rebuilt only when
    # these lines change.
    - RUN apt-get update && apt-get install -y --no-install-recommends postgresql-client jq unzip && rm -rf /var/lib/apt/lists/*
    - RUN npm install -g tsx
  setup:
    # Per-session workspace install, warm from the snapshot's npm cache.
    - name: install-workspace-deps
      commands:
        - npm ci --prefer-offline
      cache:
        key: npm-workspace-v1
        files:
          - package-lock.json
        paths:
          - /home/user/.npm
  setupCache:
    ttl: 7d
env:
  TS_AUTHKEY:
    $secret: tailscale-authkey
    optional: true
```

Note the last block: `env:` at the fragment's root is the **agent-level** env field (this fragment is an agent document that agents import), while `environment.env` would attach the variables to the environment resource itself. Both accept the same value forms.

<Note>
  Changing `steps` changes the image and forces a rebuild on the next session; changing `setup` or the mounts invalidates the sandbox snapshot cache. Neither requires any manual action — the next session picks up the new environment automatically after the change is applied through [GitHub Sync](/concepts/github-sync).
</Note>
