Skip to main content

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

  1. Configure a 1Password service account token.
  2. Create or verify a secret in your 1Password vault.
  3. Reference the secret in a workflow.
  4. Run the workflow and check the injected file without printing its contents.

Prerequisites

  • Loom CLI available (run loom version to confirm).
  • A working Loom workspace (loom check succeeds).
  • A shell session where you can export environment variables.

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:

  • ref format: op://<vault>/<item-path>/<field>.
  • file defaults to true: $DEPLOY_TOKEN contains a path to a temp file. Read the value with cat "$DEPLOY_TOKEN".
  • required defaults to true: if the secret cannot be resolved, the job fails before script execution.
  • Reference in YAML: the secrets entry contains an op:// 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-empty and 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.

tip

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.

Modefile settingJob seesWhen to use
File injection (default)truePath to a 0600 temp fileMost cases — lower leakage risk
Direct injectionfalseRaw secret valueOnly 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

RuleWhat happens on violation
A key cannot be in both variables and secrets for the same jobSchema validation error (fail-fast)
default.secrets is not allowedSchema validation error
Secret names must match ^[A-Z_][A-Z0-9_]*$Schema validation error
ref must use a supported URI schemeSECRETS_REF_INVALID
Missing required secretSECRETS_REQUIRED_MISSING — job fails
CI_DEBUG_TRACE=true with file: false secretsSECRETS_UNSAFE_DEBUG_TRACE

Troubleshooting

SymptomLikely causeFix
SECRETS_PROVIDER_UNAVAILABLEAuth config missing or invalid for the providerCheck OP_SERVICE_ACCOUNT_TOKEN or LOOM_KEEPASS_DB_* env vars
SECRETS_REQUIRED_MISSINGEnv var not exported (env://), or vault entry missingExport the variable or verify the vault item exists
SECRETS_REF_NOT_FOUNDEntry path or field does not match vault contentsVerify ref against loom secrets op item list or keepass item list
SECRETS_REF_INVALIDTypo in URI scheme or malformed refVerify scheme is env://, keepass://, or op://
Script fails reading secret valuefile: true (default) but script expects a direct valueUse cat "$VAR_NAME" in scripts, or set file: false
Schema error: key in both variables and secretsSame name in both blocks for one jobRemove the key from variables
SECRETS_UNSAFE_DEBUG_TRACECI_DEBUG_TRACE=true with file: false secretsDisable debug trace or switch to file: true