The Catppuccin splash showed the NixOS logo and the loading animation and
nothing else. The NixOS plymouth module links boot.plymouth.logo into every
theme directory named catppuccin-<flavour>, and the two-step module has no
alignment key for that header image, so its distance from the animation is not
the theme's to set. A theme under a name of its own is outside that loop.
moonarch-plymouth joins as a flake input; pkgs/moonarch-plymouth.nix rewrites
the descriptor's FHS ImageDir to the store path.
Images opened in Chromium although viewnior has been installed since 3e40a70:
no mimeapps.list named image/* at all, so xdg-open took the first candidate
from mimeinfo.cache. xdg.mime points gif, jpeg, png and webp at
viewnior.desktop. webp needs addedAssociations besides, which viewnior.desktop
does not declare, and webp-pixbuf-loader, without which gdk-pixbuf cannot
decode it.
stasis does not answer SIGTERM as part of the session teardown, and the default
90 seconds left the whole user manager waiting on it: niri received its own
SIGTERM only once stasis had been killed, 88 seconds in the journal.
TimeoutStopSec = 5. What it holds is an idle timer, so nothing is lost when it
goes down hard.
glab on the ThinkPad.
Two changes that cannot be separated in the result: the Firefox
preconfiguration is what made desktop.nix untenable, and the split is what it
now lives in.
Firefox replaces LibreWolf, with Betterfox as its preference base and the
settings that Betterfox does not cover expressed as policies: uBlock Origin,
the Catppuccin theme, no Pocket, no Sync, autoscroll, DuckDuckGo, no
translation offer, no password prompts, no bookmarks toolbar, about:blank as
home and new tab. Both add-ons are normal_installed and the preferences keep
status "default", so none of it is locked away — undoing settings by hand was
the reason to leave LibreWolf.
desktop.nix carried thirteen topics over 637 lines, and the existing split
followed the NixOS option namespace rather than the topic: services.pipewire
here, its configPackages there; services.gvfs and services.udisks2 apart from
their packages; Bluetooth likewise. Nine modules now each hold one topic with
its options, packages, configs and services together. desktop.nix keeps the
compositor, the /etc deployment, portals and what belongs to no topic.
The refactor changes nothing about the built system, and that was checked
after each of the eight steps rather than assumed. The plain store path does
not serve as the criterion: moving a package to another module reorders
environment.systemPackages, which changes the system-path hash and propagates
into every file naming it. What was compared instead is the package set
through diff-closures and every differing file with store hashes normalised
away. Two steps reordered the user tmpfiles list, which is neutral only
because no two rules name the same path — checked, not presumed.
moonarch replaced its direct gtklock calls with the moonarch-lock
wrapper. stasis.rune and PowerPopout.qml come from moonarchSrc, so the
new command arrived here at once while the script did not -- the
moonarch-scripts whitelist did not carry it.
Wrapped rather than left bare like the other seven: it has two callers
and must not depend on either one's PATH. A missing gtklock means no
lock screen at all, a missing niri or jq only the wrong monitor.
Looking at whether gtklock was worth it turned up two things.
The GTK layer was never on Mocha. colloid-gtk-theme with the catppuccin
tweak produces Frappé with a grey accent — base #292c3c, selection #ccd0da
— and #1e1e2e, #b4befe and #cba6f7 appear nowhere in its gtk.css. Kvantum,
the swaylock ring and Zed were all Mocha, so GTK was the one layer out of
step, unnoticed because the two dark bases sit close together.
catppuccin-gtk carries Colloid as a git submodule and only recolors it, so
the switch is a recolor with identical widget shapes.
The old objection to gtklock is gone: 4.0.0 links libgtk-session-lock.so.0,
so it speaks ext-session-lock-v1 like swaylock and the compositor still
holds the lock if it crashes. Only the ≤2.x layer-shell versions unlocked
on a crash. What it adds over swaylock is the modules — avatar, clock and
power buttons — which swaylock cannot do at all.
The blur stays baked into the wallpaper. gtklock cannot blur either: GTK3
has no filter CSS property at all, and it takes no screenshot of the
session, so there is nothing to blur at lock time.
Two rewrites of moonarch config were needed and both take the command from
one lockCommand binding: stasis names the locker in four places, and the
Quickshell power popout spawns it itself. Routing the popout through
`stasis trigger lock-screen` would have named it once, but the Lock button
would then fail silently whenever stasis is down. The reason to want it
turned out not to exist — the plan continues after a manual lock anyway,
because stasis takes the state from logind's LockedHint rather than from
having started the locker. Verified by locking by hand and watching DPMS
fire on schedule.
gtkSettings rewrites the Colloid name out of moonarch's settings.ini files.
Without it every GTK3 application would have fallen back to Adwaita: GTK3
reads those files directly and the dconf entry does not reach it on
Wayland.
Verified after rebuild: both hosts build, /etc/pam.d/gtklock carries the
same `auth sufficient pam_unix.so` swaylock had, the seeded stasis.rune
names gtklock at all four call sites, and the popout spawns it.
A rebuild against an unchanged lock installs nothing new, so a machine
built daily can still run months-old packages. A user timer reads the
locked lastModified of the nixpkgs input and notifies once it passes
seven days, offering a terminal that runs the update.
Quickshell has no scope support of its own, so apps started from its
launcher inherited the cgroup of quickshell.service and systemd killed
them along with the unit on every restart — a rebuild that rewrites the
unit was enough, and Librewolf counted each one as a crash until it
refused to restore the session. app2unit is the start prefix the QML now
uses; see moonarch's LauncherPopout.qml.
The unit also gained a start limit. After a rebuild sent niri SIGTERM,
graphical-session.target stayed up while the Wayland socket was gone, so
Restart=on-failure spun 32 times in two minutes against a dead display.
Three tries per 30s ends that; the window slides, so a single crash after
a normal run still gets its restart.
moonarch 7a24cdb puts a suspend step back into the desktop plan; it was lost
when that plan stopped inheriting the ac: blocks. systemctl has to be on the
unit PATH for it, or the step fails silently like swaylock did.
The port swapped moonarch's wdisplays-persistent for the nixpkgs wdisplays,
which cannot save -- so the kanshi config stayed empty and its unit was
skipped on every boot. nwg-displays supports niri and writes the layout to
~/.config/niri/monitor.kdl, which niri applies itself.
niri prefers a home config and does not merge, so config.kdl is seeded with
an include of /etc/niri/config.kdl, like moonarch does for stasis.rune.
wlsunset, both cliphist units and kanshi each only need the compositor
socket, so the set from the root oneshots applies. 9.4 UNSAFE to 0.9 SAFE,
verified by starting each one under the full set with `systemd-run --user`.
ProtectHome is out because it hides /run/user too, where the socket lives.
stasis gets five directives at most: swaylock authenticates through the
setuid-root unix_chkpwd, and every directive needing a mount namespace makes
`systemd --user` mount it nosuid, so /etc/shadow becomes unreadable.
Both units read one state file and write a single sysfs attribute, yet ran
as unrestricted root with network access and the full capability set.
systemd-analyze security scored them 7.5 EXPOSED, now 0.9 SAFE.
ProtectKernelTunables and PrivateUsers stay off on purpose; the reasons sit
in the comment, since without them they read as forgotten.
stasis does not run the configured actions through a login shell, so
/etc/profile never applies and nothing resolves via
/run/current-system/sw/bin. The old comment claimed the opposite and listed
only the binaries stasis spawns directly, which left swaylock unreachable:
`niri msg` worked, so the chain looked healthy while the lock step never ran.
The failure is silent. The spawn fails, stasis logs the action as started,
sees the immediate exit, treats the step as finished and stops there -- the
daemon then sits in "waiting for idle" with no lock and no dpms.
Verified: with an absolute path in the config, `stasis trigger lock_screen`
locks; with a bare `swaylock` and the old PATH it exits instantly.
brightnessctl comes along for the laptop's brightness step, which has the
same problem.
As a spawn-at-startup it inherited niri's environment; the unit got systemd's
minimal one, so every widget that shells out failed to start its process --
niri, nmcli, powerprofilesctl, the moonarch scripts and a dozen more.
The actions run as `sh -lc`, which sources /etc/profile and therefore resolves
swaylock and brightnessctl on its own; only a shell has to be present. niri,
pactl and notify-send are spawned directly, past the shell, so those stay. bash
resolved to bashInteractive, which pulled readline in for nothing.
The moonarch bump carries the suspend inhibit knobs, without which stasis
discarded the seeded config on every start.
nixpkgs carries stasis 1.5.1, so the pinned input and pkgs/stasis.nix go. It
ships no unit and has no NixOS module, so modules/services.nix keeps declaring
one; its actions run as `sh -lc` and it spawns niri and pactl directly, hence
the unit path.
stasis prefers a user config over /etc and generates its own when none exists,
so moonarch's is seeded into the home directory instead of only reaching /etc.
amd-pstate in active mode reads only the min/max and EPP registers and
ignores the requested performance level, so the governor auto-cpufreq
switches decides nothing. ppd writes those registers and the ACPI
platform_profile; on the testvm, where neither exists, it falls back to
the placeholder driver instead of polling sysfs a virtio guest lacks.
The NixOS module asserts against auto-cpufreq, so the two cannot run at
the same knob. Matches the moonarch tree this flake deploys from.
Gaps against moonarch/packages/official.txt: zip, unzip and viewnior were
missing outright, and thunar-archive-plugin was installed without
xarchiver, which left its menu entries inert.
services.auto-cpufreq is the backend of the Quickshell CpuGov widget,
which calls 'auto-cpufreq --get-state' and 'pkexec auto-cpufreq --force';
without it the indicator has no state and the popout cannot switch.
cpugov, sink-switcher and nightlight were referenced solely by the waybar
config; the Quickshell popouts cover all three. btnote, capsnote, vpn and
waybar-hidpp are unreferenced anywhere.
Adds the two system units for battery threshold restore and camera
shutoff, which had been missed.
defaults/xdg/kanshi/config is a 0-byte placeholder, unused on Arch as
well, where kanshi reads ~/.config/kanshi/config instead. Deploying it
made kanshi fail to parse and restart in a loop. The unit now starts only
when a user config exists.
Both are marked 'kept as a reserve' in the Arch config, a note left over
from the migration to the Quickshell bar. A fresh setup has no reason to
carry them, so the packages, their configs and the waybar-only helper
scripts stay out.
- NIRI_CONFIG points niri at /etc/xdg; without it niri writes its own
default config, so none of the Moonarch settings applied.
- config.kdl spawns the polkit agent by FHS path, provided via tmpfiles
instead of diverging the config shared with Arch.
- waypaper was missing entirely, hence no wallpaper. It invokes swww,
which the maintained awww provides under a different name.