1502 Commits
Author SHA1 Message Date
Gabriel Fontes 1cec5bbd6c chore(flake): update inputs
Update direct inputs:
- firefox-addons a5ad6b67 -> 25227969 (84 commits; uBlock Origin 1.71.0 -> 1.73.0)
- nixos-hardware 2e790b0a -> 0471accf (15 commits; Raspberry Pi and P14s fixes)
- home-manager 06258193 -> 03f4cd46 (203 commits; module fixes, additions, and migrations)
- lanzaboote 6650fb7c -> 69cf334f (34 commits; firmware key option, docs, and dependency updates)
- nix-minecraft 794884da -> 25b4dfcd (54 commits; server locks and NeoForge fixes)
- nixos-mailserver c37fd9c4 -> e668474d (23 commits; Postfix CA workaround and maintenance)
- sops-nix 420f8d2e -> a8627b21 (17 commits; dependency and initrd test fixes)
- system-manager 48d47346 -> 64748b62 (3 commits; unstable/stable branch support)
- nixpkgs 567a49d1 -> 56c02bc0 (104 direct system/home package changes)

Keep the legacy RuneScape client working with OpenSSL 1.1 from a pinned
nixos-26.05 input, and preserve the removed Materia theme locally without
its obsolete Murrine runtime dependency. Adapt removed package references and
renamed NixOS/Home Manager options exposed by the update.

- rebase the llama.cpp cache-scan patch onto 10408
- adapt the fish-helix compatibility patch to Fish 4.8
- update Hyprbars for the Hyprland 0.56 plugin API
- use the Python distribution name for Lyrics metadata checks
- disable broken qutebrowser GBM video path (QtWebEngine 6.11.1)

Assisted-by: pi (gpt-5.6-sol)
2026-08-27 09:45:57 -03:00
Gabriel Fontes 448fa54304 fix(grafana): ignore read-only Nix store mount
Assisted-by: pi (gpt-5.6-sol)
2026-08-26 15:36:38 -03:00
Gabriel Fontes b75bf2153a refactor(lib): share systemd device path conversion
Assisted-by: pi (gpt-5.6-sol)
2026-08-24 12:51:44 -03:00
Gabriel Fontes 09097bf97a fix(merope): bind media mount to stable device unit
Assisted-by: pi (gpt-5.6-sol)
2026-08-24 12:51:44 -03:00
Gabriel Fontes 631c049b89 fix(grafana): reduce availability alert noise
Assisted-by: pi (gpt-5.6-sol)
2026-08-24 11:32:26 -03:00
Gabriel Fontes a4bfacee5b feat(grafana): alert on read-only btrfs filesystems
Assisted-by: pi (gpt-5.6-sol)
2026-08-24 11:19:16 -03:00
Gabriel Fontes 0756c21c6e fix(merope): stop media consumers when storage unmounts
Assisted-by: pi (gpt-5.6-sol)
2026-08-24 11:13:53 -03:00
Gabriel Fontes 7ee25ba834 fix(merope): bind media mount to its device
Assisted-by: pi (gpt-5.6-sol)
2026-08-24 11:01:53 -03:00
Gabriel Fontes d61d8dd7c4 fix(hosts): use stable disk identifiers
Assisted-by: pi (gpt-5.6-sol)
2026-08-24 10:54:18 -03:00
Gabriel Fontes 052d20c9c7 feat(hosts/merope/calibre-web): expose calibre content server on tailnet
Add a calibre.m7.rs vhost proxying the calibre-server content server,
restricted to localhost and the headscale tailnet prefixes, and enable
calibre-server auth so the endpoint is not anonymous.
2026-08-20 19:25:54 -03:00
Gabriel Fontes 901be0cfe2 fix(hosts/merope/calibre-web): make library read-only
calibre-web wrote metadata.db directly, which desynced calibre-server's
in-memory library cache: books uploaded through books.m7.rs stayed
invisible on the content server until it was restarted, and two writers
on one library risk clobbering each other's metadata.

Drop calibre-web from the readarr and calibre-server groups and force
ReadWritePaths down to its state directory, so the pre-existing
ProtectSystem=strict leaves the library read-only. The 0002 umask only
existed to keep those shared-library writes group-writable, so it goes
too. Readarr already writes through the content server API, so
calibre-server is now the sole writer.

Note that services.calibre-web.options.enableBookUploading does not
cover this on its own: config_uploading only drives the templates, while
POST /upload stays gated on the per-user ROLE_UPLOAD bit alone.

