Every committed value must work for any person who clones the repo right now. Real values come from runtime injection, never the filesystem.Golden law 1 forbids real secrets in repo content. The table below is the canonical set of shape stand-ins to use instead, plus the injection paths that supply the real values at runtime.
Scrubbed values
| Type | Scrubbed value | Examples |
|---|---|---|
| IPv4 address | 192.168.0.* | Last octet can be accurate when it has no security meaning |
| IPv6 address | 2001:db8::* | RFC 3849 documentation prefix |
| External domain | example.com | Public services and APIs |
| Internal domain | example.local | LAN hostnames and services |
| API endpoint | https://api.example.com:8006/api2/json | Scrubbed domain pattern |
| Username | terraform, admin, user | Generic role-based names |
| Tokens and keys | your-token-here or <token> | Clearly marked placeholder |
${USER}, ${REPO_ROOT}, ${MAINTAINER_EMAIL}, ${NAS_HOST}, <redacted> for shape stand-ins where the placeholder needs to look like a variable.
Enforcement: the secret-scan gate
The publicdocs repo runs a fail-closed gitleaks check (.github/workflows/secret-scan.yml) on every PR and push to main, via gitleaks-action. A finding fails the job, so a sensitive value cannot merge.
So the list of what counts as sensitive stays private, the rule set is never committed. It lives only as the dryvist org Actions secret GITLEAKS_PRIVATE_CONFIG, injected into CI at run time. The scan runs with --redact, and the public-output features (PR comments, SARIF artifact, job summary) are disabled, so neither the patterns nor any match ever surface publicly. useDefault covers generic credentials; org-specific rules cover the values that must never appear here.
The same gate runs on the draft PRs opened by the Docs Sync routine, so machine-authored content is held to the same bar as anything authored by hand.
Portable path references
Never commit absolute user paths (/Users/{username}/*, /home/{username}/*, $HOME/*, ~/*).
| Bad (user-specific) | Good (portable) | Use case |
|---|---|---|
/Users/john/.local/bin/tool | tool | PATH lookup |
entry: /Users/john/.local/bin/ansible-lint | entry: ansible-lint | Pre-commit hooks |
~/.ssh/id_rsa | # /path/to/your/ssh/key | Templates |
$HOME/git/nix-config/main | ${NIX_CONFIG_PATH}/main | Env var for external paths |
/home/user/project/file.txt | ./file.txt | Relative paths within a project |
Variable indirection
Always reference sensitive values through a variable:Runtime secret injection
Real values come from one of these stores — never the repo:- macOS Keychain (
ai-secrets,automation,elevate-accesskeychains) — for AI / Claude projects and local CLI use, never in files or env vars - Doppler —
doppler run --name-transformer tf-varfor infrastructure - SOPS + age — encrypted secrets at rest in git
- Environment variables — CI/CD secrets or local
.envfiles (never committed) - AWS Secrets Manager / Parameter Store — for AWS deployments
- SSH agent — agent forwarding only; never commit keys
macOS Keychain reuse
Every keychain read triggers a password approval prompt. Fetch the secret once into a shell variable, then inject the variable into every command that needs it. Never inline$(security find-generic-password ...) in each command.
security find-*-password invocation.
What this connects to
Golden laws
The 15 non-negotiables — this page is the implementation of law 1.
Security overview
Which tool to reach for, for which kind of secret.
macOS Keychain
Tiered keychains, biometric unlock, the elevate-access boundary.
SOPS
Encrypted-at-rest config that can live in the repo without violating law 1.