Setups
Reusable setup steps that prepare the sandbox and carry their own resources.
setup prepares the sandbox for an environment or product. Use setup scripts to install tools, create fixtures, start services, or expose resources the agent needs. Anything the script writes into /workspace is agent-visible, so keep those paths and contents a realistic job. See Experiment design.
Each variant has its own isolated /workspace. A setup script cannot rely on files or side effects created by another variant.
Accepted shapes:
# String form: one setup script.
setup: "npm install"
# List of strings: multiple setup scripts, run in order.
setup:
- "npm install"
- "npm test -- --help"
# Object form: one named setup with optional scoped resources.
setup:
name: install-cli
script: "npm install"
# List of objects: multiple named setups, run in order.
setup:
- name: install-cli
script: "npm install"
- name: smoke-cli
script: "npm test -- --help"
# Mixed list: strings and setup objects can be combined.
setup:
- "npm install"
- name: smoke-cli
script: "npm test -- --help"Setup object
A setup object is the named form of setup. Use it when setup needs its own files, environment variables, setup-only secrets, MCP servers, skills, or setup checks.
| Field | Required | Type / values | Notes |
|---|---|---|---|
name | Yes | Kebab-case string | Required for object form. |
script | Yes | String | Bash run before setup checks and before the agent. It receives script_environment_variables and AX_VARIANT_ID, not environment_variables. |
description | No | String | Human-readable notes. |
tags | No | String list | Free-form labels. |
files | No | File entry list | Host files staged before this setup's script runs (scoped to variants that resolve this setup). |
environment_variables | No | Environment variable list | Runtime env vars scoped to variants that use this setup. |
script_environment_variables | No | Environment variable list | Env vars only the setup scripts receive. See Secrets only setup can read. |
mcp_servers | No | MCP server list | MCP servers exposed to the agent. |
skills | No | Skill list | Local Agent Skill bundles staged through the resolved agent's native discovery path. |
setup_checks | No | Setup check list | Checks run after setup and before the agent. |
When both a product and environment contribute setup, product setup runs before environment setup.
Which user runs what
In 514-managed sandboxes, setup runs as root so it can install system packages. Everything after it runs as the unprivileged sandbox user, user, with /workspace as its working directory:
| Phase | Runs as |
|---|---|
setup and teardown | root |
| Setup checks, the agent, and tests | user |
After setup succeeds, Fiveonefour hands the root-owned paths setup created under /workspace to user, so setup checks, the agent, and tests can write into the directories setup made there. A path setup gave to another account (for example chown -R 999:999 on a database's data directory) keeps that owner, and paths that existed before setup (staged files, skills, fixtures) keep theirs. A failed setup stops the variant before this step.
Anything setup creates outside /workspace stays owned by root: user can read it but can't write into it without sudo. When a setup check, the agent, or a test needs to write there, hand it over at the end of setup:
setup: |
mkdir -p /opt/npm-global
npm config set prefix /opt/npm-global
chown -R user: /opt/npm-globalax experiment validate warns with root-owned-dir-in-setup when setup creates a directory outside /workspace that a setup check, a test, or a prompt then writes into, and no setup script changes its ownership or permissions. Read-only directories don't need a chown.
A setup's files and skill sources stage before that setup's script and stay visible to the agent for the rest of the run, like top-level files. After the script finishes, Fiveonefour verifies each skill bundle and exposes it through the resolved agent's native discovery path. Setup checks run after that installation. For files only a test should see, use test-scoped files instead.
Secrets only setup can read
Use script_environment_variables when setup needs a credential that nothing after it should see, such as a read token for downloading a private fixture. It takes the same {name, value} entries as environment_variables, with literals or ax://secrets/<slug> references:
environments:
- name: imported-session
setup:
name: fetch-fixture
script_environment_variables:
- { name: FIXTURE_TOKEN, value: ax://secrets/fixture-read-token }
script: |
curl -fsS -H "Authorization: Bearer $FIXTURE_TOKEN" \
https://fixtures.example.com/session.json -o /workspace/session.jsonThese variables reach the setup scripts of each variant that resolves this setup, and nothing else:
| Phase | Receives script_environment_variables |
|---|---|
| Setup scripts | Yes |
| Setup checks, the agent, MCP servers, and tests | No |
| Teardown | No, unless declared on the teardown step itself |
A variant's setup scripts run together, so every setup script of a variant can read the bindings any of its setups declare. When two setups bind the same name, the first one in run order (product setup before environment setup) wins. On a teardown step, script_environment_variables reach only teardown, like that step's environment_variables.
Secret-backed values are replaced with [REDACTED] in the setup log and setup failure messages; a literal value stays as written. Fiveonefour can't stop a script from leaking a value it writes elsewhere, so never write one into /workspace or another file. The sandbox user has passwordless sudo, so this keeps the value out of everything the agent is given, but it is not a boundary against an agent that deliberately escalates to root. Use a read-only, narrowly scoped credential. ax experiment validate warns with setup-secret-also-agent-visible when the same secret is also bound in an environment_variables list the agent receives.
Agent config from setup
Use setup to write agent config into the sandbox home before the agent starts. For codex, write $HOME/.codex/config.toml. The harness merges that file under its own required keys when it prepares the run, so harness settings win on conflict and every other key you set is preserved.
setup: |
mkdir -p "$HOME/.codex"
cat > "$HOME/.codex/config.toml" <<'EOF'
web_search = "disabled"
EOFKeys the harness owns for a Codex run (and will overwrite if you set them):
| Key | When |
|---|---|
show_raw_agent_reasoning | Always |
model_reasoning_summary | Managed model access (the model's reasoning_summary, default auto) |
model_context_window | When the variant sets a context window |
openai_base_url, model_provider, [model_providers.gateway] | Managed model access / custom OpenAI base URL |
[mcp_servers.<name>] for each declared server | When the experiment declares that MCP server |
A malformed $HOME/.codex/config.toml fails the run before the agent starts.