Commit Graph
100 Commits
Author SHA1 Message Date
Gabriel Fontes 532a35ba7a docs(todo): drop the merope reorg plan, it is done
/srv/media is now a single btrfs subvolume on the media disk holding the
libraries and both staging trees, so imports are same-filesystem renames.
426 torrents were re-pointed through the deluge UI and are seeding without
a recheck, and the M.2 went from 164G to 39G used.

The layout is described by hardware-configuration.nix, deluge.nix and
sabnzbd.nix better than prose could, so the plan has nothing left to say.

Two follow-ups it was carrying, recorded here rather than kept as a file:
copying across filesystems flattened every reflink between the music
library and the torrent payloads, so those trees hold 170 GiB where they
held ~124 and duperemove would reclaim it; and the media filesystem has
still never been scrubbed.

Assisted-by: pi (claude-opus-5)
2026-08-10 01:17:27 -03:00
Gabriel Fontes 517bde3aa7 feat(hosts/merope): update media mountpoints 2026-08-10 01:08:54 -03:00
Gabriel Fontes 4feb27b4f6 docs(todo): rewrite as status and actions
Both documents had accreted a running commentary -- findings, then
corrections to those findings, then corrections to the corrections. That
is a changelog, and it is the wrong shape for something read mid-migration
at 2am.

Rewritten to carry only current state and what remains to be done. The
boot-chain work, the swap incident, the USB 2.0 discovery and the
successive revisions of the throughput numbers are all gone; git holds
that history if anyone wants it.

The migration is now a runbook rather than a description: what can be done
in advance without downtime, what needs the media stack stopped and
roughly how long each step takes, and what must wait until the result is
proven.

Documents the reflink structure between music and torrents, measured
rather than assumed. Both trees cost ~124 GiB today and ~170 GiB once
copied, because reflinks cannot cross filesystems. Sharing is whole-file
and strictly bimodal -- no file is fragmentarily shared, and the 23% that
sit at 90-99% differ only by a ~115 KiB tag block Lidarr rewrote after
importing -- so duperemove recovers nearly all of it afterwards. Warns
against file-level duplicate finders, which compare content rather than
extents and would trade 564 seeds for zero bytes. Replaces a fabricated
claim that music was heavily hardlinked: it has none.

Drops host SSH keys from the backup set, where they were listed as tier 1
on the false premise that sops needs them. Every creation rule already
lists the GPG key as a recipient beside the per-host age keys.

Corrects the claim that alcyone needs no dumps: firefly and headscale are
both sqlite there, and only its postgres (roundcube) is excluded.

Assisted-by: pi (claude-opus-5)
2026-08-09 23:46:04 -03:00
Gabriel Fontes f58c76ac59 fix(global/greetd): drop stray backslash in tuigreet command
The escape came from tuigreet's shell-quoted README example. Nix needs
no escape here, so the backslash reached greetd.toml and its command
lexer rejected \$ as an unknown escape sequence -- greetd has failed to
start on every host without regreet since e4fa2194 (2026-06-16).

Assisted-by: pi (claude-opus-5)
2026-08-09 22:11:47 -03:00
Gabriel Fontes 095e5783ff fix(hosts/merope): set neededForBoot on /firmware 2026-08-09 21:57:35 -03:00
Gabriel Fontes c7173939ba feat(merope): manage the pi firmware partition declaratively
The firmware partition now lives on a microSD card instead of the M.2, and
nixos-hardware repopulates it on every switch.

Two reasons for moving it off the M.2. It sits inside the Argon case, so a
bad bootloader meant opening hardware to recover; on removable media that
becomes a card swap in any laptop. And the partition was 30M, which cannot
hold the GPU blobs, device trees and overlays the install script places.

Boot chain, for the record, since none of it is reproducible from this
flake: the EEPROM boots SD first (BOOT_ORDER=0xf41) and excludes the two
data drives by VID:PID, so the media disk can occupy a USB 3 port without
hanging the bootloader. Verified booting from the card on 2026-08-09 by
setting gpu_mem on the SD copy of config.txt and watching MemTotal drop
174 MiB -- the firmware exposes no boot-source to Linux, because U-Boot
rebuilds /chosen and discards it.

config.txt is now generated. Relative to the file it replaces it keeps
arm_64bit, enable_uart and disable_overscan, carries avoid_warnings over
explicitly, and picks up the nixos-hardware defaults -- including
arm_boost, which lifts this board (Pi 4B rev 1.4) from 1.5 to 1.8 GHz.

It drops enable_gic and armstub=armstub8-gic.bin. Both are vestigial on
current Pi 4 firmware, and the install script does not copy the armstub
binary, so declaring it would point config.txt at a file that is absent.

U-Boot is pinned to ubootRaspberryPi4_64bit rather than the module default
ubootRaspberryPiAarch64: it is the exact binary verified booting today,
when replacing U-Boot 2021.04 fixed a boot failure where the legacy
distro_bootcmd only ever tried USB device 0.

Assisted-by: pi (claude-opus-5)
2026-08-09 20:53:22 -03:00
Gabriel Fontes ae200e651e chore(flake): bump nixos-hardware for rpi firmware module
Adds raspberry-pi/common/firmware.nix (upstream 2026-07-10, cross-compile
fix 07-14), which installs the Pi firmware partition from the flake on
every switch instead of leaving it hand-populated.

