Getting started with secrets
Configure a secrets provider and run a local job that checks whether Loom injected a non-empty secret file. Choose 1Password for a shared vault, KeePass for a local encrypted vault, or env:// for a value already supplied by your shell or CI runner.
What you will do
- Configure a 1Password service account token.
- Create or verify a secret in your 1Password vault.
- Reference the secret in a workflow.
- Run the workflow and check the injected file without printing its contents.
Prerequisites
- Loom CLI available (run
loom versionto confirm). - A working Loom workspace (
loom checksucceeds). - A shell session where you can export environment variables.
Option A: 1Password (recommended)
1Password provides shared vault management, access control, and audit logging. Loom resolves op:// references through the 1Password Go SDK; it does not require the op CLI.
A1. Export your service account token
Create a 1Password service account if you do not have one. The token starts with ops_....
export OP_SERVICE_ACCOUNT_TOKEN="ops_..."
Verify connectivity by listing accessible vaults:
loom secrets op vault list
You should see output like:
name=Engineering id=vlt_abc123
name=Platform id=vlt_def456
A2. Create a secret (or use an existing one)
If you already have an item in your vault, skip to A3.
The examples use a sample value, not a working deployment credential. Replace Engineering with a vault the service account can write to, then create an item:
export DEPLOY_TOKEN_VALUE="tok_example_abc123"
loom secrets op item create \
--vault Engineering \
--item-path services/loom/deploy \
--field token \
--value-from-env DEPLOY_TOKEN_VALUE
Expected output:
item field created: vault=Engineering item=services/loom/deploy field=token
Verify the item exists (no secret values are printed):
loom secrets op item list --vault Engineering
services/loom/deploy token
A3. Reference the secret in a workflow
Save this workflow as .loom/workflow.yml. Replace Engineering with the vault used above. The job checks the injected file locally and does not contact a deployment service:
version: v1
stages: [ci]
check-secret:
stage: ci
target: linux
secrets:
DEPLOY_TOKEN:
ref: op://Engineering/services/loom/deploy/token
script:
- test -r "$DEPLOY_TOKEN" && test -s "$DEPLOY_TOKEN"
- echo "Secret file is readable and non-empty"
Key points:
refformat:op://<vault>/<item-path>/<field>.filedefaults totrue:$DEPLOY_TOKENcontains a path to a temp file. Read the value withcat "$DEPLOY_TOKEN".requireddefaults totrue: if the secret cannot be resolved, the job fails before script execution.- Reference in YAML: the
secretsentry contains anop://reference, not the token value. Do not place credential material elsewhere in the workflow.
A4. Validate and run
loom check validates workflow structure. The run resolves the secret and checks the injected file.
loom check
loom run --local --workflow .loom/workflow.yml
What to expect:
- The job starts and the secret is resolved from 1Password.
- The script prints
Secret file is readable and non-emptyand the run exits with status 0. This checks injection, not every redaction path. - Loom replaces exact matches for declared secret values in console/event output, receipt stdout/stderr, output-bearing errors, and provider lifecycle messages with tokens such as
[REDACTED:SECRET_DEPLOY_TOKEN]. - Runtime logs are under
.loom/.runtime/logs/<run_id>/. Redaction does not cover transformed values or raw declared artifact contents; inspect and sanitize evidence before sharing it.
Option B: KeePass (local encrypted vaults)
KeePass stores secrets in a local encrypted .kdbx file. This example uses the explicit alias local for creation, item management, and runtime resolution.
B1. Create a KeePass vault
export KEEPASS_PASSWORD="pick-a-strong-master-password"
loom secrets keepass vault create \
--vault-path local \
--password-from-env KEEPASS_PASSWORD
Expected output:
vault created: alias=local path=.loom/keepass/LOCAL.kdbx
Without --vault-path, the CLI derives an alias from the Git remote origin path. Keeping the explicit alias here makes each command use the same vault.
B2. Configure runtime access
Vault creation writes the .kdbx file; it does not register a runtime mapping. Export this mapping before adding items or running the workflow:
export LOOM_KEEPASS_DB_LOCAL_PATH="$PWD/.loom/keepass/LOCAL.kdbx"
export LOOM_KEEPASS_DB_LOCAL_PASSWORD_ENV="KEEPASS_PASSWORD"
The LOCAL key matches the local alias. KEEPASS_PASSWORD must still hold the password used when creating the vault.
Store these exports in a local .env file (excluded from version control) and source it at the start of each session.
B3. Add a secret and reference it
export DEPLOY_TOKEN_VALUE="tok_example_abc123"
loom secrets keepass item create \
--vault-path local \
--item-path services/deploy \
--field token \
--value-from-env DEPLOY_TOKEN_VALUE
Save this complete workflow as .loom/workflow.yml:
version: v1
stages: [ci]
check-secret:
stage: ci
target: linux
secrets:
DEPLOY_TOKEN:
ref: keepass://local#services/deploy:token
script:
- test -r "$DEPLOY_TOKEN" && test -s "$DEPLOY_TOKEN"
- echo "Secret file is readable and non-empty"
B4. Validate and run
loom check validates structure; loom run resolves the reference. The run should print Secret file is readable and non-empty and exit with status 0.
loom check
loom run --local --workflow .loom/workflow.yml
Option C: Environment passthrough (env://)
env:// reads a value from the environment of the Loom process. Save this workflow as .loom/workflow.yml:
version: v1
stages: [ci]
check-secret:
stage: ci
target: linux
secrets:
DEPLOY_TOKEN:
ref: env://DEPLOY_TOKEN
script:
- test -r "$DEPLOY_TOKEN" && test -s "$DEPLOY_TOKEN"
- echo "Secret file is readable and non-empty"
Before running:
export DEPLOY_TOKEN="tok_example_abc123"
loom run --local --workflow .loom/workflow.yml
The job checks that the injected file is readable and non-empty without printing the value. Use env:// when your shell or CI runner already supplies the secret.
Injection modes
By default, $SECRET_NAME holds a path to a temp file containing the value. Some tools expect the raw value in the env var instead.
| Mode | file setting | Job sees | When to use |
|---|---|---|---|
| File injection (default) | true | Path to a 0600 temp file | Most cases — lower leakage risk |
| Direct injection | false | Raw secret value | Only when a tool cannot read from a file |
Example: direct injection for npm publish
secrets:
NPM_TOKEN:
ref: env://NPM_TOKEN
file: false
Tradeoff: direct injection is more exposed to shell tracing (set -x). If CI_DEBUG_TRACE=true is set and any secret uses file: false, Loom hard-fails with SECRETS_UNSAFE_DEBUG_TRACE to prevent accidental exposure.
Optional secrets
Mark a secret required: false when the job should succeed even if the secret is unavailable:
secrets:
SLACK_WEBHOOK:
ref: op://Engineering/notifications/webhook
required: false
Your script should handle the missing-variable case:
if [ -n "$SLACK_WEBHOOK" ]; then
curl -X POST "$(cat "$SLACK_WEBHOOK")" -d '{"text":"Deploy complete"}'
fi
Validation and runtime errors
| Rule | What happens on violation |
|---|---|
A key cannot be in both variables and secrets for the same job | Schema validation error (fail-fast) |
default.secrets is not allowed | Schema validation error |
Secret names must match ^[A-Z_][A-Z0-9_]*$ | Schema validation error |
ref must use a supported URI scheme | SECRETS_REF_INVALID |
| Missing required secret | SECRETS_REQUIRED_MISSING — job fails |
CI_DEBUG_TRACE=true with file: false secrets | SECRETS_UNSAFE_DEBUG_TRACE |
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
SECRETS_PROVIDER_UNAVAILABLE | Auth config missing or invalid for the provider | Check OP_SERVICE_ACCOUNT_TOKEN or LOOM_KEEPASS_DB_* env vars |
SECRETS_REQUIRED_MISSING | Env var not exported (env://), or vault entry missing | Export the variable or verify the vault item exists |
SECRETS_REF_NOT_FOUND | Entry path or field does not match vault contents | Verify ref against loom secrets op item list or keepass item list |
SECRETS_REF_INVALID | Typo in URI scheme or malformed ref | Verify scheme is env://, keepass://, or op:// |
| Script fails reading secret value | file: true (default) but script expects a direct value | Use cat "$VAR_NAME" in scripts, or set file: false |
Schema error: key in both variables and secrets | Same name in both blocks for one job | Remove the key from variables |
SECRETS_UNSAFE_DEBUG_TRACE | CI_DEBUG_TRACE=true with file: false secrets | Disable debug trace or switch to file: true |
What to read next
- Secrets overview — complete reference for the secrets model.
- Secrets security — threat model, controls, and incident response.
- 1Password CLI reference — vault and item management commands.
- KeePass CLI reference — database, entry, and field commands.
- Secrets error codes — full error code reference.