Adds services/dev/obsidian-livesync.nix — CouchDB 3 from the native
nixpkgs module, tuned as the backend for the Self-hosted LiveSync plugin
— and publishes it as notes.mgaction.town through neptun.
It goes out over the public reverse proxy rather than staying on the LAN
because Obsidian's mobile apps refuse cleartext HTTP and *.jupiter.sol
cannot hold a publicly trusted cert. That makes the hardening load-bearing
rather than decorative:
- require_valid_user in both [chttpd] and [chttpd_auth], so nothing
answers unauthenticated on the open internet;
- neptun's vhost matches on CouchDB's own naming rule (system endpoints
all begin with `_`, user databases never can), so Fauxton, /_all_dbs
and /_node/_local/_config — which rewrites the server config given
admin credentials — 404 at the proxy while any number of per-vault
databases pass. Verified against both sets of paths with caddy run
against a stub backend;
- the plugin's own E2EE carries the actual confidentiality: jupiter only
ever stores ciphertext. Its passphrase is deliberately NOT in sops —
it never leaves the clients, and pairing it with the server credential
would defeat the point.
flush_interval -1 is required, not tuning: replication rides a continuous
_changes feed that caddy would otherwise buffer into a stall.
Storage sits on the array with RequiresMountsFor, since a CouchDB that
starts without /mnt/data would create an empty database on the eMMC and
LiveSync would replicate that emptiness back to every client. Logs go to
journald rather than the unrotated /var/log/couchdb.log, for the same
29G-eMMC reasons as the rest of jupiter.
The admin password reaches CouchDB as an [admins] ini fragment via
extraConfigFiles; services.couchdb.adminPass would have rendered it into
the world-readable store.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TLN5nkLBtCciD3ZnUwtw2b
New on-site host mars runs Hermes Agent as its sole service: joins the
tailnet, mounts jupiter's samba share at /mnt/jupiter (doubling as
Hermes's shared dropbox), and hosts state locally under /var/lib/hermes.
Same Authentik OIDC app/Telegram bot as before, just relocated — neptun's
hermes.mgaction.town vhost now points at mars.orbit.sol instead of jupiter.
hosts/jupiter/hermes-agent.nix and its three sops secrets are removed;
jupiter's Caddy vhost for it is gone too. Also refreshes tailscale_authkey
across all hosts and fixes two stale "erik@laptop" keys in flake.nix's
kexec/installer-iso images (leftover from a previous laptop, already
swapped out of common.nix back in 2fd5752) to darman@terra.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011FHr5ug9pu8q4XPrRkFnzJ
A separate instance from terra's, deliberately locked down harder given
jupiter's much bigger blast radius (irreplaceable immich photos on an
unredundant RAID0, gitea/CI tokens, the whole media stack): its own
dedicated "hermes" system user rather than darman (who is in jupiter's
root-equivalent docker group), container.enable = true for whole-process
containment rather than native/bare-metal, its own Telegram bot + explicit
allowlist, and no volume access to /mnt/data or this repo. stateDir/
workingDirectory live on the array (off the 29G eMMC) for future coding-task
state, guarded by RequiresMountsFor like the rest of jupiter's array-backed
services.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
nix.gc (common.nix) is weekly, too slow to catch a switch pinning the old
generation's closure on a 29G eMMC — add a full nix-collect-garbage on every
boot instead. Also set boot.swraid.mdadmConf so eval stops warning that
mdmon will crash (dormant here: the RAID0 array uses native superblocks, so
mdmon never actually runs).
sabnzbd.configFile is deprecated by the module; move to services.sabnzbd.settings
with credentials (web login, api/nzb keys, eweka.nl server) sourced from sops via
secretValues instead of living in a plaintext ini. admin_dir/log_dir are pinned
absolute at their original /mnt/data location so the existing download
queue/history isn't reset by the ini moving to /var/lib/sabnzbd.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Enables hardware.graphics + intel-media-driver for the Apollo Lake's
Gen9 iGPU (VAAPI only — QSV needs an insecure/EOL runtime on this
chip) and adds jellyfin's service user to video/render for the DRI
card node. 4K HDR still can't be tone-mapped on this hardware; those
files need to direct-play or be kept as 1080p SDR.
Also relocates podman's container storage and immich's postgres
cluster to /mnt/data/AppData, after a deploy holding two ~9G closures
at once filled the 29G eMMC and postgres died mid-write.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
After the rack move one of the RAID0 disks failed to enumerate, and jupiter
boot-looped into an emergency shell nobody could use — root is locked, so
sulogin offers a prompt with no answer, and there is no ssh from there:
Timed out waiting for device /dev/disk/by-uuid/dadbff6f-...
Dependency failed for /mnt/data.
Dependency failed for /var/lib/private/prowlarr.
Dependency failed for Local File Systems.
local-fs.target: Job local-fs.target/start failed with result 'dependency'
Reached target Emergency Mode.
`nofail` on /mnt/data did not help, because the prowlarr and seerr bind
mounts layered on top of it had none: without it a mount is RequiredBy
local-fs.target, so those two failed the target on the array's behalf. Give
them `nofail` too and let them fail alone. `systemd.enableEmergencyMode =
false` then keeps a bad array from costing a reachable box at all — far more
useful on a headless host than a console prompt.
Booting further is only safe if nothing quietly relocates onto the 29G eMMC,
so pin the array-backed services to the mount. systemd derives
RequiresMountsFor from a unit's own paths, which for these is somewhere under
/var/lib (eMMC) — nothing pointed immich at mediaLocation or sabnzbd at its
configFile, so with the array gone they would have started and written to the
OS disk, into directories that go invisible the moment /mnt/data mounts over
them. jellyfin, sonarr, radarr and gitea already had a real dependency and
are untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
jupiter had a leftover docker-compose Immich on the RAID (/mnt/data/Immich,
9.9G) that survived the NixOS install. Native module now, media at
/mnt/data/AppData/immich, caddy vhost on 2283 with a 50GB body limit
(caddy's default rejects video uploads).
The package comes from nixpkgs-unstable, the module from the 26.05 pin:
26.05 ships immich 2.7.5, but that database was last written by 3.0.0 and
migrations only run forward --
corrupted migrations: previously executed migration
1776217577402-DropAuditTable is missing
Safe because the two module files are byte-identical at these revisions;
services/media/immich.nix carries the diff command to re-check on a bump.
Drop the input once the stable pin ships >= 3.0.0.
immich needs group "users" only to traverse /mnt/data/AppData (drwx--x---);
its own dir stays 0700 immich:immich. mediaLocation is outside /var/lib, so
the module's tmpfiles entry only ADJUSTS it -- add a rule that creates it.
scripts/immich-import-legacy-db does the database half: boots a copy of the
legacy PGDATA under the matching image (PG14 + vchord 0.3.0 + pgvector
0.8.1), dumps it with the local pg_dump 17, restores into a scratch DB,
fixes ownership, and only swaps after confirmation. Never touches the
original. The old cluster ran VectorChord, not pgvecto.rs, so the smart
search and face embeddings survive -- no ML re-run.
Imported: 666 assets, 25 people, 647 clip + 359 face embeddings, 2 users.
Group service modules by category (media, network, vpn, identity,
dev, desktop) to make the growing services/ dir easier to navigate.
containers.nix stays at the top level since it's a shared backend,
not a single-category service.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- sabnzbd, prowlarr, sonarr, radarr, clonarr, seerr, cinephage, mediamanager
services, wired into jupiter with LAN Caddy vhosts.
- Gitea: migrated the old ZimaOS docker instance's data (sqlite db, 4 repos,
no LFS objects) into the NixOS module's default stateDir layout. HTTP via
Caddy; git SSH on its own built-in server at :2222 (not :222 - the unpriv
gitea user can't bind <1024).
- mediamanager-nix flake input for the mediamanager service.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>