Assisted-by: pi (claude-opus-5)
2026-08-20 19:11:31 -03:00
Gabriel Fontes 2768852d7c feat(hosts/ubuntu/electra): wire up ssh server
Mirror the NixOS openssh config on system-manager: no password auth, no
root login, ed25519 host key only, and gabriel's authorized keys. Its
module runs Ubuntu's /usr/sbin/sshd, so PAM keeps working.

Also enable programs.ssh for system-wide known hosts, and teach the NixOS
side about system-manager hosts so they know electra.
2026-08-18 10:56:49 -03:00
Gabriel Fontes f3f1a448df fix(hosts/merope/calibre-web): fix umask
This ensures calibre-server and readarr can still write
2026-08-17 18:23:24 -03:00
Gabriel Fontes 28cb6d733c fix(hosts/merope): go back to upstream seerr 2026-08-17 12:47:29 -03:00
Gabriel Fontes 77279ae9ac feat(hosts/merope): make calibre able to write into library 2026-08-17 12:31:51 -03:00
Gabriel Fontes 0cc4db163f fix(merope/seerr): allow large Lidarr libraries
Raise the Axios response limit to 64 MiB only for Lidarr so large album libraries can complete SeerrNG scans without weakening the cap for other external APIs.

Assisted-by: pi (gpt-5.6-sol)
2026-08-17 11:32:50 -03:00
Gabriel Fontes a510f5d510 fix(alcyone/mail): disable authentication on public smtp
Keep SASL enabled only on the Tailscale-restricted submissions service.

Assisted-by: pi (gpt-5.6-sol)
2026-08-17 11:14:14 -03:00
Gabriel Fontes 42ed14a404 Revert "feat(merope/books): replace Calibre with Kavita"
This reverts commit 0a1f6f86c1.
2026-08-17 10:18:11 -03:00
Gabriel Fontes 863387b99b fix(merope/seerr): use canonical dynamic state path
Point SeerrNG directly at systemd's private StateDirectory target so its log
path security check does not reject the public DynamicUser symlink.

Assisted-by: pi (gpt-5.6-sol)
2026-08-17 09:57:13 -03:00
Gabriel Fontes 28d3bf2569 fix(hosts/alcyone/secrets): fix calibre pwd 2026-08-17 09:55:01 -03:00
Gabriel Fontes 3afd0ae631 feat(merope/seerr): switch to SeerrNG 2026-08-17 08:23:41 -03:00
Gabriel Fontes 4ad5343165 fix(merope/kavita): avoid JellySearch port collision
Move Kavita to port 5001 because JellySearch already occupies its default
port 5000, which left Kavita restarting against JellySearch's listener.

Assisted-by: pi (gpt-5.6-sol)
2026-08-17 01:25:23 -03:00
Gabriel Fontes 885d7fb921 refactor(merope/jellyseerr): use dynamic user
Assisted-by: pi (gpt-5.6-sol)
2026-08-17 00:56:01 -03:00
Gabriel Fontes 0a1f6f86c1 feat(merope/books): replace Calibre with Kavita
Serve the Readarr-owned library through read-only Kavita, with a persisted
machine-local token key and the existing public books.m7.rs endpoint.

Assisted-by: pi (gpt-5.6-sol)
2026-08-17 00:39:15 -03:00
Gabriel Fontes e93143e18f feat(merope/books): expose Calibre-Web publicly
Assisted-by: pi (gpt-5.6-sol)
2026-08-17 00:26:51 -03:00
Gabriel Fontes 81d93014c0 feat(hosts/alcyone/mail): add calibre mail 2026-08-16 23:48:05 -03:00
Gabriel Fontes 2506a3fa31 fix(merope/books): allow Readarr to move SABnzbd downloads
Assisted-by: pi (gpt-5.6-sol)
2026-08-16 23:45:14 -03:00
Gabriel Fontes 314ccdc9bb feat(merope/books): add Readarr and Calibre-Web stack 2026-08-16 22:51:29 -03:00
Gabriel Fontes 8bae9367ef fix(merope/sabnzbd): make completed downloads world-readable
Assisted-by: pi (gpt-5.6-sol)
2026-08-16 22:08:21 -03:00
Gabriel Fontes f42f603068 fix(electra): configure wireless interface explicitly
Assisted-by: pi (gpt-5.6-luna)
2026-08-14 15:18:04 -03:00
Gabriel Fontes 090a94b268 fix(system-manager): remove evaluation-time package reads
Assisted-by: pi (gpt-5.6-sol)
2026-08-14 15:00:06 -03:00
Gabriel Fontes 8db3f50867 fix(nixos/kdeconnect): scope ports to the tailnet
1714-1764 is a wide range to leave open on machines that join networks they
don't control.

