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
+28 -55
View File
@@ -1,50 +1,28 @@
#!/usr/bin/env bash
# Import the OLD ZimaOS/CasaOS Immich database into the NixOS-managed one.
# Run this ON jupiter, as root, ONCE, AFTER the first `./deploy switch jupiter`
# that ships services/media/immich.nix (the empty `immich` DB must exist).
#
# The media files are moved separately — do that FIRST, it is a rename on the
# same filesystem, so instant even at 9.1G. Move the CONTENTS, not the dir:
# systemd.tmpfiles already created /mnt/data/AppData/immich on the first
# deploy, so `mv <src> <dst>` would nest it as .../immich/upload/ and every
# thumbnail lookup would ENOENT.
# Run ONCE on jupiter, as root, after the first `./deploy switch jupiter` that
# ships services/media/immich.nix (the empty `immich` DB must already exist).
#
# Move the media files separately FIRST (a same-filesystem rename, instant
# even at 9.1G) — move the CONTENTS of /mnt/data/Immich/upload into
# /mnt/data/AppData/immich, not the directory itself, or it nests under
# .../immich/upload and every thumbnail lookup ENOENTs:
# systemctl stop immich-server immich-machine-learning
# mv /mnt/data/Immich/upload/* /mnt/data/AppData/immich/
# chown -R immich:immich /mnt/data/AppData/immich
# chmod 700 /mnt/data/AppData/immich
# chown -R immich:immich /mnt/data/AppData/immich && chmod 700 /mnt/data/AppData/immich
#
# Expected afterwards: library/ upload/ thumbs/ encoded-video/ profile/ backups/
#
# The legacy cluster turned out to be Postgres 14 running VectorChord 0.3.0 +
# pgvector 0.8.1 (NOT pgvecto.rs), the same extensions nixpkgs ships — so this
# is a plain version-upgrade dump/restore and the smart-search and face
# embeddings come across intact. No re-running the ML jobs over the library.
# Upstream's accepted VectorChord range is >= 0.3, < 2.0, so 0.3.0 -> 1.1.1 is
# a supported jump; the REINDEX at the end is what upstream asks for after a
# version change.
#
# What this script does:
# 1. cp -a the legacy PGDATA to a scratch dir (the original is never touched,
# never even mounted rw — postgres would replay WAL into it).
# 2. Boots that copy under immich's own PG14 image, pinned to the SAME
# VectorChord version nixpkgs has (1.1.1), and runs `ALTER EXTENSION
# vchord UPDATE` so the catalog matches the loaded library.
# 3. Dumps it with the LOCAL pg_dump (17.x) over TCP, not the container's
# pg_dump (14.x) — dumping with the newer tool is the supported direction.
# 4. Restores into a scratch DB, hands ownership to the immich role, shows
# you the row counts, and only swaps it into place after you confirm.
#
# Afterwards Immich runs its own schema migrations up to 2.7.5 on first start.
# The legacy cluster is Postgres 14 + VectorChord 0.3.0 + pgvector 0.8.1 (the
# same extensions nixpkgs ships), so this is a plain version-upgrade
# dump/restore — smart-search and face embeddings come across intact with no
# ML rerun needed.
set -euo pipefail
LEGACY="${LEGACY:-/mnt/data/Immich/pg-data}"
WORK="${WORK:-/var/tmp/immich-import}"
# Pinned to EXACTLY what the legacy cluster records in pg_extension
# vchord 0.3.0 + pgvector 0.8.1 so the old server reads its own indexes
# without any in-place extension upgrade. The target side is vchord 1.1.1 /
# pgvector 0.8.2, which is fine: a dump/restore rebuilds every index from
# scratch, so only the index DEFINITION has to still be valid there.
# Pinned to exactly what the legacy cluster's pg_extension records (vchord
# 0.3.0/pgvector 0.8.1) so it reads its own indexes unmodified; the dump/
# restore rebuilds indexes from scratch on the target's newer versions, so
# only the index definitions need to stay valid.
IMAGE="${IMAGE:-ghcr.io/immich-app/postgres:14-vectorchord0.3.0-pgvector0.8.1}"
CTR=immich-legacy-pg
PORT="${PORT:-15432}"
@@ -71,13 +49,10 @@ cp -a "$LEGACY" "$WORK/pgdata"
# A crashed cluster leaves this behind; it makes the container refuse to start.
rm -f "$WORK/pgdata/postmaster.pid"
# The dump runs over TCP (local pg_dump 17 -> published port), and this
# cluster's own pg_hba wants a password for host connections — the marketplace
# app's POSTGRES_PASSWORD is long gone, and POSTGRES_HOST_AUTH_METHOD only
# applies when the image INITIALISES a cluster, not to an existing one. This is
# a scratch copy bound to 127.0.0.1 for the length of one dump, so trust it.
# REPLACE the file rather than appending: pg_hba is first-match-wins, and the
# image's existing scram-sha-256 line would shadow anything added below it.
# The marketplace app's original POSTGRES_PASSWORD is long gone, and
# POSTGRES_HOST_AUTH_METHOD only applies when the image initializes a cluster
# (not an existing one) — so pg_hba is REPLACED outright (not appended, since
# it's first-match-wins) to trust this scratch copy while it's dumped.
cat > "$WORK/pgdata/pg_hba.conf" <<'EOF'
local all all trust
host all all 0.0.0.0/0 trust
@@ -101,10 +76,10 @@ for _ in $(seq 1 60); do
done
[ "${ready:-}" = 1 ] || { podman logs --tail 30 "$CTR"; die "legacy postgres never became ready"; }
# The compose stack's POSTGRES_USER is not recorded anywhere on disk and is NOT
# necessarily "postgres" — the ZimaOS/CasaOS marketplace app used "casaos".
# pg_isready reports "accepting connections" even for a role that doesn't
# exist, so probe for one that can actually log in.
# The original POSTGRES_USER isn't recorded on disk and wasn't necessarily
# "postgres" (this marketplace app used "casaos"), and pg_isready reports
# ready even for a role that doesn't exist — so probe for one that can
# actually log in.
if [ -z "$LEGACY_USER" ] || ! podman exec "$CTR" psql -U "$LEGACY_USER" -lqt >/dev/null 2>&1; then
for candidate in casaos immich postgres; do
if podman exec "$CTR" psql -U "$candidate" -lqt >/dev/null 2>&1; then
@@ -160,13 +135,11 @@ echo ">> errors logged: $(grep -c '^ERROR' "$WORK/restore.log" || true) (see $W
grep '^ERROR' "$WORK/restore.log" | sort -u | head -10 | sed 's/^/ /' || true
step "handing ownership to the immich role"
# --no-owner made everything owned by the restoring role (postgres); immich
# connects as "immich" and its startup migrations run ALTER TABLE, so it must
# own its own schema. NOT `REASSIGN OWNED BY postgres`that also sweeps up
# system objects and fails with "cannot reassign ownership of objects owned by
# role postgres because they are required by the database system". Extension-
# owned routines/types are excluded for the same reason; immich never alters
# those, and they correctly stay with postgres.
# immich's own ALTER TABLE migrations need it to own its schema, but plain
# `REASSIGN OWNED BY postgres` also sweeps up system objects and fails on ones
# the database system requires — so ownership is walked table-by-table
# instead, skipping extension-owned routines/types, which correctly stay with
# postgres.
sudo -u postgres psql -qd "$STAGING_DB" <<'SQL'
ALTER SCHEMA public OWNER TO immich;
DO $$