refactor(secrets): store lives at /secrets, not /srv/secrets

/srv is the projects tree — agents grep, find and `ls -R` it constantly, so a store
under it turns up in ordinary searches and risks being read (or pasted) by accident.
/secrets sits outside that blast radius: nothing routinely walks it, and it is still
0700 loops, still not a repo, still ciphertext at rest.

Override with AO_SECRETS_STORE if a host puts it elsewhere.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-01 16:54:03 +00:00
co-authored by Claude
parent 7605efe624
commit 6c56c1953e
2 changed files with 8 additions and 8 deletions
+5 -5
View File
@@ -6,9 +6,9 @@ remote URLs (`https://user:pass@host/...`, which `git remote -v` happily prints)
in .env files, a private key at mode 0644. Anything in a repo is one `git add -A` away from
a push. So: ONE encrypted file, OUTSIDE every git tree, and a helper every project uses.
store: /srv/secrets/store.yaml sops+age ciphertext, mode 0600
store: /secrets/store.yaml sops+age ciphertext, mode 0600
age key: ~/.config/sops/age/keys.txt the ONLY plaintext secret on disk, 0600
outside git by construction — /srv/secrets is not a repo and has no remote.
outside git by construction — /secrets is not a repo and has no remote.
USAGE (library):
from secrets import get, get_group
@@ -23,14 +23,14 @@ USAGE (CLI):
MATERIALIZED FILES: some consumers read a fixed path and cannot be taught otherwise (a
systemd EnvironmentFile, an ssh IdentityFile, `nix`'s authKeyFile). Those files still exist
on disk at 0600, but the STORE IS THE SOURCE OF TRUTH — `materialize` rewrites them from it.
Never edit a materialized file by hand; edit the store (`sops /srv/secrets/store.yaml`) and
Never edit a materialized file by hand; edit the store (`sops /secrets/store.yaml`) and
re-materialize, or the two silently drift.
ADDING A SECRET: sops /srv/secrets/store.yaml (opens decrypted in $EDITOR, re-encrypts on save)
ADDING A SECRET: sops /secrets/store.yaml (opens decrypted in $EDITOR, re-encrypts on save)
"""
import json, os, subprocess, sys, pathlib
STORE = os.environ.get("AO_SECRETS_STORE", "/srv/secrets/store.yaml")
STORE = os.environ.get("AO_SECRETS_STORE", "/secrets/store.yaml")
AGE_KEY = os.environ.get("SOPS_AGE_KEY_FILE", os.path.expanduser("~/.config/sops/age/keys.txt"))
# name -> (path, mode). Files a consumer reads from a fixed location; see MATERIALIZED FILES.