# Secrets and Variables Reference (https://www.shipfox.io/docs/reference/secrets-variables)

Description: Store secrets (write-only, encrypted) and variables (readable config) in your Shipfox workspace, and use secret and variable references in workflows.

Shipfox stores two kinds of workspace configuration for workflows. A
**variable** is plain configuration (an environment name, a URL, a flag) that
workflows read with `${{ vars.KEY }}`. A **secret** is a sensitive value (an
API token, a password) that workflows bind with `${{ secrets.KEY }}`. It is
write-only after creation and never resolves outside the runner.

## Managing them [#managing-them]

Both live in workspace settings (**Settings → Secrets** and
**Settings → Variables**).

* **Keys** match `^[A-Z_][A-Z0-9_]*$` (uppercase, digits, underscores).
* **Variables are readable**: the settings page shows values. Edit them in place.
* **Secrets are write-only**: after you save one, no screen or API returns the
  value. Overwrite or delete it. Secrets are encrypted at rest
  with per-workspace keys.
* **Scopes.** Values are workspace-scoped. A project-scoped value with the
  same key overrides the workspace one (project-scoped management is
  API-only today).
* One workspace cap counts secrets and variables together. Self-hosted
  deployments set it with `SECRETS_MAX_PER_WORKSPACE`.

## Using variables [#using-variables]

`${{ vars.KEY }}` resolves server-side when the run is created. It works in
every template site: `run` commands, `env` values, agent `prompt`, `model`,
`provider`, job `runner`/`name`/`outputs`, step `name`, and gate `feedback`.
A reference to a missing variable fails run creation with a typed error.

This job fragment belongs under an existing workflow's `jobs` map:

```yaml
# yaml-language-server: $schema=https://www.shipfox.io/docs/workflow.schema.json
jobs:
  deploy:
    steps:
      - run: ./deploy.sh --target "${{ vars.DEPLOY_TARGET }}"
      - prompt: >
          Check that ${{ vars.STATUS_URL }} reports healthy after the deploy.
```

## Using secrets [#using-secrets]

`${{ secrets.KEY }}` is allowed **only in a run step's `run` command or `env`
values**. It is rejected everywhere else: agent prompts, model/provider
fields, predicates, and job fields. The optional two-segment form
`${{ secrets.local.KEY }}` names the built-in `local` store explicitly.

This job fragment belongs under an existing workflow's `jobs` map:

```yaml
# yaml-language-server: $schema=https://www.shipfox.io/docs/workflow.schema.json
jobs:
  publish:
    steps:
      - run: npm publish
        env:
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
```

How a secret reaches the step:

1. The server never resolves the value. At dispatch, the reference becomes a
   **binding** in the step's config. The value is not stored with the run.
2. The runner pulls the value over a job-scoped, short-lived credential
   before the step executes, injects it into the step's process env, and adds
   it to its masking set.
3. A reference to a missing secret fails that step with a typed
   config-unresolvable error. The job does not silently run without it.

### Masking [#masking]

The runner masks secret values in step logs, agent session streams, outputs,
annotations, and error messages before anything leaves the machine, including
base64, URL-encoded, and hex encodings of the value. Masking reduces exposure,
it is not a guarantee. A step that deliberately transforms and prints a secret
can still leak it, so treat masking as a safety net, not permission to echo
secrets.

### Why secrets never reach prompts [#why-secrets-never-reach-prompts]

Prompts, model responses, and event payloads are inputs to and outputs of a
model. Anything placed there can be echoed anywhere the session goes. Keeping
secrets structurally out of agent fields (and out of predicates) means no
prompt-injection or expression trick can read one. When an agent's work needs
a credential (say, pushing a branch), put the credential on a `run` step in
the same job instead.

## Variables vs. secrets [#variables-vs-secrets]

|                                               | Variable                     | Secret                        |
| --------------------------------------------- | ---------------------------- | ----------------------------- |
| Read back in settings/API                     | Yes                          | No (write-only)               |
| Resolves                                      | Server, at run creation      | Runner, at step execution     |
| Allowed in                                    | Every template site          | Run-step `run` and `env` only |
| Allowed in predicates (`if`, `filter`, gates) | No                           | No                            |
| Masked in logs                                | No                           | Yes (best effort)             |
| Use for                                       | URLs, targets, flags, tuning | Tokens, passwords, keys       |

## Related pages [#related-pages]

### [Context and templating](https://www.shipfox.io/docs/understand/data-and-templating)

The mental model for data flowing through a run.

### [Expressions](https://www.shipfox.io/docs/reference/expressions)

Where templates work and when they resolve.