Commit Graph
74 Commits
Author SHA1 Message Date
nevaforget 7cf022fec4 fix(stasis): let the desktop suspend on idle again
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.
2026-09-09 14:12:25 +02:00
nevaforget 257df5a4dc fix(desktop): drop the redundant fwupd package
services.fwupd.enable already puts the package in systemPackages, so the
entry only mattered where the daemon is off: on the desktop it left a
working fwupdmgr with no unit to talk to.
2026-09-09 13:56:02 +02:00
nevaforget 30a9ef2e56 chore(flake): update nixpkgs and nixos-hardware 2026-09-09 12:44:58 +02:00
nevaforget 6c6c31885f fix(desktop): set XCURSOR_THEME for clients outside niri
Steam is started by the user manager and only inherits XCURSOR_PATH, so
Xcursor falls back to the missing ~/.icons/default and draws the core
theme. The name matches modules/greetd.nix.
2026-09-09 12:44:58 +02:00
nevaforget f4b8b5dd50 feat(desktop): nwg-displays writes niri outputs, kanshi dropped
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.
2026-09-08 16:47:21 +02:00
nevaforget 058ec5c98d feat(services): sandbox the wayland session clients
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.
2026-09-08 16:46:53 +02:00
nevaforget 76b899a2ad feat(services): sandbox the moonarch oneshots
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.
2026-09-08 11:48:17 +02:00
nevaforget da483dfbc5 chore(flake): update moonarch
Picks up the corrected stasis.rune: cumulative timeouts, lock before dpms.
2026-09-01 20:01:44 +02:00
nevaforget 95e502e60a fix(stasis): put swaylock and brightnessctl on the unit PATH
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.
2026-09-01 19:33:49 +02:00
nevaforget a85f89ff1b chore(flake): update moonarch
Brings the stasis lock fix: the lock command no longer daemonizes, so stasis
waits for it instead of resuming immediately.
2026-08-31 16:58:42 +02:00
nevaforget 9fc5f4bb13 fix(desktop): set the Qt theme variables for the whole session
niri sets both in its environment block, which only reaches what niri spawns.
Since quickshell became a user service, everything started from its launcher
inherits the user manager's environment instead and comes up unstyled. As
session variables they travel through /etc/profile into niri-session's
import-environment and reach the user manager too.
2026-08-31 14:23:51 +02:00
nevaforget c300f7ece8 chore(flake): update moonarch
Brings the logout fix: the power menu quits niri instead of terminating the
login session, which left the compositor running and blocked every later login.
2026-08-31 14:04:17 +02:00
nevaforget 050aa649cc fix(greetd): install regreet
niri-greeter.kdl spawns it by name. The regreet module only ever references the
binary through the default_session command it sets with mkDefault, and that is
overridden here with the niri greeter -- so nothing installed it and the greeter
came up with no UI, quit itself, and started over.
2026-08-31 13:58:54 +02:00
nevaforget c680632acf fix(greetd): restart it so logging out returns to the greeter
The option defaults to false as soon as a host sets initial_session, on the
grounds that a restart would trigger the autologin again. greetd(5) says it
does not: the initial session runs only on the first launch since boot, marked
by a runfile under /run. Left at the default, greetd exits with the session and
leaves a dead VT that only a power cycle gets out of.
2026-08-31 13:38:44 +02:00
nevaforget f065b829fe fix(desktop): put the profile's Qt plugins on QT_PLUGIN_PATH
Qt applications are wrapped, so each looks for plugins in its own closure and
never finds a style installed next to it -- VLC ships no Kvantum plugin and so
came up in the stock Qt style however the theme was configured. qt.enable adds
the profile's Qt5 and Qt6 plugin directories, which the wrappers append to.
platformTheme and style stay null: they would only pull in packages already
installed, and niri's environment block sets both variables.
2026-08-31 13:22:29 +02:00
nevaforget d0cd92fb37 fix(desktop): seed the Kvantum theme into the home directory
kvantum.kvconfig selects catppuccin-mocha-lavender, but nothing installed it,
so Kvantum fell back to its built-in default and every Qt application went
unthemed. Of the four places it searches (style/Kvantum.cpp) three are under
$HOME and the fourth is a DATADIR compiled into the style plugin, which under
Nix is the plugin's own store path -- so a theme package in systemPackages can
never be found. Seeded like the other home-only configs.
2026-08-31 13:17:09 +02:00
nevaforget 60b6daf4f3 fix(services): give quickshell the session path
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.
2026-08-31 12:59:03 +02:00
nevaforget b81f7ca7f2 feat(services): run quickshell as a user service
Mirrors the unit moonarch now ships. spawn-at-startup never restarted it, so a
crash cost the bar for the rest of the session.
2026-08-31 12:55:16 +02:00
nevaforget e14b1d3659 chore(flake): update moonarch
Brings the middle-click-to-close handler on the Quickshell window title.
2026-08-31 12:50:36 +02:00
nevaforget 8c8f9b3c2e chore: fix what a pass over the tree turned up
- zed asked for Hack Nerd Font, which is not installed; the fonts here are
  UbuntuSans and UbuntuSansMono, so it had been rendering in a fallback.
