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 slug is a unique identifier (for example prod-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 value is the sensitive value you want to store.

To save a secret you can either:

  1. Go to the Secrets page in the web app and click +Add Secret(s).
  2. Use the AX CLI directly, via ax secret (prompt or stdin; the value is never echoed):
Prompt

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). (Legacy axp://secrets/<slug> is also accepted.)
  • name is the environment variable the sandbox gets (for example GITHUB_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-gh as GITHUB_TOKEN in one experiment and as GH_TOKEN in another.
  • The same name backed by different slugs across environment or product variants: for example, bind GITHUB_TOKEN to ax://secrets/prod-gh on a prod setup and to ax://secrets/staging-gh on a staging setup so every variant still reads $GITHUB_TOKEN, but each hits the credential for its target.

Malformed and missing references

ProblemExampleWhat happens
Reserved scheme that is not a secrets referenceax://files/config, ax://secret/prod-ghRejected by ax experiment validate
Secrets reference with a bad slug (not kebab-case)ax://secrets/prod/GH, ax://secrets/Prod-GHRejected by ax experiment validate
Valid shape, slug missing from the org storeax://secrets/missing-slugSetup 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 runtime

Full 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.