Skip to main content

Secrets security

This page describes the security model for Loom secrets: what it protects, how protection works, and what you are responsible for.

Read this page if you are evaluating Loom for production use, configuring secrets providers, or responding to a suspected credential leak.

Security goals

Loom's secrets system is designed around four objectives:

  1. References-only static definitions. Workflow YAML, Graph IR, and compiled workflow output contain provider references rather than resolved values.
  2. Job-scoped injection. Loom supplies resolved values to the job through temporary files or environment variables. File injection reduces exposure through the job environment, but scripts can still copy or publish the contents.
  3. Fail closed for required secrets. Missing provider auth or invalid references prevent required secrets from resolving; unsafe debug-trace settings stop execution. An unresolved required: false secret can be omitted without stopping the job.
  4. Exact-match redaction. Loom replaces declared secret values in the text outputs listed below. Operators still inspect and sanitize evidence before sharing it.

Threat model

Protected assets

AssetExamples
Secret valuesPasswords, API tokens, signing keys, connection strings
Vault credentialsKeePass master passwords, keyfiles, 1Password service account tokens
Vault topologyEntry paths, vault names, and organizational structure

Threat scenarios

ThreatAttack pathLoom control
Credential in CI logsecho, set -x, or command-line interpolation prints a secretFile-first injection reduces command-line exposure; exact-value redaction covers verified textual sinks
Credential in static definitionsResolved value is placed in workflow YAML or compiled Graph IRSecrets use reference-only storage; schema rejects overlapping variables and secrets keys
Credential in declared artifactsA job writes a resolved or transformed secret into an artifact fileArtifact contents are not scanned; workflows and operators must prevent or sanitize that output
Auth material in sourceMaster password or service account token committed in workflow YAMLProvider architecture requires runtime-only credential configuration; no workflow field accepts vault auth material
Provider fallback driftA missing provider silently falls back to a weaker credential sourceProviders fail closed with SECRETS_PROVIDER_UNAVAILABLE; no implicit fallback chain exists
Debug trace leaks direct valuesCI_DEBUG_TRACE=true enables shell tracing that can expose file: false secretsHard failure (SECRETS_UNSAFE_DEBUG_TRACE) before job execution when this combination is detected

Core controls

1. References-only workflows

Workflow YAML stores ref URIs, not values. This keeps resolved values out of source control and loom compile output. The separation is enforced at the schema level — there is no secret field that accepts an inline value.

2. Job-scoped declaration

Secrets are allowed only at the job level. default.secrets is rejected by the schema validator to prevent a single declaration from fanning out sensitive material to every job in the workflow.

3. File-first injection

file: true is the default. Loom writes the value to a temporary file with 0600 permissions and sets the job variable to that file path. Tools that accept the path directly can avoid putting secret bytes in the following outputs. This depends on the file keeping its permissions, being removed after use, and consumers not reading its contents into those outputs:

  • Shell trace output (set -x)
  • Process listings (ps aux)
  • Command-line argument logs
  • Docker inspect output

For Docker jobs, file-injected secrets are bind-mounted read-only into the container.

File injection reduces trace and argument exposure; it does not make logs, receipts, or declared artifacts generally shareable. A script or tool that reads and emits the file contents can still disclose the value.

4. Redaction at output boundaries

Loom replaces exact matches for declared secret values in console/event output, receipt stdout/stderr, output-bearing errors, and provider lifecycle messages. It uses tokens such as:

[REDACTED:SECRET_DATABASE_PASSWORD]

Exact matches in host and Docker stdout/stderr can span output frames, including newline and 8 KiB boundaries. Loom processes stdout and stderr separately.

Stream-matching details

Loom redacts continuous byte streams before splitting them into frames. For a longest configured raw, trimmed, or quoted secret variant of L bytes, each stream retains at most L - 1 unresolved bytes, including when L exceeds 8 KiB. It releases the tail at the end of the stream. Matching proceeds from left to right and chooses the longest match at each byte offset.