- /etc/mpv/mpv.conf was deployed for a player that is not installed; vlc is.
- The /etc/stasis/stasis.rune entry is unreachable now that the config is
  seeded into the home directory, which stasis prefers.
- Comments referring to Arch now name moonarch, the repository they actually
  describe.
- The project inputs follow their default branch, not a release tag.
2026-08-31 12:43:10 +02:00
nevaforget ea12f2ca51 fix(stasis): correct the unit path and pick up the config fix
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.
2026-08-31 12:43:03 +02:00
nevaforget b1ed5d087b refactor(stasis): take the package from nixpkgs
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.
2026-08-31 11:58:33 +02:00
nevaforget 72af3e0cfc feat(desktop): let imperative nix commands see allowUnfree 2026-08-31 09:23:28 +02:00
nevaforget f6dc2be27d chore(flake): update moonarch, nixos-hardware and nixpkgs 2026-08-31 09:15:29 +02:00
nevaforget 16625ef503 refactor(services): power-profiles-daemon replaces auto-cpufreq
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.
2026-08-25 08:33:29 +02:00
nevaforget 586eab6b80 feat(desktop): enable gamemode
The module rather than the package: gamemoded needs cap_sys_nice for renice
and a pkexec helper for the governor, which the module provides by wrapping
the binary, creating the gamemode group and enabling the user service.

The module creates that group but adds nobody to it, so the account joins it
in the same host file.
2026-08-24 16:19:18 +02:00
nevaforget 3e40a701e7 fix(desktop): add the archiver, image viewer and cpufreq backend
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.
2026-08-24 10:28:28 +02:00
nevaforget 99507cc9c2 feat(zed): install the Zed editor with the Arch config
Seeds settings.json into the home directory with a tmpfiles 'C' rule, the
same route swaylock and waypaper take: Zed rewrites the file whenever a
setting is changed from its UI, so a store symlink would break saving.

nix-ld is what makes the language servers Zed downloads run here; the
alternative, zed-editor.fhsWithPackages, would cut them off from
project-level tooling out of a nix-shell.
2026-08-24 10:28:18 +02:00
nevaforget eca2264e64 docs(desktop): record why pactl is not installed
Steam prints "pactl: command not found" and it means nothing: it probes
wpctl and pactl both and uses whichever is on PATH, which wireplumber
provides. Closed as expected behaviour in nixpkgs#525793.
2026-08-20 18:43:31 +02:00
nevaforget e7ddf7bd1f fix(pipewire): denoising through configPackages, desktop gets steam and bitwig
The RNNoise config was deployed to /etc/xdg/pipewire, which PipeWire
never reads: it resolves $PIPEWIRE_CONFIG_DIR, then the home config, then
its compiled-in /etc/pipewire, and knows nothing of XDG_CONFIG_DIRS. An
installed system therefore had no denoising while carrying the file.

It now goes through services.pipewire.configPackages, with the LADSPA
plugin substituted to its store path since LADSPA_PATH has no /usr here.

Alongside, on the desktop host only: steam via its module (which also
turns on 32-bit graphics), bitwig-studio, and rtkit so a DAW can ask for
realtime scheduling at all.
2026-08-20 18:25:36 +02:00
nevaforget 35df48cdf8 feat(desktop): install librewolf as the browser
The package set provided no browser at all. Waterfox, which Arch runs,
is not in nixpkgs; packaging its upstream tarball worked but leaves
version and hash to be bumped by hand, with nothing signalling a missed
release — poor terms for the most exposed program on the machine.

