Skip to main content

KeePass in workflows

Wire keepass:// secrets into Loom workflow jobs. This page covers declaration syntax, injection modes, optional secrets, multi-secret patterns, Docker job behavior, and validation.

Declaring a KeePass secret

Add a secrets block to any job. Each key becomes an environment variable available to the job's script.

version: v1
stages: [ci]

deploy:
stage: ci
target: linux
secrets:
DATABASE_PASSWORD:
ref: keepass://local/main#services/loom/deploy:password
script:
- ./scripts/deploy.sh

Spec fields

FieldRequiredDefaultDescription
refYesKeePass URI in the format keepass://<alias>#<entry-path>:<field>
fileNotrueWhen true, Loom writes the value to a 0600 temp file and sets the env var to the file path. When false, the raw value is placed directly in the env var.
requiredNotrueWhen true, the job fails if the secret cannot be resolved. When false, Loom skips injection and the job continues. An inherited Host variable with the same name can remain.

Scoping rules

  • Secrets are job-scoped only. Declaring secrets under default is invalid and rejected by the schema validator.
  • A key cannot appear in both variables and secrets on the same job — the validator rejects the overlap.

Injection modes

secrets:
DATABASE_PASSWORD:
ref: keepass://local/main#services/loom/deploy:password
file: true

The job environment variable contains a file path. Read the value in scripts:

DB_PASS=$(cat "$DATABASE_PASSWORD")
curl -u "admin:${DB_PASS}" https://db.example.com/health

File injection is the default (file: true). Prefer it because:

  • The job variable contains a path, reducing exposure through ps or /proc/*/environ. This depends on the file retaining its 0600 permissions and being removed when Loom finishes using it.
  • Shell tracing (set -x) prints the file path while the script does not read or expand the file contents in a traced command.
  • Tools that accept credential files (e.g. --password-file) work directly with the path.

A process that reads the file can still print or publish the value. File injection does not make logs, receipts, or declared artifacts safe to share; inspect and sanitize them first.

Direct injection (use only when required)

secrets:
API_TOKEN:
ref: keepass://local/main#services/loom/deploy:token
file: false

The job environment variable contains the raw secret value. Use only when a tool requires an environment variable value and cannot read from a file.

echo "machine api.example.com password ${API_TOKEN}" >> ~/.netrc
caution

CI_DEBUG_TRACE=true combined with any file: false secret triggers a hard failure (SECRETS_UNSAFE_DEBUG_TRACE) before job execution. Either disable debug trace or switch the secret to file: true.

Optional secrets

Mark a secret required: false when the workflow should continue even if the secret is unavailable:

secrets:
OPTIONAL_WEBHOOK:
ref: keepass://local/main#services/loom/notifications:webhook
required: false

If resolution fails, Loom skips injection for this secret. It does not clear an existing variable with the same name from the Host environment. Choose an output name that is distinct from ambient variables, and ensure that output name is not already set when starting Loom. A presence check alone cannot prove that Loom resolved the secret.

For a file-injected optional value, check that the path is readable before using it:

if [ -n "${OPTIONAL_WEBHOOK:-}" ] && [ -r "$OPTIONAL_WEBHOOK" ]; then
curl -X POST "$(cat "$OPTIONAL_WEBHOOK")" -d '{"status": "deployed"}'
fi

Multiple secrets in a single job

Jobs can declare any number of secrets. Each resolves independently.

deploy:
stage: ci
target: linux
secrets:
DATABASE_PASSWORD:
ref: keepass://local/main#services/loom/deploy:password
API_TOKEN:
ref: keepass://local/main#services/loom/deploy:token
file: false
DEPLOY_KEY:
ref: keepass://team#infra/deploy:ssh-key
script:
- ./scripts/deploy.sh

Secrets can reference different aliases (local/main, team) in the same job. Each alias resolves through its own set of LOOM_KEEPASS_DB_<KEY>_* environment variables.

Docker jobs

For Docker jobs (jobs with an image field), file-injected secrets are automatically bind-mounted read-only into the container. Loom rewrites the environment variable to the container-local path.

deploy:
stage: ci
target: linux
image: alpine:3.19
secrets:
DATABASE_PASSWORD:
ref: keepass://local/main#services/loom/deploy:password
script:
- DB_PASS=$(cat "$DATABASE_PASSWORD")
- echo "Connected with credentials"

Scripts inside the container use the same cat "$VAR_NAME" pattern — no path translation needed.

URI authoring guidelines

GuidelineExampleRationale
Use environment-specific aliaseslocal, team, prodMakes workflow YAML portable across environments
Use predictable entry pathsservices/loom/<component>Easier to audit and discover with item list
Use explicit field namespassword, token, usernameSelf-documenting; avoids reliance on default fields
Specify enough path segmentsservices/loom/deploy not just deployPrevents ambiguous matches (see entry path matching)

Validation

Validate workflow structure before running:

loom check

loom check validates:

  • Each ref is a non-empty scalar. Provider URI syntax and credentials are checked at runtime.
  • No overlap between secrets and variables keys.
  • No secrets declared under default.

To run the workflow after validation:

loom run --local --workflow .loom/workflow.yml

Troubleshooting

Error codeCauseFix
SECRETS_PROVIDER_UNAVAILABLEAlias mapping or credential environment is misconfiguredVerify LOOM_KEEPASS_DB_<KEY>_* env vars — see Install and setup
SECRETS_REF_NOT_FOUNDEntry path or field does not exist in the .kdbx fileRun loom secrets keepass item list to check available entries
SECRETS_REF_INVALIDMalformed URI or ambiguous entry pathCheck URI format against URI format
SECRETS_REQUIRED_MISSINGA required: true secret could not be resolvedFix the provider config or set required: false for optional secrets
SECRETS_UNSAFE_DEBUG_TRACECI_DEBUG_TRACE=true with a file: false secretRemove debug trace or switch the secret to file: true