Assisted-by: pi (claude-opus-5)
2026-08-13 17:34:49 -03:00
Gabriel Fontes baef0c6c8a fix(ubuntu/network): wait for any interface, not all
The generic ethernet rule makes an unplugged enp44s0 managed, so
networkd-wait-online blocked on it for the full 120s timeout and delayed
wg-quick@wg0. NixOS pairs its generic DHCP networks with --any for the same
reason.

Assisted-by: pi (claude-opus-5)
2026-08-13 17:16:15 -03:00
Gabriel Fontes c19ed9a3a7 fix(hosts/wireless): disable Wi-Fi Direct
Its control socket never gets ctrl_interface_group applied (upstream never
copies the field when creating the P2P device), so with the daemon running as
root the socket is root-only. wpa_gui scans the directory, hits p2p-dev-*
first and gives up before reaching the real interface.

Assisted-by: pi (claude-opus-5)
2026-08-13 17:07:51 -03:00
Gabriel Fontes cc5bd4b3c8 refactor(hosts): rename system-manager to ubuntu
The tree is heavily Ubuntu-coupled: masking netplan's generator and Ubuntu's
own wpa_supplicant units, apt packages in cloud-init, /etc/dbus-1 policy.

Assisted-by: pi (claude-opus-5)
2026-08-13 16:44:51 -03:00
Gabriel Fontes 4da7e34d42 refactor(hosts): move shared config into hosts/common
Assisted-by: pi (claude-opus-5)
2026-08-13 16:37:39 -03:00
Gabriel Fontes fc9ab65dd1 fix(hosts/wireless): disable wpa_supplicant hardening
The sandbox binds secretsFile into the unit's namespace, so a secret that
can't be decrypted kills the daemon along with the control socket needed to
connect by hand and fix it. It also breaks wpa_gui (nixpkgs#475438).

Tradeoff: the daemon runs as root again, as it does on most distros.

Assisted-by: pi (claude-opus-5)
2026-08-13 16:36:09 -03:00
Gabriel Fontes a587e539e9 feat(system-manager): port networking.wireless and generic DHCP
Run the upstream NixOS wpa_supplicant module under system-manager through a
compat shim. system-manager already reuses nixpkgs' systemdUtils,
environment.etc and userborn, so the unit, the generated config and the
wpa_supplicant user come out unchanged; only four NixOS-only options needed
stubbing, two of which are re-expressed on top of environment.etc.

Extract the wireless config to hosts/wireless.nix so both trees share one
source: they sit at the same depth, so every path resolves identically.
electra's generated wpa_supplicant.conf is now byte-identical to atlas'
except for the country line.

Take over networkd from netplan on electra with the same generic match rules
NixOS uses, which also covers ethernet and USB tethering, neither of which
was configured before. netplan is neutered by masking its generator rather
than uninstalling it, since purging netplan.io would take cloud-init,
ubuntu-minimal and ubuntu-server-minimal with it.

Assisted-by: pi (claude-opus-5)
2026-08-13 16:36:09 -03:00
Gabriel Fontes 91005fcfb9 chore(hosts): move wifi secrets from nixos to hosts 2026-08-13 15:09:51 -03:00
Gabriel Fontes 2b8b2d9c71 feat(hosts/electra): declare uidmap deb 2026-08-13 14:26:55 -03:00
Gabriel Fontes 6e5547495d feat: rename mgc to electra, add to schemes 2026-08-12 02:42:14 -03:00
Gabriel Fontes 1ad2dd7564 fix(hosts/merope/sabnzbd): drop MemoryHigh, register for oomd swap kills
MemoryHigh is enforced by reclaim, and reclaiming anonymous pages means
writing them to swap. Holding sabnzbd near 4G resident therefore pushed 5.2G
into the 8G swapfile: the limit did not contain the growth, it converted a
RAM problem into swap exhaustion, which is the failure that actually
threatens the box. Without it sabnzbd grows in RAM, where oomd's pressure
rule can see it stall.

ManagedOOMSwap=kill registers this unit alone as a swap-monitoring
candidate. oomd ranks swap candidates by usage, so scoping it here means the
early trigger at 90% swap can only ever select sabnzbd, while jellyfin's
cold pages stay ineligible. The pressure rule on system.slice remains the
broader net for anything else.

Assisted-by: pi (claude-opus-5)
2026-08-11 10:12:46 -03:00
Gabriel Fontes 06c59fbf2a feat(hosts/merope): replace earlyoom with systemd-oomd
earlyoom only acts when available memory AND free swap are both under
threshold. During the 2026-08-11 swap exhaustion, swap crossed 5% at 02:14
but RAM did not until 06:10, so it watched the swapfile drain to zero for
four hours. It then sent 88 SIGTERMs without landing a kill: SIGKILL
escalation is gated at half the threshold (2.5%), RAM bottomed at 4.74%,
and sabnzbd's graceful shutdown took 12 minutes under thrash.

systemd-oomd was already running but monitoring zero cgroups, since every
unit defaults to ManagedOOMSwap=auto. It acts on PSI stall time, which does
not distinguish waiting on reclaim from waiting on swap I/O, and it SIGKILLs
the cgroup rather than negotiating. Candidates are ranked by reclaim
activity, so a service holding cold pages in swap is picked last rather
than first.

Set on system.slice directly because systemd.oomd.enableSystemSlice
hardcodes ManagedOOMSwap=kill, which would rank jellyfin's ~1.2G of cold
swap ahead of whatever is actually thrashing.

Assisted-by: pi (claude-opus-5)
2026-08-11 10:10:16 -03:00
Gabriel Fontes 61e044f1fa fix(hosts/merope/sabnzbd): restart after out-of-memory kills
earlyoom SIGTERMed sabnzbd at 06:10 after it grew to 4.2G resident plus
5.2G of swap, exhausting the box's 8G swapfile. sabnzbd caught the signal,
saved its queue and exited 0 twelve minutes later, and with Restart=no it
stayed dead for the next three and a half hours.

Restart=on-failure would not have helped: the graceful shutdown path is a
clean exit.

Assisted-by: pi (claude-opus-5)
2026-08-11 10:02:48 -03:00
Gabriel Fontes 8869064076 chore(hosts/merope/sabnzbd): drop timeout setting
No need to, I thought the issue was server timeout, but it actually was unaiv. artc.
2026-08-10 23:42:27 -03:00
Gabriel Fontes f884d8e087 fix(hosts/merope/sabnzbd): fix paths 2026-08-10 23:16:38 -03:00
Gabriel Fontes 9c5a9b074b fix(hosts/merope/sabnzbd): trim article retry cost
Every server was on defaults: timeout=60, and max_art_tries=3 across six
servers. That is up to eighteen rounds, each willing to wait a minute,
before SAB concludes an article is gone. Nothing legitimate takes that
long -- TLS handshakes to all six hosts complete in under a second.

Drop timeout to 20s and max_art_tries to 2. This is a straightforward
reduction in the cost of the failure path, not a fix for any particular
incident.

Explicitly not addressed: a stall found while investigating, where the
queue sat at ~0.13 MB/s with every server idle -- eweka and blocknews
holding ten established connections each with no article assigned to any
of them, and frugal at zero of thirty. Nothing was requesting articles at
all. An earlier draft of this commit blamed connections blocked on
timeouts and marked the blocknews pair optional; the connection dump
showed those sockets were idle rather than busy, so both the mechanism and
the remedy were wrong and have been dropped. Root cause still unknown.

Assisted-by: pi (claude-opus-5)
2026-08-10 23:13:42 -03:00
Gabriel Fontes 4d28bb9a29 fix(hosts/merope/sabnzbd): deprioritise cpu and io
par2/unrar routinely take a full core each and the media disk hits 83%
utilisation during a grab, both competing with jellyfin direct play and
library scans. sabnzbd was still at the default weight of 100, tied with
jellyfin, while the *arrs sit at 50.

Drop it to 20 so its pipeline yields under contention and keeps the whole
box when nothing else wants it. Children inherit the cgroup weight, so
this covers par2 and unrar without touching sabnzbd's own nice setting.

Assisted-by: pi (claude-opus-5)
2026-08-10 22:03:26 -03:00
Gabriel Fontes e3673ed9cd fix(grafana): use dashboard range for disk discovery
Assisted-by: pi (gpt-5.6-sol)
2026-08-10 20:49:36 -03:00
Gabriel Fontes e14a866cce fix(grafana): hide hosts dashboard variables
Assisted-by: pi (gpt-5.6-sol)
2026-08-10 20:24:33 -03:00