docs: condense comments across the repo

Comments had drifted into multi-paragraph narrative (git commit
lineage, debugging stories, restated code) in several hot spots
(scripts/deploy, hermes-agent.nix, flake.nix, gitea.nix, headscale.nix).
Trim every comment to its load-bearing "why" — gotchas, safety
warnings, and non-obvious rationale survive verbatim in substance,
just tightened to 1-2 sentences; historical narrative and anything
already covered in CLAUDE.md is cut. No code/logic changed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UJqEmY1y3AYX3JoX4Y6b21
This commit is contained in:
2026-09-18 21:36:30 +02:00
co-authored by Claude Sonnet 5
parent 3899290c5b
commit 6f24ab69ad
47 changed files with 1051 additions and 1965 deletions
+26 -51
View File
@@ -22,25 +22,16 @@
password=${config.sops.placeholder.samba_password}
'';
# Hermes Agent (hermes-agent.nix) — moved here from jupiter (see that
# host's git history); same Telegram bot token, opencode key, and
# Authentik OIDC client secret, so no new bot/app to provision.
# Hermes Agent (hermes-agent.nix) — same Telegram bot token, opencode key,
# and Authentik OIDC client secret as it used before moving here from
# jupiter, so no new bot/app to provision.
sops.secrets.opencode_go_api_key = { };
sops.secrets.telegram_bot_token = { };
sops.secrets.hermes_dashboard_oidc_client_secret = { };
# Same value as in secrets/jupiter.yaml (the sending side), stored WITHOUT a
# trailing newline — a stray newline would change the key the HMAC is
# computed with and fail every delivery. `scripts/edit_secrets` writes a
# bare value. hermes-agent.nix trims one anyway, belt and braces.
#
# This is NOT in the container's env any more. It used to be, because
# hermes-agent-webhook-route ran `hermes webhook subscribe` inside the
# container and read the secret back out of its environment — which meant
# podman-hermes-agent had to be restarted first on rotation, or the
# subscription silently pinned the stale value. The route config is now
# written host-side (hermes-agent-webhook-routes reads this file directly),
# so that ordering constraint is gone and the secret no longer sits in an
# env var luna can read with `env`.
# Same value as secrets/jupiter.yaml (the sending side), stored WITHOUT a
# trailing newline — a stray newline would change the HMAC key and fail
# every delivery. Written host-side by hermes-agent-webhook-routes, so it
# no longer needs to sit in the container's env where luna could read it.
sops.secrets.gitea_hermes_webhook_secret = {
restartUnits = [ "hermes-agent-webhook-routes.service" ];
};
@@ -54,47 +45,31 @@
HERMES_DASHBOARD_OIDC_CLIENT_SECRET=${config.sops.placeholder.hermes_dashboard_oidc_client_secret}
'';
# luna's own gitea push token (services/dev/gitea.nix provisions the
# account + PR-tier repo access on jupiter; this is the per-user token
# generated once via `gitea admin user generate-access-token --username
# luna --scopes write:repository,read:user` on jupiter — read:user is
# required, `tea logins add` fails without it). Read directly by
# hermes-agent.nix's prepare-dirs oneshot (default root:root owner is
# fine — that oneshot already runs as root) to set up a git
# credential-store file and a `tea` login, both written into hermesHome
# so they're visible inside the container at /opt/data/....
# restartUnits re-provisions both on rotation, without a full mars deploy.
# luna's gitea push token (services/dev/gitea.nix provisions the account +
# PR-tier access), generated once via `gitea admin user generate-access-token
# --username luna --scopes write:repository,read:user` on jupiter — read:user
# is required or `tea logins add` fails. restartUnits re-provisions the git
# credential-store file and `tea` login on rotation, without a full deploy.
sops.secrets.gitea_luna_token.restartUnits = [ "hermes-agent-prepare-dirs.service" ];
# livesync-bridge (livesync-bridge.nix) — luna's Obsidian vault, mirrored
# out of CouchDB on jupiter. Both values are consumed by the rendered
# config.json rather than read directly, so the sops default of root:root
# 0400 is correct here; only the TEMPLATE needs an owner (set where it is
# defined, next to the vault path it references).
# from CouchDB on jupiter. Consumed only via the rendered config.json, so
# the sops default of root:root 0400 is fine here.
#
# couchdb_luna_password holds jupiter's `obsidian` ADMIN password — the same
# value as secrets/jupiter.yaml's couchdb_admin_password and
# obsidian_luna_passphrase is the same passphrase as the personal vault.
# That is a deliberate choice to reuse what already existed, but it is worth
# being clear about what it costs: mars can decrypt and read EVERY vault
# database, not just luna's, and mars is the box running an autonomous
# agent. The two are independent to fix, cheapest first:
#
# 1. A vault-specific passphrase (re-encrypts luna's remote database, but
# leaves the personal vault's contents unreadable from here).
# 2. A CouchDB account scoped to luna's database via _security (three curl
# calls, in README -> "Obsidian vaults"), which also stops mars from
# reaching the other databases at all.
#
# Neither is required for the bridge to work; both shrink the blast radius
# if mars is ever compromised.
# ⚠️ couchdb_luna_password is jupiter's `obsidian` ADMIN password (same as
# secrets/jupiter.yaml's couchdb_admin_password) and obsidian_luna_passphrase
# reuses the personal vault's passphrase — reusing what already existed, but
# it means mars (running an autonomous agent) can decrypt and read EVERY
# vault database, not just luna's. To shrink that blast radius: give luna's
# vault its own passphrase, and/or scope a CouchDB account to her database
# via _security (README -> "Obsidian vaults"). Neither is required for the
# bridge to work.
sops.secrets.couchdb_luna_password = { };
# The E2EE passphrase for luna's vault, as entered in the Obsidian plugin.
# Vault passphrases otherwise never leave the clients (see the note in
# services/dev/obsidian-livesync.nix) — this one has to be here because mars
# IS a client: it decrypts in order to write real markdown to disk. Path
# obfuscation uses the same passphrase in the plugin, so the bridge's
# separate obfuscatePassphrase field is fed from this one value.
# Vault passphrases otherwise never leave the clients (obsidian-livesync.nix)
# — this has to be here because mars IS a client, decrypting to write real
# markdown to disk. Also feeds the bridge's separate obfuscatePassphrase
# field, since the plugin derives path obfuscation from the same value.
sops.secrets.obsidian_luna_passphrase = { };
}