STD-006: Secrets Handling

Rules governing credential management, secret access, and secure storage across all domus repositories and workstations. Verify presence and accessibility — never content.

Principles

  1. Secrets are inviolable. Never decrypt .age files programmatically, read ~/.secrets/, execute age -d in automation, or surface any credential value. Document paths; never reveal values.

  2. Presence, not content. Validation checks verify that secrets EXIST and are ACCESSIBLE. They MUST NOT print, log, or display secret values.

  3. Each machine owns its keys. Private keys MUST NOT be shared across machines. Each workstation gets its own keypair, its own Vault cert, its own gopass clone.

  4. Encrypted at rest. Secrets not actively in use MUST be encrypted. Use gocryptfs for credential vaults, age for file encryption, LUKS for disk encryption.

Requirements

  1. Credentials, tokens, API keys, and secret values MUST NOT appear in any .adoc document, commit message, or log output. Redact with <REDACTED>.

  2. gopass store paths and Vault policy names are acceptable to document. Secret values stored within them are not.

  3. Private keys (~/.ssh/id_*, ~/.gnupg/, ~/.age/identities) MUST NOT be committed to any git repository.

  4. Each workstation MUST have its own SSH keypair, GPG key, and EAP-TLS certificate. Never copy private keys between machines.

  5. gocryptfs vaults MUST be unmounted when not in active use. Sensitive tooling (Claude Code config, gh CLI tokens) SHOULD be symlinked from the mounted vault.

  6. After rsync’ing ~/.gnupg/ to a new machine, GPG lock files MUST be cleared and gpg-agent restarted before use. Lock files contain source machine PIDs.

  7. After gopass bootstrap on a new machine, gopass config MUST be verified to show the correct root store path.

  8. SSH config containing hostnames and connection details MUST be age-encrypted before committing: age -e -R ~/.age/recipients/self.txt.

d001 Sensitive Data Workflow

Work projects store sensitive materials (config snapshots, meeting notes, certificates, vendor configs) under data/d001/projects/<slug>/. Plaintext .adoc files are gitignored — only age-encrypted .age files are tracked.

Standard: d001 open / d001 close

The d001 shell function manages bulk encrypt/decrypt. It fuzzy-matches project directories via fzf.

d001 open okta-to-entra       # Decrypt all .adoc.age in matched project
# Edit partials...
d001 close okta-to-entra      # Encrypt partials/*.adoc, delete plaintext, stage .age

d001 close scans partials/ only. Non-adoc files in certs/, config-snapshots/, scripts/ require one-off handling.

Single-File Edit

d001 open-file okta-to-entra  # Decrypt → $EDITOR → re-encrypt on quit

One-Off (non-adoc files)

For .pem, .xml, .txt files outside partials/:

decrypt-file path/to/file.age         # Decrypt
# Edit...
rm path/to/file.age                   # Delete stale .age FIRST
encrypt-file path/to/file             # Re-encrypt

Always delete the stale .age before re-encrypting. The old .age silently overrides your edits if not removed first. This was the root cause of INC-2026-04-12.

Pre-Push Audit

Run before every push to verify no plaintext leaked:

# Check for unencrypted sensitive files in d001
find data/d001 -name '*.adoc' -not -name 'README.adoc' -not -name '*.age' -type f

# Should return zero results. Any match = forgot to d001 close.

Project Directory Standard

See STD-001 § "Sensitive Data Directory" for the required data/d001/projects/<slug>/ structure (README, partials, certs, config-snapshots, scripts, sql, output).

Compliance

Check Method Pass Criterion

No secrets in docs

grep -riE 'password|token|secret|apikey' pages/ | grep -v REDACTED | grep -v gopass | grep -v pattern

Zero real credentials

No private keys in git

git ls-files | grep -E 'id_rsa|id_ed25519[^.]' | grep -v '.pub|.age'

Zero private key files tracked

SSH config encrypted

git ls-files | grep 'ssh.*config$' should be empty; ssh/.ssh/config.age tracked

Only .age version in git

gocryptfs vault mountable

gcvault mount credentials && ls ~/.credentials/

Mount succeeds, contents accessible

gopass accessible

gopass ls | head -3

Store listed without error

Exceptions

None. All requirements apply unconditionally.