Secrets
A secret is a credential your functions 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.
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#
[[secrets]]name = "OPENAI_API_KEY"functions = ["chat"] # omit to let every function read itdescription = "Model access for the caddie assistant" [[secrets]]name = "STRIPE_WEBHOOK_SECRET"optional = true # publish can proceed before you have itNames 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#
snoozestack secrets set OPENAI_API_KEY=sk-… # the hosted defaultsnoozestack secrets set OPENAI_API_KEY=sk-test… --dev # local-only overridesnoozestack secrets set OPENAI_API_KEY=sk-… --preview stagingsnoozestack secrets list # names + which namespaces overridesnoozestack 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#
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. 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'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
--devand a preview, the real ones on live, with the same code and the same declared name throughout.