Workflows secrets overview
Declare secrets on each job using provider references. Loom resolves them before execution and injects temporary file paths by default. Loom replaces exact matches for declared secret values in console/event output, receipt stdout/stderr, output-bearing errors, and provider lifecycle messages. Transformed values and raw declared artifact contents are not covered; inspect and sanitize evidence before sharing it.
For the conceptual model and security background, see:
- Secrets concept — why references instead of values
- Secrets overview — full configuration reference
- Secrets security — threat model and controls
YAML shape
Add a secrets block to any job. Each key becomes an environment variable in the job's execution environment.
deploy:
stage: ci
target: linux
secrets:
DATABASE_PASSWORD:
ref: keepass://local/main#services/loom/deploy:password
file: true
required: true
API_TOKEN:
ref: op://Engineering/services/loom/deploy/token
file: false
required: false
script:
- ./scripts/deploy.sh
secrets is valid only on job blocks — not in default. This prevents a single declaration from fanning sensitive material to every job in the workflow.
Secret spec fields
| Field | Required | Default | Description |
|---|---|---|---|
ref | Yes | — | Provider URI that identifies the secret value. The URI scheme determines which provider resolves it. |
file | No | true | true: Loom writes the value to a 0600 temp file and sets the env var to the file path. false: the raw value is placed directly in the env var. |
required | No | true | true: the job fails if the secret cannot be resolved. false: Loom skips injection and the job continues. An inherited Host variable with the same name can remain. |
Supported URI schemes
| Scheme | Format | When to use |
|---|---|---|
env:// | env://<VAR_NAME> | Value is already in the host environment (CI runner injects it, or exported in the shell). No vault setup required. |
keepass:// | keepass://<database-alias>#<entry-path>:<field> | Local or offline encrypted vault. No external service dependency. |
op:// | op://<vault>/<item-path>/<field> | Centralized team vaults with access control and audit logging. Resolved via the 1Password Go SDK — no op CLI needed. |
All three schemes are implemented in Alpha 1. Missing or invalid provider authentication or configuration produces a SECRETS_* resolution error. See the support boundary and security limits before adopting them.
Choosing a provider
| Consideration | env:// | keepass:// | op:// |
|---|---|---|---|
| Setup effort | Minimal — export a variable | Moderate — create .kdbx, configure alias env vars | Moderate — create service account, export token |
| Offline support | Yes | Yes | No (requires network) |
| Team sharing | Manual (CI runner config) | Manual (distribute .kdbx + credentials) | Built-in (vault access control) |
| Audit trail | None | None | 1Password audit log |
| Best for | Quick prototyping, CI-managed secrets | Solo/local dev, air-gapped environments | Teams, production workflows |
Use env:// for secrets already supplied by your runner, keepass:// for a local encrypted vault, or op:// for a shared 1Password vault.
Injection modes
| Mode | file value | Job sees | Use case |
|---|---|---|---|
| File injection (default) | true | Path to a 0600 temp file | Most cases — lower leakage risk from shell tracing, ps, and interpolation |
| Direct injection | false | Raw secret value | Only when a tool requires a direct env value and cannot read from a file |
File-injected secrets are read in scripts with cat:
DB_PASS=$(cat "$DATABASE_PASSWORD")
For Docker jobs, file-injected secrets are bind-mounted read-only into the container. Scripts inside the container use the same cat "$VAR" pattern.
Validation and guardrails
| Rule | Enforced at |
|---|---|
Secret names must match ^[A-Z_][A-Z0-9_]*$ | loom check (schema validation) |
ref must be a non-empty string | loom check (schema validation) |
default.secrets is invalid | loom check (schema validation) |
A key cannot appear in both variables and secrets for the same job | loom check (schema validation) |
Malformed or unsupported ref URI | Runtime — SECRETS_REF_INVALID |
CI_DEBUG_TRACE=true with any file: false secret | Runtime — SECRETS_UNSAFE_DEBUG_TRACE |
Run loom check before every workflow run to catch schema-level issues early:
loom check
Error codes
| Code | Meaning | Typical fix |
|---|---|---|
SECRETS_PROVIDER_UNAVAILABLE | Provider auth or config is missing or invalid | Check OP_SERVICE_ACCOUNT_TOKEN or LOOM_KEEPASS_DB_* env vars |
SECRETS_REF_INVALID | Malformed or unsupported ref URI | Verify URI scheme and format against the provider's documentation |
SECRETS_REF_NOT_FOUND | Provider cannot find the referenced entry or field | Confirm the vault/database item exists with loom secrets <provider> item list |
SECRETS_REQUIRED_MISSING | A required: true secret could not be resolved | Fix the provider config, or set required: false if the secret is optional |
SECRETS_UNSAFE_DEBUG_TRACE | CI_DEBUG_TRACE=true combined with a file: false secret | Disable debug trace or switch the secret to file: true |
Provider-specific authoring
Each provider has its own URI format, authentication model, and runtime requirements:
- KeePass: Workflows secrets: KeePass — alias configuration, URI anatomy, runtime env vars
- 1Password: Workflows secrets: 1Password — service account token, Go SDK resolution, URI anatomy
- Provider reference docs: Secrets providers index — setup guides, CLI reference, and install instructions
Related docs
- Syntax (v1) — full workflow YAML reference
- Concepts: variables — how variables differ from secrets
- Getting started with secrets — end-to-end first-secret walkthrough
- Migrating from variables to secrets — move sensitive values out of
variablesblocks - Troubleshooting: diagnostics ladder — systematic failure triage