Skip to main content

Configuration

AGRO has two authored configuration surfaces, split by kind:

FileTrackedHolds
agro.jsonyesevery non-secret setting
.envno — gitignored, mode 0600secrets only

A secret must never reach agro.json, because git tracks agro.json. A non-secret must never reach .env. Code enforces the split: .agro/cli/src/lib/secrets.ts owns the secret allow-list, .agro/cli/src/lib/agro-config.ts owns the agro.json schema and validator, and .agro/cli/src/lib/config-render.ts refuses to render an allow-listed secret into the compose environment.

The two agro.json files​

The same schema has two homes, and the flag you pass picks one:

HomePathWritten byHolds
Registry entry${AGRO_HOME:-~/.agro}/sandboxes/<name>/agro.jsonagro sandbox install docker, then agro config set --sandbox <name>the sandbox: name, runtime, checkout, timezone, git.*, access.*, image.*, storage.homePath, composeOverrides
Project<repo>/agro.jsonyou, and agro config set with no flagthe settings a checkout wants to carry in git

agro sandbox install docker writes the registry entry and, beside it, the compose files and the compose wrapper. The CLI generates those files and re-materialises them on every lifecycle call, so edit only agro.json there. A registry entry keeps its own gitignored .env, written with agro secret set <KEY> --sandbox <name>.

The project agro.json is the seed, not the sandbox. agro sandbox install docker --checkout <dir> reads the project agro.json once to pre-fill the wizard; after that, the entry is authoritative. The CLI writes nothing else into a checkout — no AGENTS.md, no provider configuration, no .gitignore line other than the .env line agro secret set adds inside a git checkout.

agro config show prints the resolved agro.json and agro config set <field> <value> edits one dotted field in it; agro secret set <KEY> prompts for a credential with the input hidden and writes it to .env, and agro secret list shows which keys hold a value with the values redacted. Both accept --sandbox <name> to act on a registry entry instead of the project. agro secret set --sandbox accepts only GH_TOKEN, SANDBOX_PASSWORD, TYPESAFE_API_KEY, and XAI_API_KEY, because Compose passes only these keys to the container. For any other key, the command exits with status 1. Container processes read PI_SLACK_APP_TOKEN and PI_SLACK_BOT_TOKEN from the .env of the project checkout at /home/sandbox/harness. agro langfuse apply reads LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY from the same file. Write these keys with agro secret set <KEY> and no flag. No container process reads META_API_KEY from a .env file. agro config set refuses a secret key and agro secret set refuses a non-secret key, each pointing at the other command. Apply a change with agro stop <name> && agro sandbox install docker --name <name>.

How agro.json reaches the sandbox​

There are two routes, and which one a field takes follows one rule:

A value reaches the sandbox through Compose only if a process outside the sandbox — or the entrypoint before the control plane is readable — must act on it. For every other value, the agro CLI reads the value from agro.json.

Through Compose. .agro/cli/src/lib/config-render.ts renders those fields into KEY=value lines and .agro/scripts/docker-compose.sh passes them to Compose with --env-file. Each also has a default baked into .devcontainer/docker-compose.yml, so an omitted field is not "unset" — it takes that default. A variable already exported in the shell that runs agro beats the value in agro.json.

Through the CLI. The container reads every other value when a process needs the value — .devcontainer/entrypoint.sh calls agro config show. Adding a tool, harness, or setting therefore requires no Compose edit. config-render.ts keeps a RETIRED_KEYS list and throws if a later change renders one of those keys again.

Field reference​

Types are JSON types. "Compose variable" names the variable the field renders to; — means the field never reaches Compose — the agro CLI reads the field, or the CLI consumes the field itself.

Identity​