Also drops raspberry-pi/4/cpu-revision.nix upstream. That overlay
hardcoded linux,revision = 0x00d03114; merope reports d03114 from the
firmware DTB regardless, so removing it changes nothing here.

Assisted-by: pi (claude-opus-5)
2026-08-09 20:41:58 -03:00
Gabriel Fontes b5e059f96b docs(merope): correct disk plans, record boot fix
Device letters on merope follow USB enumeration order and are not stable;
they changed three times on 2026-08-08/09 as drives were re-plugged. Both
documents now address disks by label throughout.

Corrections to the migration plan:

- The ~28 MiB/s attributed to the media disk was a saturated USB 2.0 link,
  not a seek-bound spindle. On USB 3 it sustains 183 MB/s, 6.5x faster.
  Sizing arguments were built on the wrong number.
- The 04:42 reset was a UAS device reset on the M.2 bridge (174c:2362),
  not swap: swap stayed flat at 2.27 GiB with 4.4 GiB available. The
  watchdog held it to 105s against 48min the previous day.
- Both drives now sit on the VL805 SuperSpeed side, so they share one
  PCIe lane rather than having independent paths.

Boot, resolved 2026-08-09 in two stages, both firmware-level and
therefore invisible to nixos-rebuild:

- EEPROM walks USB in port order and looped when the drive in 2-1 had no
  bootable partition. Excluded both data drives by VID:PID. Note
  'rpi-eeprom-config --apply' is unusable on NixOS, since the wrapper
  points FIRMWARE_ROOT at a nonexistent path; build the image with
  --config and stage pieeprom.upd/.sig by hand.
- U-Boot 2021.04 predates bootstd and declares USB as a single instance,
  so boot_targets held one usb0 and only devnum=0 was ever tried.
  Replaced with 2026.04, which enumerates every bootdev. Old binary kept
  as u-boot-rpi4.bin.bak.

Backups doc records that the M.2 has 2233 corruption errors and has never
been scrubbed, so they were found passively rather than by verification.
Music (92 GB, the only irreplaceable data there) has been copied to the
backups disk; a scrub is now the open item.

Assisted-by: pi (claude-opus-5)
2026-08-09 12:16:39 -03:00
Gabriel Fontes 3cfb0d440a docs: add backup plan
No backup tooling exists anywhere in the flake today, and sdc1 is a
932GB disk holding 5.9MB.

Covers what is actually irreplaceable -- firefly, radicale, deluge
state, mail, git, immich, music -- rather than the 4.6TB of re-acquirable
video, and specifies database dumps rather than live file copies, which
would otherwise produce backups that silently will not restore.

Mail and calendars are already replicated across three hosts, so they are
included for history rather than redundancy: sync propagates deletion,
and the useful protection there is retention depth, not another copy.

Assisted-by: pi (claude-opus-5)
2026-08-07 23:46:26 -03:00
Gabriel Fontes 031f2a4e0e docs(merope): add disk reorganization plan
Consolidates all media onto sdb in a single btrfs subvolume mounted at
/srv, so every import becomes a rename or hardlink rather than a
cross-device copy, and the disk holding the swapfile sees no media I/O.

Written up after the 2026-08-07 stall, whose root cause was swap
contending with a Radarr import on the same device.

Assisted-by: pi (claude-opus-5)
2026-08-07 23:29:19 -03:00
Gabriel Fontes 401c2ad12c feat(alcyone/grafana): add Swap to RAM Usage, add Disk IO panel
Fold back UI edits from the hosts dashboard:

- RAM Usage: rename the used-memory series to "Memory", add a "Swap"
  series (SwapTotal - SwapFree), and retarget the configFromData
  transformation from byFrameRefID to byType so Total Memory applies as
  max to both series. Show the legend, since there are now two series.
- Rename the Storage gauge to "Disk Usage".
- New "Disk IO" timeseries: rate of node_disk_io_time_weighted_seconds_total,
  joined against node_btrfs_info via label_replace to strip the partition
  suffix, so series are labelled by filesystem label.

The UI export predated d9a6c23e and would have reverted its Storage
query and threshold fixes; those hunks were dropped rather than folded.

Assisted-by: pi (claude-opus-5)
2026-08-07 23:27:55 -03:00
Gabriel Fontes d9a6c23e55 fix(alcyone/grafana): correct Storage gauge thresholds and queries
The top threshold step rendered a 100%-full filesystem in plain text
colour rather than red, so the alarm state was unreachable -- red only
covered 90-99.99%.

Both targets pulled a full range while groupingToMatrix keeps one value
per cell, so switch them to instant queries.

Join on (instance, uuid) rather than uuid alone; with uuid-only matching,
a multi-value $hosts could cross-match filesystems between hosts.

Assisted-by: pi (claude-opus-5)
2026-08-07 21:48:20 -03:00
Gabriel Fontes e5e1048a99 fix(merope): replace Nice with cgroup weights, switch to BFQ
The 2026-08-07 stall presented as a CPU problem but was I/O underneath:
a Radarr cross-disk import saturated the USB SSD holding both the rootfs
and the swapfile, and everything else queued behind it.

The existing Nice values were aimed at the wrong thing twice over. They
only ever addressed CPU scheduling, which was the symptom -- renicing a
process blocked in D-state achieves nothing. They were also a layer too
low: with the cgroup v2 cpu controller enabled, CPU is split between
service cgroups by weight, and every service on this host sat at an
identical 100. Nice only reorders tasks within a cgroup, so a lone
service being polite to itself changed nothing.

