Skip to main content

Secrets

Secrets deliver sensitive values — passwords, tokens, private keys — to jobs at runtime without exposing those values in workflow YAML, compiled artifacts, receipts, or logs.

This page explains the conceptual model. For configuration details, see the Secrets reference. For security controls, see Secrets security.

The core idea: references, not values

A secret entry maps an environment variable name to a provider URI:

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

The workflow never contains the password — only the reference. Loom resolves the reference through the named provider at execution time and injects the value into the job environment. After the job finishes, the resolved value is discarded.

This separation means you can commit workflow files, share receipts, and review logs without risk of credential exposure.

How secrets differ from variables

Variables hold non-sensitive configuration. Their values are visible in compiled workflows, receipts, and logs. Secrets hold sensitive values by reference only.

AspectVariablesSecrets
Stored in YAMLPlain valueProvider reference URI (ref)
Visible in artifactsYes (compile output, receipts, logs)Never — redacted at all output boundaries
Scopedefault, job, or workflow levelJob-only (default.secrets is invalid)
Default injectionDirect environment valueTemp file path (file: true)
Key collisionAllowed across scopes (merges)Same key in both variables and secrets is rejected

Use secrets when the value would cause damage if leaked — credentials, API keys, signing keys, connection strings. Use variables for everything else.

Provider-backed resolution

Each ref URI starts with a scheme that identifies the provider:

SchemeProviderSource
env://Environment passthroughHost process environment variable
keepass://KeePassLocal encrypted .kdbx database
op://1PasswordVault via Go SDK (service account token)

Providers are pluggable. All providers follow the same contract: resolve only the secrets declared on the current job, return values only to the in-memory injection flow, and fail closed when credentials are missing or invalid.

See Secrets providers for provider-specific setup.

File-first injection

By default (file: true), Loom writes the resolved secret to a temporary file with 0600 permissions and sets the environment variable to the file path. Scripts read the value with cat "$VAR_NAME".

This default reduces accidental leakage from shell tracing (set -x), ps output, and command-line interpolation. Set file: false only when a tool requires a direct environment value.

For Docker jobs, file-injected secrets are bind-mounted read-only into the container and rewritten to container-local paths.

Automatic redaction

Resolved secret values are redacted before Loom writes to any output boundary:

  • Console and event streams
  • Receipt stdout/stderr
  • Error fields containing command output
  • Provider lifecycle messages

Redacted values appear as [REDACTED:SECRET_<NAME>].

Redaction is a safety net, not a substitute for discipline. Avoid printing secret-bearing environment variables in scripts even though redaction exists.

Job-scoped declaration

Secrets are valid only on individual job blocks. default.secrets is intentionally invalid — this prevents broad fan-out of sensitive material across all jobs in a workflow.

A secret name cannot appear in both variables and secrets for the same job. The schema validator rejects this overlap to eliminate ambiguity about whether a value is sensitive.

Secret spec fields

FieldRequiredDefaultDescription
refYesProvider URI (env://..., keepass://..., op://...)
fileNotruetrue: inject as temp-file path. false: inject raw value in env var.
requiredNotruetrue: fail job if unresolved. false: silently omit and continue.

Key safety invariants

  • Workflow YAML contains references only — never credential material.
  • Vault credentials (master passwords, keyfiles, service account tokens) are supplied through runtime environment configuration, not through workflows.
  • CI_DEBUG_TRACE=true combined with any file: false secret hard-fails with SECRETS_UNSAFE_DEBUG_TRACE.
  • Secret bytes never appear in Graph IR, compiled workflows, receipts, or log artifacts.

Next steps