Secrets
Declare secret names in the file (including vendor API keys), set values in the encrypted project vault, or use type: managed for Enterprise platform keys.
What it is. A named group of sensitive values your workflows need: API keys, tokens, credentials. The file declares the variable names; the values live in the project vault, never in the file — unless you use type: managed so Swirls supplies Enterprise platform keys.
Use it when a node calls a service that needs a key you hold, several workflows share the same credentials, or you want Enterprise platform-managed vendor keys declared in DSL.
Works with credential profiles (auth blocks that read values from a secret block), agents (every agent block references a secret block for its model key), context.secrets in @ts code, and the per-node secrets: map below.
User-defined secrets flow through context.secrets. Vendor integrations (AI, Resend, Firecrawl, Parallel) resolve their API keys internally from declared secret blocks; you do not read those keys from context.secrets in @ts blocks unless you also list them on the node secrets: map.
secret
Group related secret variable names (not values) in a top-level block. Values are set out-of-band in the project vault; no value ever appears in a .swirls file or a deployment.
| Field | Type | Required | Description |
|---|---|---|---|
label | string | No | Display label. |
description | string | No | Description. |
type | identifier | No | Only managed — platform supplies values (Enterprise). Omit for user vault values. |
vars | identifier array | Yes | Secret key identifiers (e.g. API_KEY, OPENROUTER_API_KEY). |
secret api_k {
label: "Third-party API"
vars: [API_KEY, API_SECRET]
}
secret llm_keys {
type: managed
vars: [OPENROUTER_API_KEY]
}Block names and var names match ^[a-zA-Z0-9_]+$. Repeating a var within one block is an error. Managed blocks may only list known platform vendor keys.
The project vault
Each project has its own vault. Values are encrypted at rest with per-project keys, and the vault is write-only: no CLI command, API route, or Portal page ever returns a stored value. Listings show whether a value is set, never what it is.
Set values with the swirls secret commands or on the project's Secrets page in the Portal:
| Command | Behavior |
|---|---|
swirls secret set KEY=VALUE | Stores the value as block::VAR for each matching non-managed secret block var. Rejects managed vars. |
swirls secret list | Lists the vault's keys and whether a value is set. Alias: ls. |
swirls secret remove KEY | Deletes the stored value. Alias: rm. |
All three take --project to target a project explicitly. If the same var name is declared in more than one block, swirls secret set writes every matching non-managed target and reports each.
Managed blocks show as "Managed by Swirls" on the Secrets page — no value input is offered.
Node-level secrets:
On root { } or node name { }, list which vars from which blocks this node may access:
workflow example {
label: "Example"
root {
type: code
label: "Entry"
secrets: {
api_k: [API_KEY]
}
code: @ts {
const key = context.secrets.api_k.API_KEY
return { hasKey: Boolean(key) }
}
}
}Access pattern: context.secrets.<blockName>.<VAR>. See Context. The secrets: value is always an object literal, never a bare block name, a string, or a flat array. There is no process.env in @ts code; it is an empty object in the sandbox.
A node sees only the vars it declared. Secrets are delivered at run time, per node, so a workflow's blast radius is exactly what its file says it is.
Who references a secret block
| Consumer | Syntax | Meaning |
|---|---|---|
| Any node | secrets: { <block>: [VAR, ...] } | Allowlist of vars the node may read via context.secrets. |
auth / postgres block | secrets: <block> (bare identifier) | The single block whose vars back the credential fields. |
agent block | secrets: <block> (bare identifier, required) | Block holding the model API key and any agent credentials. |
webhook block | secret: <block>.<VAR> (dotted reference) | Shared-secret value compared against the inbound header:. |
The validator checks that every referenced block exists and every listed var is declared in it.
Required vendor keys
Node types that call third-party APIs need the corresponding key declared in some secret block (user or type: managed):
| Node type | Required secret |
|---|---|
ai, agent | OPENROUTER_API_KEY, or OPENAI_API_KEY / ANTHROPIC_API_KEY / GOOGLE_GENERATIVE_AI_API_KEY per provider: |
email | RESEND_API_KEY |
scrape, search | FIRECRAWL_API_KEY |
parallel | PARALLEL_API_KEY |
disk | ARCHIL_API_KEY (platform-only; never declared in DSL) |
Until a required key is declared and satisfiable (vault value set, or managed + Enterprise), executions for the nodes that need it are held with secrets_hold. They pick up the fix on the next run after you update the DSL / vault / plan.
Further reading
Connections
How your system talks to the outside world safely. Secrets hold values, auth blocks describe credential profiles, and connections broker OAuth with no keys at all.
Credential profiles
Reusable credential profiles for outbound HTTP and inbound form gates. Declared as auth blocks in the file — OAuth client credentials, API keys, basic, and bearer.