librewolf comes from nixpkgs, so its updates arrive with the flake, and
it is fully substitutable. Its defaults are stricter than Waterfox's.
2026-08-20 17:57:22 +02:00
nevaforget 3361c7c46e feat(desktop): driverless printing, OpenVPN plugin, VLC
networking.networkmanager.plugins defaulted to an empty list, so
NetworkManager offered no OpenVPN connection type at all.

Printing is driverless: asked over IPP the Samsung M267x 287x reports
image/urf and application/PCLm, so CUPS builds the queue from the
printer's own capabilities. No PPD, no unfree driver, and no printer
declared here. Avahi provides the discovery and resolves .local.

VLC instead of the mpv the Arch lists carry.
2026-08-20 17:41:04 +02:00
nevaforget b98c85ec27 feat(desktop): declare the real disk paths
hosts/desktop carried two SET-VIA-disko-install placeholders, so the file
did not say which disks the machine has. The invalid paths only ever
caught a missing --disk flag, never a wrong one, and paid for it by
leaving the hardware undocumented.

The system disk is the 512G XPG, /home goes on the 1T Crucial;
disko-install --disk main|home still overrides both. The 1T currently
holds an ext4 Data partition, which the install erases.
2026-08-20 16:50:27 +02:00
nevaforget d36e5d6633 fix(zsh): seed a placeholder ~/.zshrc
zsh/newuser runs zsh-newuser-install on every interactive login while the
user has none of .zshenv, .zprofile, .zshrc or .zlogin. The whole
configuration lives in /etc/zshrc, which is not a personal file, so the
wizard never stopped coming up.

The seeded file carries comments only: sourcing the shared zshrc from it,
as the Arch counterpart does, would load it a second time.
2026-08-20 15:39:09 +02:00
nevaforget e366a3c256 fix(installer): keep the cloned remote on HTTPS
A freshly installed machine has no key on Gitea, so the SSH remote the
installer set turned the first `git pull` into a password prompt.
2026-08-20 14:36:08 +02:00
nevaforget a5d6c3eb77 fix(zsh): defend the sourced zshrc against the /etc/zshrc defaults
/etc/zshrc renders promptInit and environment.shellAliases after
interactiveShellInit, so both overrode the shared zshrc: the prompt
became `prompt suse` and ls/ll/l fell back to coreutils. The plugin
sourcing in the zshrc points at the Arch path /usr/share/zsh/plugins
and silently found nothing.

