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
+49 -71
View File
@@ -1,59 +1,47 @@
{ config, pkgs, inputs, ... }:
# livesync-bridge (vrtmrz) — mirrors an Obsidian LiveSync vault out of CouchDB
# on jupiter (services/dev/obsidian-livesync.nix) into a real directory of
# markdown here, so luna can read and write the vault as files. Obsidian itself
# is an Electron GUI with no headless mode, and an agent wants files anyway.
#
# ⚠️ THE WRITE-BACK PATH IS THE RISKY ONE. Upstream has three open, unanswered
# issues on storage->couchdb — #50 (Jun 2026, writes detected and logged as
# uploaded, database never updated), #23 (only lowercase filenames transmitted
# from storage), #46 (silent stall on files over ~30KB). All fail QUIETLY: the
# log says success and the note never arrives. So do not treat this directory
# as durable storage for anything luna cannot regenerate, and check that her
# edits actually reach your devices before trusting it. (E2EE itself is fine —
# PeerCouchDB.ts hard-errors if a passphrase is missing for an encrypted
# remote, so it is a deliberate code path. The one issue claiming E2EE breaks
# bridging, #12, is a single unreproduced report with no maintainer reply.)
# on jupiter (services/dev/obsidian-livesync.nix) into real markdown files
# here, since Obsidian itself is a GUI-only Electron app and luna needs files.
#
# ⚠️ THE WRITE-BACK PATH IS THE RISKY ONE: upstream has open bugs where a
# write is logged as uploaded but the database is never updated (#50), only
# lowercase filenames sync from storage (#23), and files over ~30KB silently
# stall (#46) — all fail quietly with no error in the log. Don't treat this
# directory as durable for anything luna can't regenerate, and verify her
# edits actually reach your devices. (E2EE itself is fine — it hard-errors on
# a missing passphrase rather than failing silently.)
#
# EXPECTED NOISE ON FIRST SYNC: a stack trace per historically-deleted file —
# NotFound: ... remove '<vault>/Welcome.md' at PeerStorage.delete
# CouchDB keeps deletion tombstones, and the bridge replays them against a
# directory where the file never existed. PeerStorage.ts:33-40 catches it,
# logs, and returns false, so nothing is wrong; it only LOOKS fatal because
# main.ts pins the logger to LOG_LEVEL_DEBUG, which prints exception dumps
# that are otherwise verbose-level. It stops once the initial catch-up ends.
# Talks to CouchDB over the TAILNET (jupiter.orbit.sol:5984), not through
# neptun: mars is a tailnet node, so the public vhost, its TLS and its path
# allowlist are all irrelevant here.
# CouchDB replays deletion tombstones against a directory where the file
# never existed. Harmless, caught and logged, and stops once the initial
# catch-up ends.
#
# Talks to CouchDB over the tailnet (jupiter.orbit.sol:5984) directly — mars
# is a tailnet node, so neptun's public vhost/TLS/allowlist don't apply here.
let
stateDir = "/var/lib/livesync-bridge";
appDir = "${stateDir}/app";
vaultDir = "${stateDir}/vault";
# The same uid/gid the hermes-agent container runs as (hermes-agent.nix).
# Deliberate: the bridge and luna both read and write these files, and
# sharing one uid removes any dependence on the container's umask. Two
# different uids in a shared group only works while every file stays
# group-writable, and a single 0644 file dropped by the agent would stall
# sync on that path with nothing but a permission error in the log.
# The same uid/gid hermes-agent runs as (hermes-agent.nix), so both peers
# share files without depending on umask — two uids in a shared group only
# works while every file stays group-writable, and one 0644 file from the
# agent would silently stall sync.
hermesUid = 986;
# Which vault. `group` is what pairs the two peers — both must match or the
# bridge starts cleanly and simply never syncs anything.
# `group` pairs the two peers — mismatched and the bridge starts but never
# syncs.
#
# ⚠️ `database` must be the name entered in the Obsidian plugin for luna's
# vault. Get it wrong and nothing errors: the credential below is CouchDB's
# admin, so PouchDB CREATES the misnamed database and replicates an empty
# vault into it quite happily.
# ⚠️ `database` must match the name entered in the Obsidian plugin exactly:
# get it wrong and nothing errors, since the admin credential below lets
# PouchDB just create the misnamed database and replicate an empty vault.
peerGroup = "luna";
database = "luna_wiki";
in
{
# hermes-agent.nix declares the GROUP (gid 983) but no user: the container
# brings its own uid and needs no host account. The bridge does need one to
# run as, so the matching user is declared here.
# hermes-agent.nix declares the group (gid 983) but no user the container
# needs no host account, but this service does, so it's declared here.
users.users.hermes = {
uid = hermesUid;
group = "hermes";
@@ -62,27 +50,23 @@ in
description = "Hermes agent uid, shared with the livesync-bridge service";
};
# Created here rather than by the service so they exist before anything
# tries to use them:
# - vaultDir before podman-hermes-agent starts, because a bind-mount
# source that does not exist is created by podman as root:root and the
# bridge then cannot write into its own vault;
# - appDir because WorkingDirectory applies to ExecStartPre as well, so a
# missing one fails the unit before preStart ever gets to create it.
# Created here, not by the service, so they exist before anything needs
# them: vaultDir before podman-hermes-agent starts (else podman creates it
# as root:root), and appDir before ExecStartPre runs (WorkingDirectory
# applies to it too).
systemd.tmpfiles.rules = [
"d ${vaultDir} 0770 hermes hermes -"
"d ${appDir} 0750 hermes hermes -"
"d ${stateDir}/deno 0750 hermes hermes -"
];
# The bridge's peer config, rendered by sops because it carries three
# secrets inline (CouchDB password + both passphrases) and the file format
# has no include mechanism.
# Rendered by sops (three inline secrets: CouchDB password + both
# passphrases; the json format has no include mechanism).
#
# ⚠️ sops substitutes placeholders into the ALREADY-RENDERED json, so a
# secret containing a double quote or a backslash produces an invalid config
# and the bridge logs "Could not parse configuration!" and then sits there
# with zero peers — it does not exit. Keep all three values alphanumeric.
# ⚠️ sops substitutes into the ALREADY-RENDERED json, so a secret with a
# quote or backslash yields invalid config — the bridge then just sits with
# zero peers logging "Could not parse configuration!" instead of exiting.
# Keep all three values alphanumeric.
sops.templates."livesync-bridge.json" = {
owner = "hermes";
content = builtins.toJSON {
@@ -96,11 +80,10 @@ in
username = "obsidian";
password = config.sops.placeholder.couchdb_luna_password;
passphrase = config.sops.placeholder.obsidian_luna_passphrase;
# The plugin derives path obfuscation from the same passphrase it
# uses for content, so this is the same secret. Split into its own
# field because the bridge takes them separately — if paths come
# back as garbage while contents decode fine, this is the field that
# is wrong.
# Same secret as the content passphrase — the plugin derives path
# obfuscation from it too, but the bridge takes them as separate
# fields. If paths come back as garbage while contents decode fine,
# this is the field to check.
obfuscatePassphrase = config.sops.placeholder.obsidian_luna_passphrase;
# Reads the chunking tweaks the plugin stored in the remote, instead
# of guessing sizes that then disagree with every other client.
@@ -137,22 +120,17 @@ in
HOME = stateDir;
};
# Copy the pinned source out of the store and install its locked deps.
# It cannot run from /nix/store directly: deno.jsonc sets
# `nodeModulesDir: manual` with byonm, so `deno install` must write a
# node_modules/ next to the sources.
# Copies the pinned source out of the store and installs locked deps,
# since deno.jsonc's `nodeModulesDir: manual` (byonm) needs to write
# node_modules/ next to the sources — it can't run from /nix/store directly.
#
# The copy target is a FIXED path on purpose. Deno keys localStorage
# which is where the bridge records per-file sync state (Peer.ts:119) — by
# the main module's origin, and stores it under
# DENO_DIR/location_data/<sha of that origin>. VERIFIED by running the same
# source from two paths against one DENO_DIR: two separate origin dirs
# appear. Running straight from /nix/store would therefore change the
# origin on every input bump and silently reset the bridge to a full
# rescan of both peers.
# The copy target is a FIXED path on purpose: Deno keys its localStorage
# (where the bridge tracks per-file sync state) by the main module's
# origin, so running straight from /nix/store would change that origin —
# and reset the bridge to a full rescan of both peers — on every input bump.
#
# Guarded by a stamp file so this is a no-op on ordinary restarts; only a
# flake input bump pays for the re-install (which needs network).
# Guarded by a stamp file: a no-op on ordinary restarts, only a flake
# input bump pays for the (networked) re-install.
preStart = ''
set -eu
stamp=${stateDir}/.src