# Secrets

> Declare secret names in the project; set their values with the CLI, per namespace.

[HTML version](https://snoozestack.com/docs/secrets/) · [Docs index](https://snoozestack.com/llms.txt)

A secret is a credential your [functions](https://snoozestack.com/docs/functions.md) need but your repository must never hold — a third-party API key, a signing key, a webhook token. The **name** is declared in `project.toml` (and can name exactly which functions may read it); the **value** is set with the CLI and stored by the platform, never committed.

**Why use it**

Splitting the name from the value is what makes a credential reviewable. The diff shows that a new function wants `STRIPE_SECRET_KEY` and that only `checkout` may read it; the value itself never appears in a pull request, a log line, or the console. A reviewer can reason about blast radius without ever seeing the thing they're protecting.

It also means a rotation is not a deploy. Values live outside the immutable version your project publishes to, so setting a new one takes effect on the running runtimes without rebuilding or republishing anything.

### Declaring one

```bash title="snoozestack/project.toml"
[[secrets]]
name = "OPENAI_API_KEY"
functions = ["chat"]        # omit to let every function read it
description = "Model access for the caddie assistant"

[[secrets]]
name = "STRIPE_WEBHOOK_SECRET"
optional = true             # publish can proceed before you have it
```

Names follow environment-variable rules (letters, digits, underscores). `functions` is the allowlist — a function not named there has no such property on its `capabilities.secrets`, so a leak can't come from code that was never supposed to hold the credential. `optional = true` lets `snoozestack publish` succeed while a credential is still being obtained, instead of blocking the whole release on one pending key.

### Setting values

```bash title="terminal"
snoozestack secrets set OPENAI_API_KEY=sk-…                 # the hosted default
snoozestack secrets set OPENAI_API_KEY=sk-test… --dev       # local-only override
snoozestack secrets set OPENAI_API_KEY=sk-…    --preview staging
snoozestack secrets list                                     # names + which namespaces override
snoozestack secrets unset OPENAI_API_KEY
```

| Namespace | Set with | Used by |
| --- | --- | --- |
| `dev` | `--dev` | Your machine, under `snoozestack dev` — stored in `.snoozestack/secrets.json`, never sent anywhere |
| a preview | `--preview <name>` | That preview only; falls back to the default when unset |
| `default` | no flag | Live, and any preview without its own override |

`--dev` is local-first: `secrets set --dev` writes `.snoozestack/secrets.json` (mode 0600, gitignored) with no account and no network, and `snoozestack dev` reads it at startup and on every `project.toml` reload. A declared name missing from the file is fetched from the linked project's `dev` namespace, when the directory is linked and logged in, and cached into the file so the next start needs nothing; unlinked, it simply reads as unset and the startup line says which name and how to set it. The Secrets page shows where each loaded value came from. `snoozestack dev reset` leaves the file alone — it is your input, not derived state.

`secrets list` prints names and which namespaces override them — never values. A value cannot be read back out of the platform by any surface: not the CLI, not the console, not the management API. If you lose one, rotate it at the provider and set the new value.

| Surface | Availability |
| --- | --- |
| Portal UI | Read-only: declared names and which namespaces override them, never values |
| CLI | `snoozestack secrets set/list/unset`, scoped by `--dev` / `--preview` |
| SDK / HTTP | None, deliberately — a secret that reached a client wouldn't be one |

### Reading one in a function

```ts title="snoozestack/functions/chat/index.ts"
export default async function handler(req: Request, capabilities) {
  const key = capabilities.secrets.OPENAI_API_KEY;   // declared for this function
  // … call the vendor with it; the value never leaves the runtime
}
```

The function must also declare the `secrets` capability — see [Permissions](https://snoozestack.com/docs/permissions.md). Changing a value reaches the runtimes already running, so a rotation needs no republish; what a container cannot pick up without being recreated is a change to its *declaration*.

- **A third-party API key** — The model, payment, or email provider your function calls. Name it for the one function that calls it, and nothing else in the project can reach it.
- **Provider credentials for sign-in** — An [auth provider](https://snoozestack.com/docs/authentication.md)'s client secret or signing key. The provider entry references the secret by name — `secret_name` — so the key itself stays out of the file.
- **A different value per namespace** — Test credentials under `--dev` and a preview, the real ones on live, with the same code and the same declared name throughout.