Limitation: Redaction operates on exact byte matching. If a script transforms a secret value (e.g., base64-encodes it) before printing, the transformed output will not be redacted. Treat redaction as defense-in-depth, not a guarantee.

Loom does not inspect raw declared artifact contents. Inspect and sanitize console/event output, receipts, errors, provider messages, and declared artifacts before sharing them.

5. Fail-closed provider behavior

Missing or invalid provider authentication or configuration produces a SECRETS_* resolution error. A required secret prevents the job from continuing. A secret marked required: false can be omitted; Loom does not substitute another provider, implicit credentials, or an empty value.

Provider-specific security notes

env://

  • Risk profile: The host process environment may contain values visible to unrelated tooling or inherited by child processes.
  • Guidance: Use env:// only for values already secured at the runner or session boundary. Avoid long-lived shell sessions with broadly exported secrets.

keepass://

  • Risk profile: Security depends on local filesystem hygiene and unlock credential handling. A compromised keyfile or weak master password undermines all entries.
  • Guidance:
    • Map aliases through runtime config environment variables (LOOM_KEEPASS_DB_<ALIAS>_PATH).
    • Use credential indirection (..._PASSWORD_ENV, ..._KEYFILE_ENV) — never place master passwords in workflow YAML.
    • Restrict filesystem permissions on .kdbx files and keyfiles.

op://

  • Risk profile: Security depends on the 1Password service account token scope and rotation.
  • Guidance:
    • Use least-privilege token scoping — grant access only to the vaults and items required.
    • Rotate service account tokens on a defined schedule.
    • Keep OP_SERVICE_ACCOUNT_TOKEN out of workflow YAML and variables blocks.

Operational guardrails

GuidelineRationale
Prefer file: true for all secretsReduces leakage surface from shell tracing, ps, and interpolation
Disable CI_DEBUG_TRACE on secrets-heavy jobsPrevents hard failure and avoids accidental exposure of file: false values
Avoid printing secret env vars in scriptsRedaction is defense-in-depth, not foolproof
Inspect and sanitize evidence before sharingTransformed values and raw artifact contents are outside exact-match redaction
Rotate provider credentials independentlyWorkflow updates and credential rotation should be decoupled
Validate workflows before runningloom check catches schema violations and scope conflicts; provider URI syntax, configuration, and resolution are checked at runtime

Debug-trace safety

When CI_DEBUG_TRACE=true and any secret uses file: false, Loom hard-fails with SECRETS_UNSAFE_DEBUG_TRACE before execution begins. This prevents shell traces from printing direct-injected secret values.

To debug a job that uses direct-injected secrets, either switch the secrets to file: true or remove CI_DEBUG_TRACE.

Incident response

If you suspect a secret has leaked:

  1. Revoke immediately. Rotate the exposed credential at the source provider (1Password, KeePass, environment).
  2. Scope the exposure. Identify the run ID and job, then inspect logs, receipts, errors, provider messages, and declared artifacts. A redaction token confirms an exact match was removed; its absence does not prove non-exposure.
  3. Assess downstream impact. Determine whether the leaked value was consumed by external systems or persisted to external sinks.
  4. Patch the source. Fix the workflow or script pattern that caused the leak — typically switching from file: false to file: true or removing an echo statement.
  5. Verify the fix. Re-run the corrected workflow with a disposable test credential. Inspect its console/event output, receipt stdout/stderr, output-bearing errors, provider messages, and declared artifacts for the value and any transformations the script creates. Confirm the leaking command or file output is gone before restoring normal operation. A missing redaction token alone is not proof that the output is safe.

Security review checklist (for contributors)

When adding a new secrets provider, verify:

  • Provider contract includes explicit auth-source policy and allowlisting.
  • Resolver fails closed on unavailable or invalid auth.
  • Static planner/compile outputs store references only. Exact secret values are replaced in receipt/event text and the other outputs listed under Redaction at output boundaries.
  • Redaction test coverage includes quoted and newline variants.
  • Error codes are deterministic and do not contain sensitive content.
  • Documentation covers setup, runtime boundaries, and risk caveats.