> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stackra.figentra.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Secrets

> Two kinds of secret, two mechanisms, never a third. A human sets every value and an agent never reads one.

## Two kinds, two mechanisms

| Kind | Where the value lives | Who sets it | How code reads it |
| - | - | - | - |
| **Application secret** (payment keys, OAuth client secrets, webhook signing secrets, provider API keys) | The runtime's secret store, per environment | A human | By name at runtime through the runtime's binding, for example `secret("NAME")` once Encore lands. Never from a file. |
| **Developer or CI token** (MCP server tokens, CI vendor tokens) | The Infisical vault. `/agents` for developer tokens, `/ci` for CI. Nowhere else. | A human, in Infisical | Developers: `tools/scripts/mcp.sh` injects a token for the server process. CI: OIDC login, then `infisical run` for one step. |

There is no third mechanism. There is no `.env` file, no `.env.example`, and no second vault. A local override for development goes in a gitignored file that holds no value anyone else needs. Rationale: [ADR 0009](/adr/0009-secrets-two-mechanisms). Procedures: [secrets runbook](/runbooks/secrets).

> [!NOTE]
>
> Stackra has no deployed runtime yet, so only the developer-token mechanism and the repository
> checks are live. The application-secret rules apply from the first service that declares a secret.

## What an agent may do

| An agent may | An agent may not |
| - | - |
| Declare `secret("NAME")` in code and name the key and its purpose in its report. | Set, rotate or delete a value. |
| Reference a developer token in `.mcp.json` as a bare `$KEY`. Not `${KEY}`: Claude Code expands that form itself and disables the server when the variable is unset. | Read a value, in any form, into the transcript. |
| Run `tools/scripts/secrets-doctor.mjs` once it exists. It reports names only. | Write a value into a file, test, fixture, log, comment, commit message or chat message. |
| Use sentinel test values such as `sentinel-<topic>-never-printed`. | Add a `.env` file, a second vault, or a sync into the code host. |

## Articles

Each article names the check that enforces it.

| # | Article | Enforcement |
| - | - | - |
| S1 | No secret value in any file in git: source, test, fixture, doc, comment, workflow, lockfile. | **Enforced.** gitleaks (`.gitleaks.toml`) at pre-commit through lefthook and `pnpm secrets:scan`. CI scan arrives with [ADR 0012](/adr/0012-code-host-and-ci). |
| S2 | Manifests and config carry names only. | **Planned.** Covered by S1 today; a name-pattern check comes with the first manifest that lists secrets. |
| S3 | No static credential stored in the code host. Vendor tokens come from Infisical through OIDC. | **Planned.** A CI workflow allowlist check (`secrets-workflow-allowlist`) lands with the CI pipeline. |
| S4 | CI authenticates by OIDC only, with one machine identity per environment. Pull-request jobs can never read production. | **Planned.** A `vault-oidc` composite step plus an OIDC-permissions check, built with the CI pipeline. |
| S5 | A secret is process environment for one step. Never written to a CI env or output file, an artifact, a cache, a log or a comment. | **Partial.** `infisical login --silent` in the steps; the agent hook denies `printenv`. The CI write check is planned. |
| S6 | No `.env`, no `.env.example`, no second vault in any repository. | **Planned.** `secrets-no-dotenv` check in the validator. The agent hook already denies reading `.env` and `.dev.vars`. |
| S7 | An agent never reads a secret value. Agents see names, never values. | **Partial.** `.agents/hooks/guard-bash.mjs` denies `.env`/`.dev.vars` reads, `printenv` and bare `env`, `encore secret set`, and `wrangler secret put`. It does **not** yet deny `infisical export`, `infisical secrets get` without `--silent`, or `encore secret get`. Adding those rules, with a hook test, is planned. Until then an agent must never run them. |
| S8 | Setting, rotating and deleting a value is a human act, in the vault or store. Production changes need a second person once a second maintainer exists. | **Planned.** Vault configuration; CODEOWNERS now. |
| S9 | A test needs zero secrets. `verify` and every test run with no credential. A test that needs a real one is a design finding. | **Partial.** `pnpm verify` takes no secret. gitleaks scans fixtures. |
| S10 | Test values that look like credentials are built at runtime from fragments, so the scanner sees nothing in the diff. | **Enforced** by S1: gitleaks blocks anything else. |
| S11 | The image never bakes a value. | **Planned**, with the first deployable image. |

## When a secret leaks

1. Rotate first, investigate second. The value is compromised the moment it is pushed. Rewriting history does not un-leak it.
2. Record what, when and when it was rotated. Never paste the value.
3. If gitleaks missed the pattern, add it to `.gitleaks.toml`.

## Not allowed

* A third mechanism "just for this one case".
* A `${KEY}` placeholder that a tool or shell expands into a file.
* A secret in a commit message, PR description, chat message, issue or AI prompt.
* Lowering an article so a gate passes. Change the law through an ADR, or fix the code.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.