FieldTypeDefaultCompose variableWhat the field does
versionnumber1—Schema version. Must be 1.
namestringdirectory nameSANDBOX_NAMEContainer and Compose project name.
runtime"docker" | "openshell"unset—The runtime that provisioned the entry. agro sandbox install <runtime> writes the field. The lifecycle verbs read the field to select the runtime. An unset field means docker.
checkoutstringunsetAGRO_REPO_DIRAbsolute host path of a checkout to bind-mount at /home/sandbox/harness. agro sandbox install docker --checkout <dir> sets the field. The CLI selects the build-capable compose base only when that path holds .devcontainer/Dockerfile. This field also lets a lifecycle verb resolve this sandbox from inside that directory. Unset means image-only: the entrypoint seeds the workspace volume from the image's /opt/agro-seed. The field repo and the flag --repo remain as deprecated aliases. A file that holds both fields uses checkout.
timezonestringAmerica/Los_AngelesTZTimezone for log timestamps.
storage.homePathstringunsetAGRO_HOME_MOUNTAbsolute host path for the single /home/sandbox mount. Leave unset and Docker manages it as the named volume <name>_workspace. Must start with /; use a dedicated empty directory, since the sandbox takes ownership of every file in that directory. Set this field at create time with agro sandbox install docker --home-mount <dir>. The flag resolves the argument to an absolute path. The flag creates the directory when the directory is absent. The flag accepts a non-empty directory. agro config set storage.homePath <dir> refuses the change when the named volume <name>_workspace already exists. The next start would swap the mount source and orphan every file in that volume. Pass --force to override the refusal. A stale AGRO_HOME_MOUNT in .devcontainer/.env outranks this value, because the wrapper passes the dotenv last.

Git identity inside the sandbox​

FieldTypeDefaultCompose variableWhat the field does
git.userNamestringunsetGIT_USER_NAMEuser.name for commits made inside the sandbox. Spaces are fine.
git.userEmailstringunsetGIT_USER_EMAILuser.email for commits made inside the sandbox.

Harness and tool installs​

agro.json holds no install field. A harness or tool enters the sandbox only when you run agro harness install <id> or agro tool install <id>. Nothing installs at boot. The install lands in ~/.local inside the persistent home volume, and agro destroy removes it. See Harnesses Overview and Installation.

Access​

FieldTypeDefaultCompose variableWhat the field does
access.dockerSocketbooleanfalseDOCKER_SOCKETApplies the docker-compose.docker-sock.yml overlay. Mounting /var/run/docker.sock is effectively HOST ROOT: an agent can start a privileged container that mounts the host filesystem. See security considerations.
access.sshbooleanfalseSANDBOX_SSHApplies the docker-compose.ssh.yml overlay, which runs sshd for direct container SSH. See sshd.
access.sshPortnumber (1–65535)2222SANDBOX_SSH_PORTHost loopback port published for SSH.
access.sshAuthorizedKeysstringunset—One or more public keys, newline or literal \n separated, read by entrypoint.sh through agro config show. The field holds public key material, not a secret. Without a key and without password auth nobody can log in, and sshd warns loudly.
access.sshPasswordAuthbooleanfalse—Enables SSH password auth, which uses the SANDBOX_PASSWORD secret. agro sandbox install docker generates this value; run agro secret get SANDBOX_PASSWORD to read the generated value. Never enable password auth on a public-facing bind.
access.egressenum: open, allowlistopenAGRO_EGRESSSelects the sandbox's outbound network mode. allowlist routes traffic through a proxy that permits only known hosts.
access.egressAllowarray of strings[]—Extra hosts the allowlist proxy permits, beyond the built-in policy hosts.

Egress allowlist​

Set access.egress to allowlist to remove the sandbox's direct route out. The docker-compose.egress.yml overlay puts the sandbox on an internal: true network and adds a proxy service. The sandbox reaches the internet only through that proxy, and the proxy denies every host outside egress-allow.txt.

materialize() writes egress-allow.txt from one source: the host entries under network_policies in .devcontainer/openshell-policy.yaml, plus access.egressAllow. The built-in hosts are github.com, api.github.com, release-assets.githubusercontent.com, registry.npmjs.org, api.anthropic.com, and platform.claude.com. Add another host with agro config set access.egressAllow "<hosts>" --sandbox <name>, then run agro egress reload to apply the change.

agro egress log [name] prints the last 1000 lines of the proxy access log and marks each denied line DENIED. agro egress reload [name] rewrites egress-allow.txt and sends the proxy squid -k reconfigure; the sandbox container keeps running. Both verbs exit 1 on a sandbox whose access.egress is open and on an openshell entry.

