← Security and risk guides

How to spec secrets handling

How-To Security and risk Intermediate 1161008HOWTO-1161008

HOWTO-1161008Security and riskIntermediate

This guide shows you how to spec secrets handling with SpecDD in a spec-driven development workflow.

Secrets include credentials, API keys, signing keys, tokens, certificates, webhook secrets, encryption keys, and anything else your project treats as secret material. SpecDD helps by making the handling contract explicit near the code that loads, passes, stores, rotates, or uses those secrets.

Short answer

Put secrets handling rules in the spec that owns the secret-loading or secret-using behavior. Use Must for required loading, storage, passing, rotation, and failure behavior. Use Must not for logging, committing, exposing, or persisting raw secret values. Use Forbids for blocked direct access to secret stores, files, paths, libraries, or provider SDKs. Use Done when for tests, secret-scan checks, log checks, and review evidence.

When to use this guide

Use this guide when code:

Steps

1. Name the secret and owner

Do not put actual secret values in specs. Name the secret by purpose.

Spec: Webhook Signature Verification

Purpose:
  Verify inbound payment webhooks using the configured webhook signing secret.

Owns:
  ./webhook-signature-verification.ts
  ./webhook-signature-verification.test.ts

The owner may be a secret-loading adapter, a verifier, a provider adapter, or a runtime configuration module.

2. Specify how the secret is loaded

Use Must to describe the approved source and behavior.

Must:
  Load the webhook signing secret through the project secret provider.
  Reject webhook verification when the signing secret is unavailable.
  Use test-only fake secret values in tests.

Avoid specifying actual environment variable values, keys, tokens, or credentials.

3. Block direct secret access

Use Forbids for blocked paths, stores, libraries, tools, or access patterns.

Forbids:
  Reading webhook secrets from checked-in files.
  Direct secret manager access outside ../secrets/*
  Direct provider SDK access outside ../payment-provider/*

This prevents agents from adding a shortcut when a local test needs a secret-like value.

4. Write logging and commit rules with Must not

Use Must not for forbidden behavior.

Must not:
  Log raw webhook signing secrets.
  Include secrets in test snapshots, fixtures, error messages, or documentation.
  Continue verification after the signing secret is missing.
  Store secret values in application database records.

Failure paths matter. Many leaks happen in diagnostics, exception messages, snapshots, or generated files rather than the main success path.

5. Specify passing and storage boundaries

If a secret must be passed to another unit, name the approved boundary.

Depends on:
  ../secrets/secret-provider.sdd
  ../payment-provider/payment-provider-port.sdd

Must:
  Pass the webhook signing secret only to the signature verifier.
  Keep raw secret values out of audit event payloads.

If a secret must be stored, specify the approved storage behavior. If it must not be stored, say that explicitly.

6. Handle rotation, reload, and missing secret behavior

Only write rotation rules your project actually supports or intends to support.

Handles:
  missing webhook signing secret
  invalid webhook signature
  rotated webhook signing secret

Must:
  Use the currently configured signing secret for new verification attempts.
  Return verification failure when the configured secret is unavailable.

If rotation is owned elsewhere, reference that owner instead of duplicating the rule.

7. Add evidence in Done when

Done when:
  Missing signing secret returns verification failure.
  Invalid signature behavior is covered by a test using fake secret material.
  Logs for verification failure do not include raw secret values.
  Secret scan passes for fixtures, snapshots, and generated files.
  The verifier does not read checked-in secret files.

Use project scanning tools when available. Use targeted review when logs or generated artifacts need inspection.

Example

Spec: Email Provider Credentials

Purpose:
  Provide email provider credentials to the email adapter without exposing raw secret values elsewhere.

Owns:
  ./email-provider-credentials.ts
  ./email-provider-credentials.test.ts

Can modify:
  ./email-provider-credentials.ts
  ./email-provider-credentials.test.ts

Depends on:
  ../secrets/secret-provider.sdd

Must:
  Load provider credentials through the project secret provider.
  Return a missing-credentials error when required credentials are unavailable.
  Use fake credential values in tests.

Must not:
  Log raw provider credentials.
  Store provider credentials in application database records.
  Include provider credentials in snapshots, fixtures, or documentation.

Forbids:
  Reading provider credentials from checked-in files.
  Direct secret manager access outside ../secrets/*

Raises:
  EmailCredentialsMissing

Done when:
  Missing credential behavior is covered by a test.
  Test fixtures use fake credential values only.
  Secret scan passes for changed files.
  Failure logs do not include raw credential values.

Agent-specific guardrails

For secret-adjacent work, add direct rules that prevent common agent shortcuts:

Must not:
  Create realistic-looking production credentials for examples or tests.
  Copy secret values from logs into fixtures.
  Add debugging output that prints raw secret values.

These are useful when agents generate tests, snapshots, seed data, or documentation.

Common mistakes

How to verify the result

Secrets handling is well specified when:

← Security and risk guides