Replace them with CPUWeight/IOWeight, which is the layer that actually
arbitrates between services. Nice and IOSchedulingPriority are dropped
rather than kept alongside: nothing here escapes the cpu controller, and
ioprio depends on BFQ exactly as IOWeight does, so it is not a fallback
for anything -- if BFQ is absent both are inert together.

deluged is included despite never having had a Nice; it writes to the
same device continuously and was the other half of the load that day.
recyclarr loses its Nice without replacement -- it only makes a handful
of API calls to the *arrs and never contends for anything.

Requires BFQ. All three disks were on mq-deadline, which has no notion of
I/O weights at all and would have made IOWeight a silent no-op.

Assisted-by: pi (claude-opus-5)
2026-08-07 21:00:23 -03:00
Gabriel Fontes 6338b93a5d feat(home/hyprland): invert focus_on_activate
Almost everything that requests activation is being rude about it, and with
follow_mouse on it turns into a focus fight. Default to off and opt browsers
back in, so opening a link from another app still raises the browser.

Makes the LinOffice focus rule redundant; the maximize suppression stays,
since that one is Excel being Excel rather than a focus policy.

Assisted-by: pi (claude-opus-5)
2026-08-07 20:43:09 -03:00
Gabriel Fontes 09b4856163 feat(home/hyprland): tame LinOffice RemoteApp windows
FreeRDP RemoteApp windows mirror the Windows-side focus state, so Office
requests activation constantly. With focus_on_activate and follow_mouse both
on, that ping-pongs: 22 unprompted focus flips in 30s between Excel and the
terminal under the cursor, which also made clicks land on a window that was
mid-flip. Each activation carries a maximize request too, which read as the
window spontaneously fullscreening whenever it regained focus.

Assisted-by: pi (claude-opus-5)
2026-08-07 20:39:43 -03:00
Gabriel Fontes e5ae4001e3 fix(merope): bound stalls and stop swapping onto the contended disk
merope hung for ~48min (16:59-17:56 on 2026-08-07) with all four cores
pinned. No OOM, no panic, no thermal event -- it simply stopped making
progress and needed a manual power cycle.

Root cause: /swap/swapfile lives on sda, the same USB SSD as the rootfs.
swappiness=60 had ratcheted ~4.2G of cold anonymous pages onto it over
days (monotonically -- it never came back). A torrent write plus a Radarr
cross-disk import then saturated that device at ~85MiB/s, so page-ins
queued behind bulk I/O indefinitely. Nothing exceeded a memory limit, so
the kernel OOM killer never engaged.

All four USB ports share one VL805 controller on a single PCIe lane, so
relocating swap to another disk would not have given it an independent
path.

- watchdog: bound any future stall to a reboot instead of a silent hang
- ondemand: merope never exceeded 600MHz of its 1500MHz in 4h of 100% load
- swappiness=10: MemAvailable never fell below 3.6G, swap is not
  load-bearing here; keep the swapfile as a backstop, not a habit
- psi=1 + earlyoom: the kernel was not even measuring stall time

Assisted-by: pi (claude-opus-5)
2026-08-07 18:23:09 -03:00
Gabriel Fontes 264ce2d6f3 feat(home/productivity): add mgc account 2026-08-07 14:36:42 -03:00
Gabriel Fontes c76aaec68f refactor(home/productivity): make accounts opt-in 2026-08-07 14:30:49 -03:00
Gabriel Fontes ec52586ed2 fix(home/todoman): fallback to null if no todo supporting calendar present 2026-08-07 11:35:34 -03:00
Gabriel Fontes 97ec061da1 feat(home/mail): don't gpg sign by default, remove gpg key from signature 2026-08-07 11:27:15 -03:00
Gabriel Fontes c6d8944b03 chore(home): get rid of syncthing 2026-08-07 11:26:26 -03:00
Gabriel Fontes 2743828f22 chore(home/waybar): remove unused modules 2026-08-07 11:25:26 -03:00
Gabriel Fontes 16b2eb0241 feat(home/waybar): improve ubuntu logo 2026-08-07 11:22:39 -03:00
Gabriel Fontes bc0eabb66c fix(home/hyprland/hypridle): add floor to brightness
Required for intel panels, as their 0 is truly 0
2026-08-07 11:14:43 -03:00
Gabriel Fontes 5091348ab9 feat(hosts/mgc): install wireguard-tools
Used for systemctl enable wg-quick@wg0
2026-08-07 07:54:03 -03:00
Gabriel Fontes 1bfb3c687d fix(jagex-auth): use wrapped package name 2026-08-07 02:22:19 -03:00
Gabriel Fontes 400bc2021b fix(home/ai): gate runelite-mcp behind runelite package 2026-08-07 02:16:30 -03:00
Gabriel Fontes 52c180d4ae feat(home/pi): bump opus and sonnet to 5 2026-08-07 02:12:34 -03:00
Gabriel Fontes 18e1db5b05 chore(hosts/sm): disable hydra autoupgrade for now 2026-08-07 01:55:39 -03:00
Gabriel Fontes e41292b51d feat(home/mgc): add basic mgc clis 2026-08-07 01:54:26 -03:00
Gabriel Fontes d42a4fb8a2 fix(home): get rid of signing by default 2026-08-07 01:43:07 -03:00
Gabriel Fontes 3ad46d1523 refactor(system-manager): extract unix_chkpwd wrapper into a module
The setuid /run/wrappers/bin/unix_chkpwd wrapper is generic plumbing that
nixpkgs' pam_unix requires (system-manager, unlike NixOS, doesn't provide
it), not host policy. Move it to modules/system-manager/unix-chkpwd.nix,
applied unconditionally like NixOS does; hosts/.../pam.nix keeps only the
pam.d stacks.