Allowlist mode needs Compose 2.23 or later, because the overlay defines the proxy configuration with the inline configs.content field. The overlay also sets NODE_USE_ENV_PROXY=1, so a Node.js fetch call, including the one corepack makes, uses the proxy.

See Security considerations § Egress allowlist for the limits of this mode and the security boundary it enforces.

Hermes dashboard​

FieldTypeDefaultCompose variableWhat the field does
hermesDashboard.enabledbooleanfalse—Auto-starts the web dashboard in the app-hermes-dashboard tmux session, bound to container loopback.
hermesDashboard.portnumber (1–65535)9119—Container loopback port for the dashboard. Compose does not publish the port to the host. Reach the dashboard from inside the sandbox, or over cloudflared or Tailscale.

Langfuse tracing​

agro config langfuse writes these four fields. The two Langfuse keys are secrets and live in .env, not here. agro langfuse apply reads both surfaces and renders them into the credential fragment ~/.config/agro/langfuse.env and into one tracing file per harness. See Langfuse.

FieldTypeDefaultCompose variableWhat the field does
langfuse.enabledbooleanfalse—Turns tracing on. agro langfuse apply writes the credential fragment only when this field is true. agro langfuse disable sets it to false and deletes the fragment.
langfuse.baseUrlstringhttps://cloud.langfuse.com—Endpoint each harness sends traces to. Pick the URL from where the harness runs, not from where you browse.
langfuse.environmentstringthe name field, else sandbox—Trace environment. Langfuse stores it at write time, so choose the name before you collect traces.
langfuse.userIdstringunset—User id for Pi traces. It reaches ~/.pi/agent/langfuse.json only.

Prebuilt image​

Run a published image instead of building from .devcontainer/Dockerfile. Recipe: Creating a sandbox.

FieldTypeDefaultCompose variableWhat the field does
image.refstringghcr.io/mifunedev/agro:<CLI version>AGRO_SANDBOX_IMAGEPublished image reference. Set the field per sandbox with agro config set --sandbox <name> image.ref <ref>. agro sandbox install docker writes this field only for an explicit pin: --version <X.Y.Z> or --image=<ref>. Without a pin, each start of an image-mode sandbox renders the default. If the CLI version is not a plain X.Y.Z release, the default tag is latest. The install preserves a configured value.
image.mode"build" | "image"build—Whether the lifecycle builds locally or runs image.ref. A build happens only when the entry carries checkout and that directory holds .devcontainer/Dockerfile. Pairs with agro sandbox install docker --image.
image.pullPolicy"missing" | "always" | "never"missingAGRO_PULL_POLICYCompose pull policy for image.ref.

Compose overlays​

FieldTypeDefaultCompose variableWhat the field does
composeOverridesstring[][]—Extra -f overlay paths, applied after the built-in overlays selected by access (last -f wins).

Secrets​

The allow-list in .agro/cli/src/lib/secrets.ts is the complete set of keys the root .env may hold. The tracked .example.env documents each key, commented out:

GH_TOKEN, SANDBOX_PASSWORD, XAI_API_KEY, META_API_KEY, PI_SLACK_APP_TOKEN, PI_SLACK_BOT_TOKEN, LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY, TYPESAFE_API_KEY.

agro secret set rejects any other key.

For Muse Code, agro secret set META_API_KEY stores the key but does not export it into a running shell. See Muse authentication for process injection and credential precedence.

TYPESAFE_API_KEY serves the optional typesafe-ai skill. See .agro/skills/typesafe-ai/SKILL.md.

Environment variables​

VariableDefaultWhat the variable does
AGRO_NO_STAR_PROMPTunset1 suppresses the one-time line ⭐ If AGRO helps, star https://github.com/mifunedev/agro that agro sandbox install prints after its first successful install. The line also stays off when CI holds a non-empty value, or when stdout is not a TTY. The marker ${AGRO_HOME:-~/.agro}/star-prompt-shown records the first display.

Retired keys​

No setting changes the directory layout. config-render.ts refuses to render a retired key such as WORKTREES_DIR, PROJECTS_DIR, or CRONS_DIR. See .agro/ directory layout.