Worker secrets handling — the contract

Class: security guardrail · Audience: any worker/agent session that touches a credential · Authoritative internals: worker-tmp-secret-hygiene.md

This is the short, worker-facing contract. For the incident, the full guardrail set, and the runtime/CI internals, read worker-tmp-secret-hygiene.md.

The rules

  1. Never write a secret value to a file you then read back. No /tmp/wenv.sh, no sops -d … > /tmp/x.env; source /tmp/x.env. That is the #7724 leak class and the #5078 execute-the-env class. The lint-worker-tmp-secrets CI check blocks it from reaching the repo; the hourly TmpSecretScratchExposed alert catches a runtime relapse.

  2. Extract one key to stdout, use it, unset it — via scripts/lib/extract_env_key.sh, which writes the value to stdout only (never a file) and matches ^KEY= exactly (collision-proof, #3494):

    source /opt/p24-infra/scripts/lib/extract_env_key.sh
    TOKEN=$(extract_env_key CF_API_TOKEN "$ENV_FILE"); some-cmd --token "$TOKEN"; unset TOKEN
     
    # straight from a SOPS stream, nothing lands on disk:
    TOKEN=$(sops -d --input-type dotenv --output-type dotenv secrets/x.env.sops \
              | extract_env_key_stdin CF_API_TOKEN) || exit 1
  3. If you genuinely need a temp file, use the private TMPDIR and self-clean. Workers launched by scripts/spawn-worker.sh run with an owner-only 0700 per-worker TMPDIR already set (#7813), so mktemp defaults into an un-listable private dir. Still make it owner-only and self-cleaning:

    TMP=$(mktemp); chmod 600 "$TMP"; trap 'rm -f "$TMP"' EXIT

    umask 077 is set before launch, so new files are 0600 even if you forget — but do not rely on it as the only line of defence.

  4. Never display a secret value in chat, a GitHub comment, a commit, a log, or a subagent prompt. Reference the key NAME only. This is the global CLAUDE.md §Secrets ABSOLUTE rule.

The per-worker TMPDIR contract (#7813)

spawn-worker.sh exports TMPDIR=$(mktemp -d …/p24-worker-<issue>-XXXXXX) at 0700 and reaps it when the worker exits. You inherit it automatically — you do not create or remove it. Do not hard-code /tmp/... paths for scratch that could hold a secret: let mktemp/tempfile honour TMPDIR so the scratch stays inside the private subtree. Full mechanics (why cleanup is colocated with the $WORKDIR teardown rather than a parent trap … EXIT): worker-tmp-secret-hygiene.md §Guardrail 4.