secrets: project-scoped secrets go in /secrets/<project>/
Operator convention, 2026-08-20. If a secret belongs to one project it lives in that project's directory rather than in files/ with the project name baked into the filename: /secrets/lichen/test-pds.env, not /secrets/files/lichen-test-pds.env. files/ is reserved for things genuinely shared across projects. A flat directory forces every name to carry its own scope, which nobody does consistently, and then 'what does this project hold' and 'what do I revoke if it is compromised' both need a grep. A directory answers both by listing. The convention already existed in practice (b1, notplants-orchestrator, emily-sandbox) and was simply never written down. The symlink rule is unchanged: a consumer insisting on a fixed path gets a symlink into /secrets/<project>/, so the file still exists exactly once. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V3LdmEL7CvCYTNpoBq1kce
This commit is contained in:
+21
-4
@@ -7,12 +7,29 @@ in .env files, a private key at mode 0644. Anything in a repo is one `git add -A
|
||||
a push. So: ONE encrypted file, OUTSIDE every git tree, and a helper every project uses.
|
||||
|
||||
/secrets/store.yaml sops+age ciphertext (0600) — values our code reads
|
||||
/secrets/files/ real files (0600) SYMLINKED from the fixed path a third
|
||||
party insists on: ~/.ssh keys, a systemd EnvironmentFile,
|
||||
nix authKeyFile, a TLS keypair
|
||||
/secrets/<project>/ PROJECT-SCOPED secrets (0600): everything belonging to one
|
||||
project lives together, e.g. /secrets/lichen/,
|
||||
/secrets/b1/, /secrets/notplants-orchestrator/
|
||||
/secrets/files/ CROSS-PROJECT files (0600) SYMLINKED from the fixed path a
|
||||
third party insists on: ~/.ssh keys, a systemd
|
||||
EnvironmentFile, nix authKeyFile, a TLS keypair
|
||||
~/.config/sops/age/keys.txt the age private key, 0600
|
||||
|
||||
One home per secret: a value is in the store OR a file in /secrets/files, never both.
|
||||
PROJECT SECRETS GO IN /secrets/<project>/ (operator, 2026-08-20). If a secret belongs to one
|
||||
project, it goes in that project's directory — not in files/, and not with the project name
|
||||
baked into the filename. `/secrets/lichen/test-pds.env`, not `/secrets/files/lichen-test-pds.env`.
|
||||
Reserve files/ for things genuinely shared across projects.
|
||||
|
||||
WHY: a flat directory forces every name to carry its own scope, which nobody does consistently,
|
||||
and then nobody can answer "what does this project hold?" or "what do I revoke if this project is
|
||||
compromised?" without grepping. A directory answers both by listing. Put a README.md in the
|
||||
project directory saying what each file is, what consumes it, and what breaks if it is lost —
|
||||
the next person to read it will be doing so under time pressure.
|
||||
|
||||
The symlink rule is unchanged and applies the same way: a consumer that insists on a fixed path
|
||||
gets a SYMLINK into /secrets/<project>/, so the file still exists exactly once.
|
||||
|
||||
One home per secret: a value is in the store OR a file under /secrets, never both.
|
||||
/secrets is outside every git tree — not a repo, no remote — and outside /srv, which agents
|
||||
grep and walk constantly.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user