Assisted-by: pi (claude-opus-4-8)
2026-08-07 01:14:11 -03:00
Gabriel Fontes 7aac214925 refactor(system-manager): add nix.registry module, use it in nix.nix
Port nixpkgs' nix.registry option (config/nix-flakes.nix) into
modules/system-manager/nix-registry.nix, giving system-manager the same
interface NixOS has. common/global/nix.nix now just sets
'nix.registry = mapAttrs (_: flake: {inherit flake;}) flakeInputs' like
the NixOS side, instead of hand-writing /etc/nix/registry.json.

Assisted-by: pi (claude-opus-4-8)
2026-08-07 01:13:56 -03:00
Gabriel Fontes a4e5e7dedb fix(system-manager): put flake inputs (incl. self) on the nix registry
system-manager's nix module has no nix.registry option (NixOS does), so
generate /etc/nix/registry.json directly, mirroring nixpkgs' generator.
The hydra auto-upgrade's 'nix flake metadata self' needs 'self' resolvable
from the system registry; this also registers the other inputs (nixpkgs,
etc.) for ad-hoc use, matching the NixOS side.

Assisted-by: pi (claude-opus-4-8)
2026-08-07 00:56:31 -03:00
Gabriel Fontes a186eb89e5 feat(home/mgc): use another wallpaper 2026-08-07 00:44:08 -03:00
Gabriel Fontes fb3a0b6c92 fix(runelite-mcp): pin RuneLite to 1.12.35, regen deps lockfile
build.gradle used 'latest.release', a dynamic version, so the Gradle
deps lockfile captured an ephemeral net.runelite snapshot
(client 1.12.34-20260721…-SNAPSHOT). repo.runelite.net prunes old
snapshots, so fetchDeps 404'd on re-fetch. Pin to the current concrete
release (1.12.35) and regenerate deps.json — releases aren't pruned, so
the FOD stays reproducible.

