Secrets
Store org secrets securely and inject them into runs.
Every Fiveonefour org has a managed secret store for sensitive values your experiments need at runtime: API tokens, database URLs, GitHub PATs, and similar credentials that must reach the sandbox but must not live in YAML or git.
Saving secrets
Secrets are stored as key-value pairs. When you save a secret, you must provide a slug and a value (a description is optional):
- The
slugis a unique identifier (for exampleprod-gh) used to reference the secret in your experiments. Slugs are kebab-case: lowercase letters, digits, and dashes, starting and ending with a letter or digit. - The
valueis the sensitive value you want to store.
To save a secret you can either:
- Go to the Secrets page in the web app and click
+Add Secret(s). - Use the AX CLI directly, via
ax secret(prompt or stdin; the value is never echoed):
Run `ax learn secrets` and help me add a secret to my org.You can also use the MCP tools secret_set, secret_list, and secret_delete in the current MCP org. See the full ax secret reference for every subcommand.
Who can access secrets?
Right now, all members of the org can reference a saved secret by slug in their experiments. There is no per-secret access control at the moment, but it is expected in the future.
Reference secrets in experiments
You wire a stored secret into your sandboxed runs by referencing the secret's slug in an environment_variables entry in your experiment YAML.
When you declare the environment variable:
value: ax://secrets/<slug>points at the secret in the org store. The<slug>is the secret's unique identifier that you provided when you saved the secret (e.g.prod-gh). (Legacyaxp://secrets/<slug>is also accepted.)nameis the environment variable the sandbox gets (for exampleGITHUB_TOKEN). When the sandbox runs, agents, tests, and scripts read$GITHUB_TOKEN, which is securely resolved from the secret store at runtime.
schema_version: 2
id: github-example
name: "GitHub example"
environment_variables:
- { name: LOG_LEVEL, value: debug } # literal, injected as-is
- { name: GITHUB_TOKEN, value: "ax://secrets/prod-gh" } # $GITHUB_TOKEN ← secret prod-gh
agents: [claude]
prompts: ["…"]
tests:
- { name: noop, script: "true" }
limits: { max_time_seconds: 120 }So prod-gh is the store key; GITHUB_TOKEN is the env-var name you choose for this experiment. A plain string value (for example debug) is a literal and is injected as written.
name must follow the env-var format rules, and there are a few special names that are reserved for internal AX use: see reserved names.
Reusing secrets across experiments
Because the store slug and the env-var name are independent, one stored secret can power many experiments:
- The same slug under different names:
prod-ghasGITHUB_TOKENin one experiment and asGH_TOKENin another. - The same name backed by different slugs across environment or product variants: for example, bind
GITHUB_TOKENtoax://secrets/prod-ghon a prod setup and toax://secrets/staging-ghon a staging setup so every variant still reads$GITHUB_TOKEN, but each hits the credential for its target.
Malformed and missing references
| Problem | Example | What happens |
|---|---|---|
| Reserved scheme that is not a secrets reference | ax://files/config, ax://secret/prod-gh | Rejected by ax experiment validate |
| Secrets reference with a bad slug (not kebab-case) | ax://secrets/prod/GH, ax://secrets/Prod-GH | Rejected by ax experiment validate |
| Valid shape, slug missing from the org store | ax://secrets/missing-slug | Setup fails with Error status before the agent starts |
Secrets in tests
Test scripts receive the resolved environment_variables, so a test can use a secret-backed value the same way the agent does: read $NAME. A test that verifies real side effects against an external system can authenticate with the same secret the agent used:
environment_variables:
- { name: GITHUB_TOKEN, value: "ax://secrets/prod-gh" }
tests:
- name: pr-created
description: The agent opened a pull request on the fixture repo.
script: |
curl -sf -H "Authorization: Bearer $GITHUB_TOKEN" \
"https://api.github.com/repos/acme/fixture/pulls?state=open" \
| jq -e 'length > 0'Do not leak secrets in test output
Test output is captured in run data. Use secret values without printing them: never echo $GITHUB_TOKEN or dump the environment in a test script.
Forwarding secrets to MCP servers
A declared environment_variables entry can also reach the MCP servers a setup exposes, referenced by name: stdio servers take it as an env entry, HTTP/SSE servers as a ${NAME} placeholder in a header value. Only names and placeholders appear in the YAML; values wire in at runtime.
environment_variables:
- { name: GITHUB_TOKEN, value: "ax://secrets/prod-gh" }
setups:
- name: with-mcp
mcp_servers:
- name: github
type: stdio
command: /workspace/github-mcp
env:
- GITHUB_TOKEN # forwarded under the same name
- name: internal-api
type: http
url: http://localhost:8080/mcp
headers:
- name: Authorization
value: "Bearer ${GITHUB_TOKEN}" # placeholder, resolved at runtimeFull schema and validation rules: MCP servers.
Changing a secret value
Runs have no host overrides: only the YAML and the org store apply. To change a value, update the stored secret or the YAML literal.
Model credentials
Model provider credentials are not org secrets. See Model providers.