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, recorded state errors, output-bearing errors, and provider lifecycle messages with tokens such as [REDACTED:SECRET_DEPLOY_TOKEN].
  • Runtime logs are under .loom/.runtime/runs/<run>/. 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