Clear promptInit, drop the three aliases with null, and enable the
syntax-highlighting and autosuggestions modules.
2026-08-20 14:35:55 +02:00
nevaforget 427e667dff feat(desktop): thunar and the moonarch zshrc
Both were missing against the Arch package set: thunar with volman and the
archive plugin, tumbler and ffmpegthumbnailer (packages/official.txt:79-83),
and defaults/shell/zshrc, which Arch installs as /etc/zsh/zshrc.moonarch.
2026-08-20 14:16:51 +02:00
nevaforget ee44464e4c feat(testvm): declare the second disk
The launcher attaches moonix-home.qcow2 as /dev/vdb, so moonarch.disk.home is
exercised in the VM before it runs on the desktop own disks.
2026-08-20 14:16:51 +02:00
nevaforget 81570d9109 feat(installer): ISO that installs a host with one command
hosts/installer builds an image (nix build .#installer-iso) carrying this
flake as /etc/moonix, disko and a wrapper. The guest runs
`moonix-install <host>`: disko formats per the host file, nixos-install
installs, and the repository is cloned into the account own ~/flakes/moonix
with /etc/nixos/flake.nix pointing at it.

The image is built from the local tree, so a state does not have to be pushed
to be installable.

disko is patched to re-ask for the LUKS passphrase on a mismatch instead of
killing the script: its `exit 1` sits inside a function called from the until
loop that was meant to repeat the prompt.
2026-08-20 14:16:39 +02:00
nevaforget 55c81dbb8a docs: correct false claims in the README
Verified against nix eval, upstream sources and a local test:

- testvm accepts SSH password authentication (NixOS default), the README
  claimed the opposite
- only /boot goes through by-partlabel; the filesystems inside the container
  come up as /dev/mapper/crypted
- kanshi parses an empty config fine; a missing one is the error case, in the
  README and in the desktop module comment
- modules/user.nix ABOUTME claimed a login name shared by every host
- the flake imports six modules, not five

Rewrites "Keeping a machine up to date": the checkout is ~/flakes/moonix and
/etc/nixos/flake.nix a symlink to it, which man 8 nixos-rebuild documents.
Drops the git and ssh-keygen basics. Adds an optional LUKS header backup step,
and records the checkout decision in DECISIONS.md.
2026-08-20 12:09:23 +02:00
nevaforget eb9b5fd3d1 feat: desktop host with /home on a second disk
Adds hosts/desktop: 512G system disk, 1T disk for /home, each in its own
LUKS container. modules/disk.nix gains moonarch.disk.home, which moves the
/home subvolume to the second disk and drops it from the first.

New modules/user.nix declares moonarch.user without a default, so the login
name exists once per host instead of once per reference. Both hosts set an
initialPassword: swaylock authenticates through pam_unix and cannot unlock a
screen for an account without one.
2026-08-20 12:09:07 +02:00
nevaforget ec985a8f7f fix(desktop): follow swaylock config move, use blurred wallpaper
The config source is defaults/etc/swaylock/config now. The tmpfiles seed
stays: nixpkgs leaves meson's sysconfdir at $out/etc, so the
/etc/swaylock/config that Arch owns is never read here.
2026-08-18 17:34:01 +02:00
nevaforget 686ee83475 chore: point the moonarch input at the power popout 2026-08-14 22:31:22 +02:00
nevaforget f765a460b5 feat: drop moonlock and moonset
The power menu is a Quickshell popout in the moonarch config now, and it
was the last caller of moonlock. Inputs, package entries and both build
recipes are gone, together with the generated /etc configs and the
moonlock PAM stack — swaylock's comes from programs.niri.enable.
2026-08-14 17:48:52 +02:00
nevaforget ad4cd937f9 chore: point the moonarch input at the regreet and swaylock configs 2026-08-14 16:45:03 +02:00
nevaforget 630faf5390 feat: regreet and swaylock from nixpkgs, autologin after LUKS
greetd.nix configures services.displayManager.regreet and keeps niri as
the greeter compositor, so niri-greeter.kdl stays the shared file that
carries the German keyboard layout. The moongreet input, its package and
pkgs/moongreet.nix are gone.

extraCss goes through builtins.readFile: the module only recognises a
direct /nix/store child as a path, so passing the file path as a string
wrote the path itself into /etc/greetd/regreet.css.

desktop.nix installs swaylock, seeds ~/.config/swaylock/config with the
wallpaper rewritten to the store path, and now owns the moonlock PAM
stack. The Colloid overrides moved to flake.nix so the greeter and the
desktop share one derivation.

initial_session is set per host because the accounts differ. greetd's
restart option flips itself off, verified.

flake.lock still pins moonarch at the pushed revision; it needs
`nix flake update moonarch` once the moonarch commit is pushed.
2026-08-14 14:32:43 +02:00
nevaforget 16ee94f647 Add the qemu-guest profile to the test VM
The initrd default set covers nvme, ahci, sd_mod and USB HID, but not
virtio. Without virtio_blk and virtio_pci the initrd never sees the
disk and stops at

  timed out waiting for device /dev/disk/by-partlabel/disk-main-root

profiles/qemu-guest.nix provides them. Real hardware needs no
counterpart; nvme is in the default set.
2026-08-11 15:01:04 +02:00
nevaforget e53f0d75f7 Update lock: add disko 2026-08-11 11:47:09 +02:00
nevaforget 1ec12da9ff Declare the disk layout with disko
Installing used to mean six manual steps through Calamares, one of which
copied hardware-configuration.nix into the repo — a step that fails
silently unless the file is also git added, since Nix only sees tracked
files. It also did not scale past one machine.

modules/disk.nix defines moonarch.disk.{enable,device,encrypt,espSize,
swapSize} and builds disko.devices from it, so another machine is a
device path rather than a new layout. Installing is a single
disko-install run from the ISO.

hardware-configuration.nix is no longer needed: nvme, ahci, sd_mod and
the USB HID modules are in the initrd via
boot.initrd.includeDefaultModules, and nixpkgs.hostPlatform comes from
nixosSystem. The testvm copy described a qcow2 that no longer exists and
is deleted; that host now uses the module against /dev/vda.

The user password no longer arrives with the installer, so it is set
once via nixos-enter after installing instead of committing a hash.
2026-08-11 09:50:55 +02:00
nevaforget 9988dc1e7b Update lock: add nixos-hardware 2026-08-07 17:14:23 +02:00