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.

Local runs

The org secret store works on ax experiment run --local too: when you are signed in, store references resolve from your active org the same way they do on remote runs.

The difference is precedence. Local resolution checks your machine first, by env-var name (for example GITHUB_TOKEN), using the same local sources as every other environment variable (.env, --env-file, host env, and --env). Only if no local source supplies that name does the CLI fetch ax://secrets/<slug> from the org store.

  • Local sources always win over the store. A local value is never re-resolved: a .env entry of EXAMPLE_SECRET=ax://secrets/x is injected as that string, not fetched from the org store.
  • A name missing from both local sources and the store fails preflight.

The CLI reports provenance without values: axp: loaded secrets from /path/to/.env when ./.env supplied a referenced name, plus a names-only stderr line per resolved store reference (variant id, env-var name, local sources or org secret store).

Remote runs have no host overrides: only the YAML and the org store apply. To change a value there, update the stored secret or the YAML literal.

Model credentials

Model provider credentials are not org secrets. See Model providers.