Assisted-by: pi (claude-opus-4-8)
2026-08-07 00:38:40 -03:00
Gabriel Fontes d54bb50ceb feat(taygeta): switch to a violet-magenta wallpaper
Its old nebula-cyan-yellow (seed #0a2a4a, blue 210) clashed with
merope's towers-ice (blue 213). Switch to samurai-cyberpunk-pink-green
(seed #a63ee1), a vivid violet-magenta that fills the empty ~280 gap and
sits clear of every other home's seed color.
2026-08-07 00:30:28 -03:00
Gabriel Fontes 40a57ba37b feat(mgc): give mgc its own teal wallpaper
Stop reusing maia's deer-lunar-fantasy; switch to
cubist-crystal-brown-teal (seed #1acdaf), a vivid teal that fills the
empty teal/cyan gap and is distinct from every other home's seed color.
2026-08-07 00:28:00 -03:00
Gabriel Fontes 45ce0e8031 feat(mgc/install): set up Tailscale apt repo + install during bootstrap
hydra.m7.rs is tailnet-gated (write-enabled, so locked down), so mgc needs
Tailscale to reach it for the hosts.mgc auto-upgrade. Add Tailscale's
official apt repo + signing key and install it in the Ubuntu-prereqs
stage, keeping it apt-managed (updates via apt, up on boot, independent of
the nix config). Joining the tailnet (tailscale up) stays a manual step.

Assisted-by: pi (claude-opus-4-8)
2026-08-07 00:19:16 -03:00
Gabriel Fontes e4d9acfc1f refactor(flake): drop the system-manager app; bootstrap via config scripts
The CLI now ships on the system PATH (environment.systemPackages), so the
apps.system-manager passthrough is redundant. install.sh's bootstrap (the
one caller) instead builds the systemConfig and runs its own
bin/register-profile + bin/activate, needing no flake app.

Assisted-by: pi (claude-opus-4-8)
2026-08-07 00:06:58 -03:00
Gabriel Fontes 2a362b3913 feat(system-manager/mgc): enable hydra auto-upgrade
Mirror hosts/nixos/common/global/auto-upgrade.nix for the system-manager
side: enable system.hydraAutoUpgrade against hydra.m7.rs (foundry/main),
job hosts.mgc. Disable the timer when the flake is dirty.

Assisted-by: pi (claude-opus-4-8)
2026-08-07 00:05:08 -03:00
Gabriel Fontes 5bd536014a feat(hydra): build systemConfigs as hosts jobs
Fold outputs.systemConfigs into the hydra 'hosts' jobs alongside
nixosConfigurations, so Hydra builds mgc's system-manager generation as
hosts.mgc (matching the hosts.<name> convention the auto-upgrade uses).

Assisted-by: pi (claude-opus-4-8)
2026-08-07 00:03:04 -03:00
Gabriel Fontes b3d166215c feat(system-manager): hydra-based auto-upgrade module
Port modules/nixos/hydra-auto-upgrade.nix to the system-manager side as
modules/system-manager/hydra-auto-upgrade.nix, exposed via a new
outputs.systemManagerModules and imported by the common/global base.

The script (cached-system-manager) mirrors cached-nixos-rebuild, adapted
to system-manager's model: there is no /run/current-system active pointer
and no boot-time reactivation, so the registered profile
(/nix/var/nix/profiles/system-manager-profiles/system-manager) is the
single source of truth. Only 'diff' and 'switch' actions; switch does
register-profile + activate (the CLI's switch = both, atomically).

Not enabled anywhere yet.

Assisted-by: pi (claude-opus-4-8)
2026-08-06 23:57:08 -03:00
Gabriel Fontes 99373cf795 fix(system-manager): don't kill greetd session on every switch
greetd had Requires=userborn.service, but System Manager restarts the
oneshot userborn.service on every activation. The Requires propagates
that restart to greetd, which tears down the running Wayland session
(then Restart=always bounces it back to the greeter) on every switch.

Relax to Wants=userborn.service, keeping After= for ordering: userborn
still runs before greetd at boot, but its per-activation restart no
longer drags greetd (and the live session) down. X-RestartIfChanged=false
already prevented greetd's own restart; this closes the dependency path.

Assisted-by: pi (claude-opus-4-8)
2026-08-06 23:31:10 -03:00
Gabriel Fontes a4161de3bb feat(system-manager): add system-manager CLI to environment.systemPackages
Put the CLI on the system PATH (/run/system-manager/sw/bin), like
nixos-rebuild is on NixOS, instead of 'nix run .#system-manager'. Pinned
to the flake's system-manager input, matching what builds the host.

Assisted-by: pi (claude-opus-4-8)
2026-08-06 23:31:10 -03:00
Gabriel Fontes 8e9a95a71b refactor(system-manager): split PAM out of greetd, dedup auth, trim nix.nix
Move the unix_chkpwd security wrapper and the greetd/hyprlock pam.d
stacks out of greetd.nix into a dedicated pam.nix (greetd.nix keeps just
the greeter service/user/session-wrapper). Factor the shared auth block
into one binding. The generated pam.d/* and the wrapper are byte-identical
before and after.

Also drop two no-op nix.settings: build-users-group (already Nix's
default 'nixbld') and the @wheel trusted-user (Ubuntu has no wheel group).

Assisted-by: pi (claude-opus-4-8)
2026-08-06 21:19:09 -03:00
Gabriel Fontes 22d391716e fix(system-manager): self-contained hyprlock PAM auth stack
hyprlock's pam.d did 'include login' too, which nix's linux-pam can't
parse (Debian @include). Give it a minimal self-contained auth stack on
nix's pam_unix, matching greetd. hyprlock runs unprivileged, so pam_unix
verifies via the setuid /run/wrappers/bin/unix_chkpwd added alongside the
greetd fix.

Assisted-by: pi (claude-opus-4-8)
2026-08-06 19:47:01 -03:00
Gabriel Fontes 8b0bdcbd8a fix(system-manager): self-contained greetd PAM stack + unix_chkpwd wrapper
Our pam.d/greetd did 'include login', chaining into Ubuntu's
/etc/pam.d/{login,common-*}, which is built on Debian's @include
extension. greetd links nix's linux-pam, whose config parser only knows
'include'/'substack' (no @include), so it errored 'illegal module type:
@include' and greetd never authenticated on tty1.

nix's libpam also can't dlopen Ubuntu's modules (pamtester reports
'Module is unknown'), so the stack uses nix's own modules by absolute
path, mirroring Ubuntu's expanded common-auth/account/session: pam_unix
for auth/account/session, pam_loginuid/pam_env, and nix pam_systemd to
register the logind session (XDG_RUNTIME_DIR, seat) the Wayland session
needs.

nixpkgs' pam_unix execs its verification helper from the fixed path
/run/wrappers/bin/unix_chkpwd; without it, pam_unix auth silently fails
even though the store binary works. Add a setuid unix_chkpwd security
wrapper (as NixOS does), which fixes pam_unix for both root callers
(greetd) and unprivileged ones (hyprlock).

Assisted-by: pi (claude-opus-4-8)
2026-08-06 19:47:01 -03:00
Gabriel Fontes 7442f58ac4 fix(system-manager): move greetd session-wrapper to a script
greetd 0.10's command tokenizer rejects the POSIX '\'' quoting that
lib.escapeShellArgs emits for the inline `bash --login -c 'exec "$@"' --`
session-wrapper argument, failing at config parse time with
'configuration error: illegal character'. greetd then crash-loops and
tty1 stays blank (login only worked from another tty).

Move the wrapper into a writeScript with a `bash --login` shebang and
pass its path to --session-wrapper. Same semantics (session runs in a
login shell so /etc/profile is sourced), but the command string no
longer contains nested shell quoting greetd can't parse.

Assisted-by: pi (claude-opus-4-8)
2026-08-06 17:23:03 -03:00
Gabriel Fontes 76bbb1905b feat(home/mgc): source POSIX profile.d env in fish via babelfish
System Manager and the upstream Nix installer only ship POSIX
/etc/profile.d fragments to set PATH/XDG_DATA_DIRS (SM fish support is an
unfinished upstream TODO), so a bare fish shell misses /run/wrappers/bin,
/run/system-manager/sw/bin, the nix default profile, and
/run/system-manager/sw/share. Translate the two relevant fragments with
babelfish and source them; guard on the SM bin dir so it is a no-op when
the env was already inherited (e.g. the bash-wrapped graphical session).

Assisted-by: pi (claude-opus-4-8)
2026-08-06 17:21:40 -03:00
Gabriel Fontes f6c0af14f4 fix(system-manager): replaceExisting on greetd/hyprlock pam files
A prior System Manager activation can leave our /etc/pam.d/{greetd,hyprlock}
symlinks on disk without recording them in its etc-files state, so a
re-activation sees them as unmanaged and aborts. Set replaceExisting so it
backs the stale file up to <path>.system-manager-backup and relinks instead.

Scoped to the two files we declare: Ubuntu ships no greetd/hyprlock, and
System Manager's own generated entries (nix.conf already carries upstream
replaceExisting; profile.d, environment.d) don't need our intervention.

Assisted-by: pi (claude-opus-4-8)
2026-08-06 17:21:40 -03:00
Gabriel Fontes 1ff1558a05 feat(home/mgc): declare eDP-1 monitor
1920x1080@60 with 1.25 scaling, matching the declarative monitor setup used by
the other homes.

Assisted-by: pi (gpt-5.6-sol)
2026-08-06 16:18:28 -03:00
Gabriel Fontes 7c0515a20a fix(mgc): set user manager XDG_DATA_DIRS for portals
The environment.d approach set XDG_DATA_DIRS in the user manager environment
(visible in show-environment) but not in time to affect its unit search path,
which systemd fixes when `systemd --user` is exec'd -- so home-manager's
xdg-desktop-portal units under share/systemd/user still were not found. Set it
on the user@.service process instead via an /etc drop-in, which does reach the
search path (and its user D-Bus and services), and drop the now-redundant
environment.d file. Takes effect on next login.

Assisted-by: pi (gpt-5.6-sol)
2026-08-06 16:13:08 -03:00
Gabriel Fontes 7d9ca4e9ff fix(home/mgc): expose nix profile to systemd --user
Ubuntu's logind starts the systemd user manager (and user D-Bus) without the
nix profiles in XDG_DATA_DIRS, so package-shipped units like the
xdg-desktop-portal services under share/systemd/user are never discovered.
targets.genericLinux only exports XDG_DATA_DIRS to login shells, too late for
the user manager. Set it through environment.d, which systemd reads at manager
start. Takes effect on next login.

Assisted-by: pi (gpt-5.6-sol)
2026-08-06 15:56:55 -03:00
Gabriel Fontes 04130f45dd fix(home/mgc): disable productivity feature 2026-08-06 15:49:53 -03:00
Gabriel Fontes b6aba33362 fix(home/pi): harden npm-deps integrity fetch
Download each integrity tarball to a file with --fail, connect/max timeouts,
and retries instead of piping curl straight into openssl. A stalled connection
now times out and retries rather than hanging the FOD build forever, and a
failed fetch aborts instead of yielding an empty digest. Output is unchanged,
so npmDepsHash values stay the same.

Assisted-by: pi (gpt-5.6-sol)
2026-08-06 15:47:45 -03:00
Gabriel Fontes bc96ef68ad fix(home/pi): correct pi-mcp-adapter npm-deps hash
pi-mcp-adapter does have integrity-less deps, so its FOD content changed with
the real-integrity fix. The old output was reused from the store (FOD paths
derive from name+hash), masking the mismatch on hosts with a warm cache; a
clean build (e.g. mgc) failed. Update to the deterministic hash.

Assisted-by: pi (gpt-5.6-sol)
2026-08-06 15:37:43 -03:00
Gabriel Fontes 66a6db18f0 fix(home/pi): make npm-deps FODs deterministic
Pi ships some registry deps without a lockfile integrity field (pi#5653). The
old workaround backfilled every one with a single shared fake sha512, so all of
them collided on one npm cache content address and a parallel fetch race decided
which tarball won -- making pi-claude-bridge-npm-deps non-deterministic (and
serving the wrong tarball for the losers).

Instead fetch each dep's real registry integrity inside the FOD, where network
is available and prefetch-npm-deps verifies every tarball, so the output is
stable. The main build (no network) reuses the FOD's patched lockfile. Compose
with any package-specific prePatch so vendored-lockfile packages keep working.

Only pi-claude-bridge was affected; its npmDepsHash is updated. pi-mcp-adapter,
pi-btw, and rpiv-ask-user-question have complete lockfiles and are unchanged.

Assisted-by: pi (gpt-5.6-sol)
2026-08-06 15:01:28 -03:00
Gabriel Fontes d9d7e86b8f chore(mgc): add SOPS host recipient
Add the rebuilt mgc SSH host recipient, rekey shared host secrets, and let SOPS activation validate decryption rather than grepping age metadata.

Assisted-by: pi (gpt-5.6-sol)
2026-08-06 13:20:46 -03:00
Gabriel Fontes e238ee438f chore(readme): remove mgc installation notes
Assisted-by: pi (gpt-5.6-sol)
2026-08-06 13:09:54 -03:00
Gabriel Fontes 206f8269ee refactor(mgc): require an existing Foundry checkout
Drop automatic Jujutsu cloning from the bootstrap and allow an existing checkout to be selected with FOUNDRY_DIR.

Assisted-by: pi (gpt-5.6-sol)
2026-08-06 13:08:44 -03:00
Gabriel Fontes ce2c1d89cb fix(mgc): use upstream Nix installer
Replace the Determinate installer with NixOS's official multi-user installer and reject existing Determinate installations instead of silently adopting them.

Assisted-by: pi (gpt-5.6-sol)
2026-08-06 12:54:26 -03:00
Gabriel Fontes 0f1c528887 fix(mgc): keep wpa_supplicant networking
Keep Ubuntu's networkd/wpa_supplicant stack, install wpa_gui through Home Manager, and grant gabriel access through the netdev group.

Assisted-by: pi (gpt-5.6-sol)
2026-08-06 12:43:46 -03:00
Gabriel Fontes 55207e59cf feat(mgc): add Ubuntu bootstrap script
Bootstrap Ubuntu runtime services and Determinate Nix, clone Foundry with Jujutsu, print the host age recipient, and gate System Manager activation on SOPS rekeying.

Assisted-by: pi (gpt-5.6-sol)
2026-08-06 11:20:33 -03:00
Gabriel Fontes 279be89bd6 refactor(mgc): declare fish as user shell package
Let System Manager install Fish and derive its runtime shell path from the package while bypassing the unavailable NixOS programs.fish check.

Assisted-by: pi (gpt-5.6-sol)
2026-08-06 11:09:41 -03:00
Gabriel Fontes 74fbb4c331 refactor(hosts): share API secrets across systems
Move the Pi web-search credentials to a host-wide SOPS file and declare them for both NixOS and System Manager users. Leave mgc's age recipient disabled until Ubuntu is reinstalled.

Assisted-by: pi (gpt-5.6-sol)
2026-08-06 10:54:48 -03:00
Gabriel Fontes 96f81800ac chore(hosts): remove unused secrets 2026-08-06 10:29:56 -03:00
Gabriel Fontes 2e85e5a27d feat(mgc): add Ubuntu system-manager host
- manage Nix, Mesa graphics, users, PAM glue, and greetd on Ubuntu
- activate the work Home Manager profile in the same switch
- expose all exported sessions to tuigreet through a login environment
- isolate reusable System Manager configuration under hosts/system-manager/common

Assisted-by: pi (gpt-5.6-sol)
2026-08-06 10:26:36 -03:00
Gabriel Fontes 9f449c7ea2 refactor(hosts): separate nixos and system-manager hosts
Assisted-by: pi (gpt-5.6-sol)
2026-08-06 09:53:42 -03:00
Gabriel Fontes ddf933e40f feat(home/pi): add btw and ask-user-question extensions
Assisted-by: pi (gpt-5.6-sol)
2026-08-05 18:42:10 -03:00
Gabriel Fontes 06c808ac46 chore(home/pi): remove playerctl callback guidance
Assisted-by: pi (gpt-5.6-sol)
2026-08-05 18:37:10 -03:00
Gabriel Fontes 6decb4fecb fix(home/pi): disable telemetry override
Assisted-by: pi (gpt-5.6-sol)
2026-08-05 18:07:06 -03:00
Gabriel Fontes aaf4a4764e chore(home/pi): update pi to 0.83.0
Update Pi from 0.80.6 to 0.83.0 (272 commits), using the release source archive with generated model catalogs. Align local extension types, make the default provider explicit, and allow model refreshes while disabling version checks and telemetry.

Assisted-by: pi (gpt-5.6-sol)
2026-08-05 17:53:25 -03:00
Gabriel Fontes fc523a5b1c refactor(home/pi): remove gondolin sandbox
Remove the unused VM tool wrappers, proxy configuration, dependency tree, and stale web-fetch guidance.

Assisted-by: pi (gpt-5.6-sol)
2026-08-05 17:44:33 -03:00
Gabriel Fontes 531488a546 refactor(home/pi): use built-in llama provider
Remove pi-llama-cpp, configure the upstream provider for llm.m7.rs, and qualify the router model scopes.

Assisted-by: pi (gpt-5.6-sol)
2026-08-05 17:41:14 -03:00
Gabriel Fontes 86d9794255 chore(home/pi): update claude bridge
Update pi-claude-bridge from 0.5.0 to 0.6.3 for Pi 0.82 compatibility, Opus 5 support, and corrected long-context handling.

Assisted-by: pi (gpt-5.6-sol)
2026-08-05 17:38:59 -03:00
Gabriel Fontes ed88d0da88 fix(home/pi): hide mcp status icon
Assisted-by: pi (gpt-5.6-sol)
2026-08-05 17:34:56 -03:00
Gabriel Fontes f3472759af chore(home/pi): update mcp adapter
Update pi-mcp-adapter from 2.11.0 to 2.20.1 for hardened OAuth and app isolation, modern protocol negotiation, and lifecycle fixes.

Assisted-by: pi (gpt-5.6-sol)
2026-08-05 17:30:53 -03:00
Gabriel Fontes 1b7c2e91d5 chore(home/pi): update codex image generation
Update to 0.1.12 for upstream image editing support and remove the local reference-image patch.

Assisted-by: pi (gpt-5.6-sol)
2026-08-05 17:26:42 -03:00
Gabriel Fontes e97f232361 refactor(home/cli): add jj workspace shell launcher
Replace piw with a general-purpose jjw shell launcher and diff workspace changes from the fork point so concurrent invoking-workspace edits are excluded.

Assisted-by: pi (openai-codex/gpt-5.6-sol)
2026-08-05 17:19:56 -03:00
Gabriel Fontes 593e051a77 refactor(home/pi): remove model context extension
Assisted-by: pi (gpt-5.6-sol)
2026-08-05 17:09:25 -03:00
Gabriel Fontes 5831a80bc4 refactor(home/pi): remove scoped model patch
Drop the local resolver override and qualify automated sub-agent model selection so catalog ordering cannot change worker providers.

Assisted-by: pi (gpt-5.6-sol)
2026-08-05 17:04:09 -03:00
Gabriel Fontes 2d254a89ef fix(hosts/alcyone): pass timezone to f irefly 2026-08-01 11:22:08 -03:00
Gabriel Fontes 7915284118 fix(hosts): go back to static timezone 2026-08-01 11:15:48 -03:00
Gabriel Fontes db627e50b2 refactor(pkgs): register project packages consistently
Expose website and runelite-mcp through the shared package registry, keeping flake outputs generic and host consumers on the overlaid package set.

Assisted-by: pi (openai-codex/gpt-5.6-sol)
2026-07-31 11:44:50 -03:00
Gabriel Fontes d48beac801 refactor(projects/runelite-mcp): colocate nix package
Move the package expression and Gradle dependency lock alongside the project source, and update the package set to call it there.

Assisted-by: pi (openai-codex/gpt-5.6-sol)
2026-07-31 11:44:50 -03:00
Gabriel Fontes 8db762c164 refactor(projects/website): package website from foundry flake
Remove the nested flake and Hydra jobset metadata, expose the website directly from Foundry, and point consumers at the package output.

Assisted-by: pi (openai-codex/gpt-5.6-sol)
2026-07-31 11:44:50 -03:00
Gabriel Fontes 42bae188a6 chore(pkgs/overleaf-sync): clarify Git ignore behavior
Assisted-by: pi (gpt-5.6-sol)
2026-07-31 11:44:50 -03:00
Gabriel Fontes ac4e999374 chore(pkgs/overleaf-sync): document synchronization behavior
Assisted-by: pi (gpt-5.6-sol)
2026-07-31 11:44:50 -03:00
Gabriel Fontes 69bb96d825 fix(pkgs/overleaf-sync): preserve unchanged local symlinks
Assisted-by: pi (gpt-5.6-sol)
2026-07-31 11:44:50 -03:00
Gabriel Fontes 0983fcb3be feat(pkgs/overleaf-sync): package overleaf project synchronization
Assisted-by: pi (gpt-5.6-sol)
2026-07-31 11:44:50 -03:00
Gabriel Fontes 7f06013904 fix(home/pi): preserve working directory in piw
Assisted-by: pi (openai-codex/gpt-5.6-sol)
2026-07-26 14:48:35 -03:00
Gabriel Fontes 5735c13586 feat(home/helix): configure texlab to build on save and format with latexindent 2026-07-26 13:50:20 -03:00
Gabriel Fontes 29653446c0 docs(projects/runelite-mcp): refresh roadmap
Record completed semantic-area and POH work, correct dynamic tool counts, and classify exporter-review gaps into native follow-ups, investigations, and deliberate exclusions.

Assisted-by: pi (openai-codex/gpt-5.6-sol)
2026-07-25 11:58:23 -03:00
Gabriel Fontes 2ac9db1225 fix(projects/runelite-mcp): persist confirmed POH observations
Serialize confirmed self-house object state automatically on the next game tick so it remains available after leaving even when no MCP call was made inside the house.

Assisted-by: pi (openai-codex/gpt-5.6-sol)
2026-07-25 11:51:26 -03:00
Gabriel Fontes b672bbae97 feat(projects/runelite-mcp): confirm POH ownership from teleports
Track explicit Teleport to House spell casts, unredirected house-tablet breaks, and right-click portal Home actions across a bounded loading transition. Confirm self ownership only when that transition lands inside a POH, while failed, Outside, and ambiguous portal-dialog entries remain untrusted.

Assisted-by: pi (openai-codex/gpt-5.6-sol)
2026-07-25 11:40:24 -03:00
Gabriel Fontes 35bf217a82 feat(projects/runelite-mcp): expose loaded POH state
Add a bounded get_poh_state tool backed by RuneLite POH features and object lifecycle events. Resolve instanced template regions, infer ownership only from direct UI evidence, and retain only confirmed self-house observations across scene changes.

Assisted-by: pi (openai-codex/gpt-5.6-sol)
2026-07-25 11:08:31 -03:00
Gabriel Fontes 74f4170476 feat(projects/runelite-mcp): expose RuneLite semantic areas
Reuse RuneLite's internal Discord region catalogue to add fail-closed area names and categories to live player locations without maintaining a separate geography dataset.

Assisted-by: pi (openai-codex/gpt-5.6-sol)
2026-07-25 01:30:22 -03:00
Gabriel Fontes f00a046f38 docs(projects/runelite-mcp): record account and knowledge verification
Record packaged Pi verification for bounded progression, bank, wealth, item-price, and optional Wiki workflows.

Assisted-by: pi (openai-codex/gpt-5.6-sol)
2026-07-25 01:18:48 -03:00
Gabriel Fontes 5fd3d9aada fix(projects/runelite-mcp): clarify unloaded account data
Treat unloaded collection totals as recent-only and reject RuneLite placeholder item definitions instead of reporting a string named null.

Assisted-by: pi (openai-codex/gpt-5.6-sol)
2026-07-25 01:13:29 -03:00