config.rasi was the rofi theme dropped in to show the desired style; it's
not part of the distro. Remove it from tracking (the file stays on disk,
untracked). Its content informed 994025f and remains in that history.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reworked the menu look, palette-driven so it lands on every theme:
- Real Papirus icons in place of Nerd Font glyphs: a row() helper emits
rofi's per-row icon protocol, and the five static menus (root, Tools,
System, Power, Capture) map each entry to a freedesktop icon name
(verified present in Papirus). Entry text is now clean ("Apps"), the
case-matching is unchanged.
- Larger icons via a new t.ui.iconSize knob (36px, generated theme); the
summer whole-swaps keep their 40px identity.
- Rounded, roomy rows with subtle zebra striping: alternate rows lift to
@surface (a faint bg0 wash on the summer cream/slate windows), the
selected row to @accent — using rofi's normal/alternate/selected row
parities.
- Content-fit windows: fixed-num-lines=false so a 6-entry menu stops
reserving 8 rows of empty space (the launcher still fills + scrolls).
Dropped the fixed height:50% from the summer .rasi so they size to
content too — which also unhid their clipped Power/Keybindings rows.
- Title-cased the menu prompts.
Verified by building the HM config and rendering the generated theme plus
both summer whole-swaps headlessly (sway + grim): icons resolve, zebra
shows, every menu hugs its rows.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
VM-verified the full batch of opt-in service toggles (libvirt, docker, obs
+ v4l2loopback, ollama, printing, openrgb, wireshark, adb, gamemode,
kdeconnect, restic) in a headless nixosTest — all green, no module bugs.
The one thing worth surfacing: services.restic ships a pre-configured
`restic-nomarchy` wrapper (repository + passwordFile baked in) for listing
and restoring snapshots. Mention it in the option description and README so
the restore path is discoverable — a backup you can't restore is half a
feature.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Finishing the four remaining "opt-in services" candidates — but two of
them aren't service-shaped, so the package-vs-toggle principle applies:
- Nextcloud client → a bare tray app; already in the app-suite menu,
not a service.
- WireGuard → NetworkManager (on distro-wide) imports .conf
tunnels natively, so there's no daemon to "just
enable". Only the wg/wg-quick CLI is missing →
wireguard-tools added to the app-suite menu.
The two that ARE config-backed become nomarchy.services.* toggles:
- openrgb → services.hardware.openrgb (daemon + device udev rules).
- restic → a small scaffolded surface (repository / passwordFile /
paths) over services.restic.backups: daily timer, 7/4/6
retention, --exclude-caches, repo auto-init. Asserts that
repository + passwordFile are set when enabled. passwordFile
is typed `str`, not `path`, so a flake-relative secret can't
be copied into the world-readable store.
README option table and ROADMAP (services now "Seventeen shipped";
cloud-sync / networking / backup / devices areas updated) + the downstream
system.nix examples. Both branches eval-verified; the restic assertions
confirmed firing when repo/password are unset.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Make the whole workstation menu available, all off/commented by default.
home.nix: the bare GUI apps become a categorized commented menu (~50
suggestions across browsers, comms, office, notes, code, graphics, A/V,
media, gaming, files/sync, security, system, local AI) — uncomment to add.
services.nix: the members that need system config (not just a package)
become opt-in nomarchy.services.* toggles, each with a commented example
in the downstream system.nix:
- docker → rootful Docker + user in docker group; asserts against the
podman docker-compat (both provide the `docker` command)
- kdeconnect → programs.kdeconnect (opens phone-pairing ports)
- gamemode → Feral GameMode daemon
- adb → android-tools (programs.adb was removed; systemd ≥258 does
the device uaccess udev rules, so no group needed)
- wireshark → Qt GUI + user in wireshark group (capture without root)
- ollama → local LLM runtime on 127.0.0.1:11434
- printing → CUPS + Avahi/mDNS network printer discovery
README option table and ROADMAP (services item now "Fifteen shipped";
local-AI / containers / gaming / devices areas marked) updated. All seven
branches eval-verified with the toggles forced on; the docker/podman
assertion confirmed firing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The "default application suite" roadmap item, but deliberately NOT a
nomarchy.apps.* option surface: a bare package install has no config to
put behind a toggle, so a thin `mkIf x { systemPackages = [p]; }` only
reinvents what a package list already gives. Instead the suite is a
curated home.packages list in templates/downstream/home.nix (extending
the existing `# firefox` convention) — active staples (libreoffice,
vscode, gimp, inkscape, mpv, amberol) plus commented suggestions. The
opt-out is deleting a line; it also keeps the apps out of the live ISO
automatically (the ISO never scaffolds from the template).
The members that DO need system config become nomarchy.services.* opt-in
toggles instead (config behind the switch, like the existing services):
- steam → programs.steam (32-bit stack, controller udev, RP ports)
- libvirt → libvirtd + virt-manager, login user added to libvirtd group
- obs → obs-studio + a v4l2loopback virtual camera (exclusive_caps=1)
selectable as a webcam in Zoom/Teams
README option table, ROADMAP (services item now "Eight shipped"; suite
item marked done with the no-surface rationale) and the downstream
system.nix examples updated to match.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
List the on-by-default zsh aliases (navigation, git, nix, misc) in the
Day-to-day section so they're discoverable from the README, not just by
running `alias`. Note the doc location in the roadmap item.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extend the zsh shellAliases in shell.nix with a curated, on-by-default
set: navigation (.., ..., ....), a git block (g, gst, ga/gaa, gc/gcm,
gco/gsw, gb, gd/gds, gl/glg, gp/gpl, gf), a nix block (ns, nr, nfu, nfc,
nsearch, ngc), and path/reload. Same restraint as rg/fd: short where it
helps but never shadowing a real binary — the git set keeps off gs
(ghostscript), and the system/home rebuilds keep their sys-update /
home-update names rather than cryptic aliases.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The repo-organization re-check found nothing to move: no orphaned modules
(keybinds.nix is a data import, not a HM module), no dead-code markers or
editor/backup cruft, every themes/<slug>.json pairs with its optional
overrides dir, and hosts/{default,live} are the reference + ISO hosts.
Recorded the one open decision: no formatter is declared and the .nix
hand-formatting is deliberate, so adopting nixfmt is a maintainer call,
left untouched. Also marked the menu category grouping a standing
convention — new menu-facing features get placed in the right submenu.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The root picker had grown to 16 flat entries. Collapse it to six —
Apps · Theme · Tools › · System › · Power · Keybindings — with Tools
(Calculator, Clipboard, Emoji, Files, Web, Capture, Ask) and System
(Network, Bluetooth, DND, plus the self-gated Snapshots / Power-profile)
as submenus the dispatcher routes via `nomarchy-menu tools|system`. Each
submenu ends in a ← Back entry that re-opens the root. The direct
SUPER+CTRL+<mnemonic> binds still reach the leaves straight (bypassing
the menu), and the gated entries still hide when unavailable.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extend nomarchy.services.* with three more opt-in toggles:
- podman: rootless containers, `docker` aliased to it, container DNS, and a
subuid/subgid range for the login user.
- flatpak: services.flatpak + a oneshot that adds the Flathub remote
system-wide (idempotent, retries when online).
- pika: ships pika-backup, the GUI for scheduled Borg backups.
Commented examples in the downstream system.nix template, per convention.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Start the opt-in services surface: nomarchy.services.{tailscale,syncthing},
off by default, in modules/nixos/services.nix. Tailscale ships the daemon
(authenticate with `sudo tailscale up`); Syncthing runs as the login user
with its GUI on 127.0.0.1:8384 (overrideDevices/Folders off so GUI-managed
folders survive rebuilds). username is read lazily, only when Syncthing is
on. Commented examples in the downstream system.nix template per the opt-in
convention.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Convention: an off-by-default feature that needs nomarchy.* settings in the
downstream files should appear commented-out in templates/downstream/{home,
system}.nix, so a new install enables it by uncommenting + tweaking rather
than hunting the docs for the option name. Apply it now: night light,
nomarchy.monitors, per-device keyboard layouts (home.nix) and power
management (system.nix). Also fix the stale keyboard.layout note
(console/LUKS follow xkb automatically now). Documented in README S6.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add nomarchy.nightlight (opt-in): a scheduled colour-temperature shift via
the HM services.hyprsunset module -- warm (.temperature, default 4000K) at
night, identity (no shift) by day, switching at .sunset / .sunrise. Two
time-based hyprsunset profiles, so hyprsunset handles the schedule and the
on-login state. New modules/home/nightlight.nix, imported in the home module.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The root picker has grown into a long flat list; group it into category
submenus (System / Tools / Look & Feel / Power) with the dispatcher routing
nomarchy-menu <category> to its leaf modules, keeping the SUPER+CTRL+<mnemonic>
binds as direct fast paths.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A VM regression test caught that btrfs-assistant 2.2 segfaults on launch
inside libbtrfsutil.so.1.4.0 -- a library ABI mismatch in nixpkgs 26.05, not
a GL/VM artifact (crashes with software GL and every Qt platform; the fault
is in a btrfs library). The snapshot menu wiring is correct and the app is
installed, but the GUI doesn't open. The snapshot backend is unaffected and
is itself VM-verified (full BTRFS+LUKS install: root+home snapper configs, a
/home timeline snapshot, /home/.snapshots a real subvolume, the oneshot
Result=success). Keep the entry, document the bug pending a nixpkgs fix.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The runtime-remember complement to nomarchy.keyboard.devices. Set
nomarchy.keyboard.layouts (candidate layouts) and a nomarchy-keyboard-watch
daemon runs (exec-once): it polls hyprctl devices, and when a keyboard
connects after login that isn't declared and hasn't been chosen before, it
pops a rofi layout picker, applies the choice via hyprctl switchxkblayout (an
index into the candidate set carried by input.kb_layout), and remembers it
per-device in ~/.local/state -- re-applied silently on later reconnects. The
boot-time set (incl. the built-in keyboard) is never prompted.
Pure bash + jq/hyprctl/rofi (all in the session PATH); state lives outside
the flake (a stateful runtime piece by design). Eval + bash-syntax verified;
the hotplug behaviour needs an on-hardware test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A runtime-remember complement to the declarative nomarchy.keyboard.devices:
a daemon watches Hyprland's socket, pops a rofi layout picker when an unknown
keyboard connects, applies it, and persists per-device so it re-applies on
reconnect — optionally writing the choice back into nomarchy.keyboard.devices.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add nomarchy.keyboard.devices ({ "<hyprctl-device-name>" = { layout; variant; }; })
generating Hyprland `device` blocks that override the session
nomarchy.keyboard.layout for a named keyboard -- e.g. an external board
that's physically a different layout than the laptop's built-in one.
Hyprland applies it whenever that device connects, so the external keyboard
"remembers" its layout the declarative way. A Waybar hyprland/language
indicator shows the active layout, placed only when more than one layout is
in play (a comma in the session layout, or any per-device override) so
single-layout bars stay clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Surface snapper snapshots from the desktop: `nomarchy-menu snapshot` (in the
SUPER+M picker, self-gated) launches btrfs-assistant, the polkit-aware
browse/diff/restore/rollback UI -- safer than a fat-fingerable rofi rollback
for a destructive root-only op. Shipped system-side gated on
nomarchy.system.snapper.
Also snapshot /home by default (hourly 5 / daily 7 / weekly 4) when it's its
own BTRFS subvolume -- the installer only set up snapper for /. A boot
oneshot creates the required /home/.snapshots subvolume if missing, so
already-installed machines get it too (no disko change).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Verified this session in headless QEMU: the org.freedesktop.appearance
color-scheme portal reads 1 on a dark theme and 2 after switching to a light
one, so the dconf value reaches GTK4/Qt apps. Removes the stale "needs a
live session to confirm" note.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
custom/powerprofile only appeared in the generated bar -- its exec was a
writeShellScript store path, which can't go in the summer themes' static
whole-swap waybar.jsonc, so summer-day/night were missing it (the DND bell
already had parity). Promote powerProfileStatus/Cycle to named
writeShellScriptBins on PATH (waybar.nix home.packages) and reference them
by bare name, then add custom/powerprofile to both summer waybar.jsonc +
their waybar.css. Same pattern as the swaync bell.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
nixos-hardware covers basic per-model enablement, but advanced features
(keyed on CPU/GPU generation) are left to the user. Capture a big item:
extend the installer's autodetection + a nomarchy.hardware.* surface to turn
on safe features automatically (opt-out) and gate the heavy/experimental
ones (opt-in), without per-feature fiddling. Worked example on the ThinkPad
T14s (Ryzen 7 PRO 7840U + Radeon 780M): ROCm (gfx1103 override), Ryzen AI
NPU/XDNA, AMD P-State EPP, VA-API, fprintd biometrics (fwupd already ships),
SSD fstrim.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a per-output monitor schema (name / resolution / position / scale /
transform / mirror / bitdepth / vrr / disable) that generates Hyprland
`monitor` rules, keeping the ,preferred,auto,1 wildcard as the fallback.
Hyprland applies the rules on hotplug, so a declared external/dock output
arranges itself on connect (no kanshi -- it fights Hyprland's own output
management). Default [] -> wildcard only, so no change for existing setups;
set wayland.windowManager.hyprland.settings.monitor directly to override.
nwg-displays ships behind nomarchy.displays.enable (default true) as an
interactive arranger -- a helper to discover values; the declarative config
stays the source of truth (its output file isn't sourced).
Remaining (roadmap): docked/undocked profile switching, workspace-to-monitor
binding.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The previous nomarchy-hibernate-unlock ran `pkill hyprlock` after a
hibernate resume, but killing a Wayland session-lock client without
releasing the lock trips hyprlock's "go to a tty" crash failsafe -- the
compositor keeps the screen locked for safety. So resume showed a hyprlock
error screen instead of unlocking.
Fix it the right way: never engage the lock before an encrypted hibernate
(the LUKS passphrase at resume is the gate). Replace the post-resume unlock
with a nomarchy-lock-before-sleep unit that takes over hypridle's
before_sleep_cmd: it locks on the RAM-resume sleeps (suspend / hybrid-sleep
/ suspend-then-hibernate) always, and on hibernate.target only when the disk
is unencrypted. idle.nix drops before_sleep_cmd accordingly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The custom/powerprofile indicator is missing from the whole-swap summer
themes (its exec uses Nix-built script paths that can't go in static
JSON). Note the fix (promote the scripts to named bins on PATH) and the
general "new module → also add to whole-swap themes" rule.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The DND bell only landed in the generated Waybar config; summer-day and
summer-night ship waybar.jsonc whole-swaps that replace it entirely, so
they'd have shown no bell. Add custom/notification (swaync-client -swb,
left-click panel / right-click DND) to both legacy layouts with their
FontAwesome bell / bell-slash glyphs, and style it alongside the other
right-side modules in each waybar.css.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a Do-Not-Disturb story on top of swaync:
- Waybar `custom/notification` module via `swaync-client -swb` — streams
the notification count + DND state (no polling). Left-click toggles the
panel, right-click toggles DND; bell-off glyph + muted color when DND is
on, accent when notifications are waiting.
- `nomarchy-menu dnd` toggle in the root picker and bound to SUPER+CTRL+D
(so it shows in the SUPER+? cheatsheet). Turning DND off confirms with a
toast; turning it on is silent — notifications are suppressed and the
bell-off icon is the cue.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The downstream flake at ~/.nomarchy is a live working tree by design —
nomarchy-theme-sync rewrites theme-state.json on every switch — so Nix's
dirty-tree warning fired on every sys-update/home-update and was just
noise. Default nix.settings.warn-dirty to false (mkDefault, overridable).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Document the main=dev / v1=release-pointer model in flake.nix (where the
v1 ref is defined) and mark the repo-sanitize roadmap items shipped
(installer prune, option-table reconcile, settings untrack, branch
realignment).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
It holds this machine's Claude Code permission allowlist — local by
convention (the `.local` suffix; settings.json is the shared one), so it
shouldn't be versioned. Remove from tracking (keeping the working file)
and gitignore it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
console.useXkbConfig and boot.initrd.systemd.enable are set distro-wide
(lib.mkDefault true in modules/nixos/default.nix, since the LUKS-keymap
fix), so the installer rewriting them in the generated system.nix was
redundant. Drop them there and from the offline-pin test fixture in
flake.nix — the nomarchy module still defaults them on, so the closure
(and the pin set) is unchanged; verified the values still resolve true
and `nix flake check` passes. Only the machine-specific xkb layout is
written now. Adjusts the README keyboard-option wording to match.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Brings main current with the 51 commits of post-rewrite feature work
(laptop power, idle cohesion, menu plugins, fwupd, docs restructure)
that had been accumulating on v1. New model: main is the development
default; v1 is the release pointer downstreams pin (?ref=v1), advanced
to main once a batch is tested. Rolling, no tags.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The roadmap + known-issues had grown to ~280 lines, nearly half the
README. Moved both into docs/ROADMAP.md (forward-looking plans + a log of
shipped fixes) and left a short pointer, so the README is back to a
focused entry point (572 -> 302 lines).
Also fixed drift surfaced in the pass:
- layout tree: add modules/nixos/power.nix, modules/home/{keybinds,keys,
fastfetch}.nix and docs/ROADMAP.md, which were missing.
- option table: add keys.enable, fastfetch.enable, system.snapper.enable
(default false — the installer opts in) and system.greeter.autoLogin.
OVERRIDES.md, TESTING.md and the downstream template README were checked
and are accurate (.#default / .#me names and the 21-preset count match),
so they're left as-is. Docs stay Markdown-in-repo — no docs site.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The rofi-file-browser-extended navigate-a-tree model felt worse than flat
fuzzy-find for a quick launcher (yazi already covers real browsing), so
`files` goes back to `fd` → `rofi -dmenu` → xdg-open. calc (rofi-calc)
and emoji (rofi-emoji) stay. Re-adds `fd` to the module's packages and
notes the dropped plugin in the roadmap.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the hand-rolled calc/files modules with purpose-built rofi modi
(via programs.rofi.plugins) and add an emoji picker — all themed through
the same .rasi as every other modi:
- calc -> rofi-calc: live results as you type via libqalculate directly,
which also dodges the qalc CLI's "15% of 200" -> rem(15, 1 B) misparse.
Enter copies; the menu persists to chain calculations. Drops the
libqalculate (qalc CLI) dependency.
- files -> rofi-file-browser-extended: live directory navigation +
xdg-open, replacing the flat 50k-cap `fd` dump of $HOME. yazi (SUPER+E)
stays the power browser; `fd` already ships via shell.nix.
- emoji -> rofi-emoji (new module): glyph picker, copies through the
plugin's Wayland clipboard adapter. Bound to SUPER+CTRL+E in
keybinds.nix (so it lands in the SUPER+? cheatsheet too).
rofi 2.0 rebuilds cleanly with all three plugin .so (ABI-matched, same
nixpkgs). README roadmap updated: calc rework marked shipped, with a
follow-up item for the script-based plugins (network/secrets/audio).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
hypridle's suspend listener now runs `nomarchy-on-ac || systemctl
suspend`, so a plugged-in machine no longer suspends itself mid-idle
(long builds, media, presentations survive). It also fires sooner —
15 min, vs the old fixed 30 — since it now only applies unplugged.
Lock and screen-off are unchanged on both power sources, and logind's
lid handling stays at its sensible defaults (suspend on lid close,
ignore when docked).
Closes the idle-cohesion follow-up under the laptop-power roadmap item.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A dedicated prose pass distinct from the repo-sanitize item: split the
~200-line roadmap out of the README, reconcile the option tables with the
live nomarchy.* surface, and review docs/ + the downstream template for
drift.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ship `services.fwupd.enable` distro-wide (mkDefault) for firmware updates
via LVFS — the daemon + metadata-refresh timer only, no automatic
flashing, so applying an update stays an explicit `fwupdmgr update`. A
no-op where LVFS sees no devices; overridable natively. Sits with the
other mkDefault desktop services rather than a nomarchy toggle (it's a
single service, like upower).
Also documents the `nomarchy.system.power.*` options in the README table
(missed when the power module landed) and adds a short note on the
fwupd default.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a `nomarchy.system.power.*` surface (modules/nixos/power.nix),
replacing the bare `services.upower` (Waybar reporting only) baseline:
- power-profiles-daemon ships by default; switched through polkit (no
root prompt). TLP is the opt-in `backend = "tlp"` for deeper battery
tuning — the two are mutually exclusive (asserted).
- thermald behind `power.thermal.enable` (Intel-only).
- `power.batteryChargeLimit` (e.g. 80): a backend-independent sysfs
oneshot writes charge_control_end_threshold; gated on `power.laptop`.
Switcher + indicator live home-side and self-gate on a battery being
present + powerprofilesctl running, so they hide on desktops and under
TLP — no system->home wiring (like the Waybar battery widget):
- rofi.nix: `nomarchy-menu power-profile` module + a picker entry shown
only on laptops with PPD.
- waybar.nix: first `custom/*` module — click cycles the profile.
Installer writes `power.laptop = true` (battery probe) and
`power.thermal.enable` (GenuineIntel) into the generated system.nix, and
scaffolds the charge limit commented-out.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
hypridle locks hyprlock before every sleep -- right for suspend, but
redundant for hibernate: the LUKS passphrase entered at boot already gates
the resume, so the user typed a password twice. Add a nomarchy-hibernate-
unlock systemd unit (WantedBy hibernate.target, After systemd-hibernate.service
=> runs post-resume) that dismisses hyprlock, gated on the disk being LUKS-
encrypted. Suspend (and the RAM-resume phase of suspend-then-hibernate) keep
locking -- they have no passphrase gate; an unencrypted hibernate keeps its
lock too. idle.nix gains a pointer comment; roadmap item marked done.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
home-update rebuilds the desktop against the current flake.lock and never
touches it; only sys-update runs `nix flake update`. So pulling a distro
change that affects the home layer requires sys-update first (updates the
lock), then home-update -- the reverse order rebuilds against stale inputs
and silently skips the new home-side changes (hit while testing the mic
fix). Also note hyprctl reload after a home-side keybind change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The wpctl-based mic toggle lost the volume bar the native swayosd input OSD
used to draw -- it only showed an icon + text. swayosd 0.3.x exposes
--custom-progress (0.0-1.0), so read the level back from wpctl after the
toggle and render icon + bar (muted/active glyph via --custom-icon),
matching the output-volume OSD. Parsing is pure bash builtins so the script
needs no PATH beyond the two pinned tools.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The web module now opens google.com/search instead of DuckDuckGo. Also log
a roadmap item to rework calc: qalc -t misparses common phrasings (15% of
200 -> rem(15, 1 B)) and the result only shows in the next menu's -mesg
line -- move toward live results as you type.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
yazi 26.x renamed the previewer/fetcher match key from name to url. The
fetchers were updated but the markdown previewer still used name, so the
whole [plugin] config failed to parse ("at least one of 'url' or 'mime'
must be specified") and yazi fell back to its presets.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
swayosd-client --input-volume mute-toggle draws the OSD but never flips the
default source's mute (its output-mute path works; input does not). Confirmed
on hardware: wpctl set-mute @DEFAULT_SOURCE@ toggle flips the state, swayosd
does not. Do the real toggle with wpctl and keep an OSD via swayosd's
--custom-icon/--custom-message.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Setting only services.xserver.xkb.layout moves the graphical session but
leaves console.useXkbConfig unset, so the virtual-console keymap -- and the
systemd initrd the LUKS passphrase prompt runs in -- fall back to US (seen
on a Latitude 5410: X11 Layout gb, VC Keymap unset, LUKS prompt US).
Default console.useXkbConfig + console.earlySetup + boot.initrd.systemd.enable
distro-wide, so xkb.layout alone reaches the console and the encrypted-disk
prompt. Moved the latter two out of plymouth.nix -- they no longer hinge on
the splash being enabled. All mkDefault: an explicit console.keyMap or a
scripted initrd still wins.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two branding follow-ups, using the vendored legacy assets:
- fastfetch (modules/home/fastfetch.nix, nomarchy.fastfetch.enable): the
vector logo recolored to the palette accent and rendered to compact
block-art via chafa at build time (so it tracks the theme), fronting a
curated module list. Replaces the oversized legacy ASCII.
- Waybar logo glyph: vendor Nomarchy.ttf (modules/nixos/branding/),
install it via fonts.packages, and switch the summer-day/night menu
buttons to its U+F000 mark with `font-family: Nomarchy` pinned in CSS
(Nerd Fonts also occupy U+F000, so the pin is required). Glyph stored as
the JSON escape for robustness.
Also log a roadmap item for a quality-of-life alias collection.
Verified: font family resolves as "Nomarchy" and the ttf maps U+F000;
both summer jsonc parse; chafa logo builds headless; nix flake check passes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Stylix set polarity and the GTK theme but not the XDG portal's
org.freedesktop.appearance color-scheme, which GTK4/libadwaita and Qt6
apps actually read to choose dark vs light — so a light theme could still
render those apps dark (and vice-versa).
Set dconf org/gnome/desktop/interface color-scheme to
prefer-light/prefer-dark from theme.mode in stylix.nix.
xdg-desktop-portal-gtk already ships and programs.dconf is on system-side,
so the portal exposes it to apps.
Also log two branding follow-ups from the legacy assets: fastfetch ASCII
logo (logo.txt) and the Nomarchy.ttf glyph for the Waybar menu button.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Vendor the vector logo from the legacy branch
(modules/nixos/branding/logo.svg) and use it for the live ISO's isolinux
splash: rsvg-convert renders it, recolored to the palette accent and
centred on the theme base (from theme-state.json) at build time, wired via
isoImage.splashImage in hosts/live.nix. Verified the generated PNG by
building it in the sandbox.
UEFI/GRUB still uses the stock theme (grubTheme needs a full theme dir —
left on the roadmap), as does the distroId switch.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Round 2 of distro branding, text half: tuigreet now shows
'Welcome to <distroName>', and users.motd carries a branded banner that
doubles as a cheat sheet for the distro helpers (sys-update, home-update,
nomarchy-theme-sync, SUPER+?). Both key off system.nixos.distroName so the
brand name stays in one place.
Deferred (need an asset / a boot test): ISO splashImage/grubTheme art and
the distroId switch.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The shipped boot art was a fixed navy that vanished against dark theme
bases. Recolor each element from theme-state.json at build time (flat
fill, alpha preserved): foreground glyphs (logo, padlock, password dots)
→ text, the field/track boxes (entry, progress_box) → surface, and the
progress fill → accent. text/surface both contrast with base in either
polarity, so the splash now reads on light and dark themes alike.
Adds imagemagick as a build-time input to the theme derivation. Like the
background tint, this follows the theme as of the last system rebuild.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add modules/home/keys.nix (nomarchy.keys.enable, default on): one agent —
services.gpg-agent with enableSshSupport fronts SSH, so a single pinentry
handles every passphrase (GPG signing/decryption and SSH key unlocks).
pinentry-qt for a reliable native-Wayland prompt under Hyprland, themed by
Stylix's Qt config. Cache TTLs 30 min / 2 h.
SSH_AUTH_SOCK is exported by the agent's zsh integration (terminal git/ssh
is the supported path). gnome-keyring stays the Secret Service — modern
versions run no SSH agent, so there's no socket contention; screen lock
doesn't flush the agent cache.
nix flake check passes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Each nomarchy-menu module is now reachable directly, not just through the
SUPER+M picker: SUPER+CTRL+ V clipboard · C calc · W web · F files ·
N network · B bluetooth · S capture · A ask. Added to keybinds.nix (the
single source), so they render in the SUPER+? cheatsheet too. No key
collisions; nix flake check passes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
summer-day/summer-night showed all 10 workspace icons up-front. The cause:
legacy used the deprecated `persistent_workspaces` (underscore), which
current Waybar silently ignores — so legacy only ever showed *existing*
workspaces. The v1 port rewrote it to the modern `persistent-workspaces`
(hyphen), which Waybar honours, surfacing all ten as phantoms.
Drop the block from both summer waybar.jsonc whole-swaps. The other themes
render from waybar.nix, which never declared persistent-workspaces, so they
already only show existing workspaces — no change needed there.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
v1 forced an accent→accentAlt 45deg gradient on every theme's window
border; the legacy identity themes all used a *solid* border — accent for
nord/retro-82/lumon, the text tone for kanagawa/summer-day/summer-night.
Add a palette-resolved `border` field ({active, inactive}, each a palette
key or literal hex) to the theme schema with accent/overlay defaults in
theme.nix, consume it solid in hyprland.nix, and declare it in every
preset (and the live state). Declaring it everywhere is required because
`nomarchy-theme-sync apply` deep-merges presets — an omitted field would
stick across switches, like colors already are fully specified per theme.
All six identity themes resolve to their exact legacy active border
(verified: nord #81a1c1, retro-82 #faa968, lumon #f2fcff, kanagawa
#dcd7ba, summer-night #d3c6aa, summer-day #5c6a72). nix flake check passes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The powerline look (the `[]` separator glyph + bg-filled segments) didn't
read well. Switch to a flat prompt: bold-accent directory, warn git
branch, subtext git-status/duration, `❯` character — plain colored text,
no background fills, each module carrying its own trailing space so there
are no stray gaps. Also reference cmd_duration in `format` (it was
configured but never shown).
Generated starship.toml verified valid; nix flake check passes. Visual
pass still wants a live terminal.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Three quick known-issue fixes:
- yazi: 26.x made the fetcher `group` field required, so the git
prepend_fetchers failed to parse and yazi fell back to presets. Add
`group = "git"` to both entries (the git plugin's setup() only renders
the linemode — the fetcher still needs registering here).
- file-manager: xfce.exo and the three Thunar plugins moved to top-level;
use pkgs.xfce4-exo / pkgs.thunar-{archive-plugin,volman,media-tags-plugin}.
Clears the eval-time deprecation warnings.
- nix-ld: enable programs.nix-ld distro-wide so foreign dynamically-linked
binaries (downloaded tools, npx-fetched claude-code, language servers)
run without patchelf.
Also drops the now-stale claude-code mention from the allowUnfree comment
(the ask module uses npx). nix flake check passes; xfce warnings gone.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Build out the rofi menu system roadmap item:
- New nomarchy-menu modules: network (nmtui), bluetooth (blueman-manager),
capture (grim/slurp submenu → clipboard/file), ask (free-text → claude
CLI in a terminal), and keybinds (the SUPER+? cheatsheet).
- modules/home/keybinds.nix is now the single source of truth for both the
Hyprland bind strings and the cheatsheet, so the two can't drift —
hyprland.nix maps it into `bind`, rofi.nix renders the padded two-column
sheet (generated/mouse binds live in its `extra` rows).
- New binds: SUPER+Space (quick launch), SUPER+M (main menu), SUPER+?
(cheatsheet). SUPER+D stays rofi -show drun.
- Ask Claude pulls claude-code fresh from npm via `npx
@anthropic-ai/claude-code@latest` (the nixpkgs package lags model
releases); nodejs is bundled for npx.
nix flake check passes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
zsh is now the default login shell (programs.zsh.enable +
users.defaultUserShell, mkOverride 500 to beat bash.nix's mkDefault;
per-user shell= still overrides). modules/home/shell.nix configures the
interactive experience (nomarchy.shell.enable):
- zsh: completion, autosuggestions, syntax highlighting, big shared
history, autocd.
- starship prompt themed from the palette (a `nomarchy` palette built
from theme-state.json colors).
- bat as cat (theme "ansi" → tracks the terminal/theme palette), eza as
ls (icons + git + dirs-first), zoxide as cd (--cmd cd).
- rg/fd shipped as themselves — deliberately NOT aliased over grep/find
(flag differences surprise).
home.shell.enableZshIntegration = true is the single lever; starship,
eza, zoxide, ghostty and yazi all emit their zsh hooks from it (no
per-module edits). Verified: flake check green, default shell resolves
to zsh, .zshrc carries the starship/zoxide/eza/yazi hooks + aliases,
starship palette renders, all tools in the live ISO closure.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ships papirus-icon-theme and resolves an icon-theme name once in
theme.nix → nomarchy.theme.iconTheme: the JSON's optional `icons` field,
else Papirus-Dark/Light picked by `mode`. That single value feeds:
- Stylix `stylix.icons` → gtk.iconTheme (Thunar and GTK apps)
- rofi `show-icons` + `icon-theme` (the launcher now shows app icons)
A preset or theme-state.json can set `icons` to any theme in the icon
package to override per theme.
Verified: flake check green; iconTheme resolves Papirus-Dark for
tokyo-night and Papirus-Light for summer-day; gtk.enable true with
gtk.iconTheme.name set; papirus in home.packages and the live ISO
closure; rofi config.rasi carries icon-theme + show-icons:true. The
rendered icons need a real session to see.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The media keys called wpctl/brightnessctl directly with no visual
feedback — changing volume showed nothing on screen. Now they drive
swayosd-client, which performs the action AND shows a themed OSD.
- modules/home/osd.nix: services.swayosd, style.css generated from the
palette (base bg, accent progress/border); nomarchy.osd.enable.
- hyprland media binds → swayosd-client (--output-volume raise/lower/
mute-toggle, --input-volume mute-toggle, --brightness raise/lower).
- modules/nixos: services.udev.packages = [ swayosd ] installs the
backlight rule (chgrp video + g+w) so brightness works for video-group
users without root (the installer/live user is in video).
swaync is unaffected (it was already running — this was purely the
missing OSD). Verified: flake check green, swayosd.service bound to
graphical-session.target, hyprland config verifies, swayosd in the live
ISO closure. On-screen rendering needs a real session to confirm.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The "Applying theme — rebuilding…" toast used the default timeout, so it
vanished seconds into a multi-minute home-manager switch — leaving the
user with no signal and the impression the selection failed. Now it's
persistent (timeout 0) and all theme notifications share a synchronous
tag, so swaync REPLACES it in place with "Theme applied ✓" (or a critical
failure toast that persists) when the rebuild finishes.
notify() grew persistent/urgency/summary params. Honest indeterminate
feedback — HM exposes no percentage, so no fake progress bar.
Verified: py_compile clean; the full apply→switch→notify path runs end to
end (dummy NOMARCHY_REBUILD). The in-place replacement is visible only in
a real swaync session.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
themes/<slug>/rofi.rasi whole-swaps for the two themes that had a
distinct launcher look in the legacy branch — inverted window (bg=fg),
green inputbar, yellow bottom-border accent, rounded. Adapted: dropped
the configuration{} block (rofi.nix owns modi/terminal/show-icons) and
swapped the unshipped Iosevka 17 for a shipped Nerd Font.
Verified: both .rasi parse under `rofi -dump-theme`; the whole-swap
selects per active theme (summer-night → #2d353b/#a7c080, summer-day →
#fdf6e3, others → the generated palette theme); flake check green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
rofi 2.0 landed in nixpkgs 26.05 with native Wayland (mainline upstreamed
the lbonn rofi-wayland fork — `wayland enabled`), so the original reason
to prefer fuzzel (rofi being X11-only / needing a community fork) is gone.
rofi's `.rasi` is far more expressive than fuzzel's flat INI, which lets
the launcher be a proper themed hero surface and revives the legacy
per-theme launcher designs.
- modules/home/rofi.nix replaces fuzzel.nix: per-element theme generated
from theme-state.json (accent border, highlighted selection, rounded
inputbar) via lib.formats.rasi.mkLiteral; themes/<slug>/rofi.rasi
whole-swap; the nomarchy-menu dispatcher re-pointed to `rofi -dmenu`
(power/theme/clipboard/calc with -mesg/files/web + root picker).
- SUPER+D is now `rofi -show drun`; the menu binds are unchanged.
- nomarchy.fuzzel.enable → nomarchy.rofi.enable; fuzzel dropped from
home.packages. Pango handles nf-glyph fallback (no explicit font list
needed as fuzzel's fcft did). show-icons off until an icon theme ships.
Verified: flake check green, HM generation builds, the generated
theme.rasi parses under `rofi -dump-theme`, Hyprland config verifies.
Not verified: rofi's on-screen rendering needs a real session.
Docs: README menu/override/roadmap sections and the module tree updated;
the per-theme launcher roadmap item now points at porting the legacy
rofi.rasi designs (the whole-swap mechanism is in place).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Legacy shipped only Thunar (and a stray nautilus extension, mid-migration).
This pairs a keyboard-driven TUI flagship that fits the distro's identity
with the GUI half for point-and-click + the "open folder" handler.
yazi (modules/home/yazi.nix, nomarchy.yazi.enable, SUPER+E, `y` shell
wrapper cd's on exit): themed from theme-state.json (mgr/mode/status
mapped from the palette; merges over yazi's defaults so it tracks the
theme) with a curated, correctly-wired plugin set —
full-border, git (setup + prepend_fetchers), smart-enter (l/Enter),
chmod (cm), mount (M), compress (caa), mediainfo (audio/subtitle only —
images/videos keep native thumbnails), glow (markdown previews).
Preview/tooling deps: ffmpegthumbnailer, poppler-utils, p7zip, jq, fd,
ripgrep, fzf, chafa, mediainfo, glow.
Thunar (modules/nixos/file-manager.nix, nomarchy.system.fileManager.enable):
GTK file manager (Stylix-themed) + archive/volman/media-tags plugins +
gvfs/tumbler/udisks2 backends + thumbnailers + exo. udisks2 also backs
yazi's mount plugin.
Verified: flake check green, HM generation builds (yazi TOML/init.lua
render, all 8 plugins resolve), every package present in the live ISO
closure (offline install draws from it — no source-build leak).
Not verified: yazi's runtime preview/plugin behaviour needs a real
session (TUI; can't drive in CI here).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Nomarchy's Hyprland/Ghostty/Waybar settings were set at normal priority,
so a downstream `home.nix` setting any value Nomarchy already set threw a
"conflicting definition values" error — overriding required lib.mkForce,
contradicting the README's "mkDefault throughout" claim.
Now: behaviour/chrome leaves (input, misc, monitor, animations, dwindle,
gesture, decoration.blur sizing, ghostty padding/decoration, waybar
settings+style) are lib.mkDefault — a plain home.nix assignment wins.
Appearance values that flow from theme-state.json (gaps, colors,
rounding, opacity, fonts) stay at normal priority on purpose: change them
via nomarchy-theme-sync (their source of truth) or lib.mkForce to
hardcode. bind/exec-once lists stay normal priority so additions
concatenate rather than replace.
Verified: behaviour/ghostty plain overrides build; theme values still
conflict (steering to the CLI); default rendered config is byte-identical.
New docs/OVERRIDES.md with the appearance→CLI / behaviour→home.nix /
component→toggle model and worked examples; linked from the README.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Super+Escape didn't fire on real hardware (Latitude 5410) while its
neighbour binds did — the chord gets swallowed before reaching
Hyprland's dispatcher on some setups. SUPER+X is unambiguous.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Pre-activation failure ("desktop pre-activation failed"): the in-chroot
HM build hard-coded `--option substituters ""`, which only works on
OFFLINE installs (where the ISO store is copied in). Online installs
(now common — wifi works) had no pins and built from source. Fix: build
the generation in the live env (its store always has the pins), nix copy
the closure to the target, chroot only activates. In-chroot build kept
as fallback. Uses path: (not git+file:) to sidestep libgit2 ownership.
- Read-only home.nix: templates come out of the store mode 0444 and cp
preserves it — chmod -R u+w after staging so the user can edit it.
- Keyboard + locale selection: gum prompts (localectl layouts/variants +
curated UTF-8 locale list) and NOMARCHY_LOCALE/KB_LAYOUT/KB_VARIANT for
unattended. One choice flows everywhere — services.xserver.xkb (source
of truth) + console.useXkbConfig (tty/greeter) + boot.initrd.systemd
(so the LUKS passphrase prompt uses the layout; script initrd can't) +
the generated home.nix's nomarchy.keyboard.* for the Hyprland session.
- test-install.sh: NOMARCHY_OVMF env override for sandboxed OVMF paths.
Verified end-to-end in QEMU: offline LUKS install completes, boots to a
themed desktop (no autogenerated-config banner), Plymouth renders the LUKS
prompt on the theme-tinted background.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Themed fuzzel (modules/home/fuzzel.nix): palette/fonts/border from the
JSON, themes/<slug>/fuzzel.ini whole-swap mechanism. Plus the
nomarchy-menu dispatcher: root picker · power (SUPER+Escape) · theme
(SUPER+T) · clipboard (SUPER+CTRL+V, cliphist) · calc (qalc) · files
(fd→xdg-open) · web (DuckDuckGo).
- swaync notifications themed from the JSON (SUPER+N) — until now nothing
rendered notify-send (theme toasts, font warnings, welcome msg).
- hyprlock + hypridle (modules/home/idle.nix): lock 5min, dpms off 10,
suspend 30; enables the power menu's Lock entry. cliphist daemon on.
- NetworkManager applet in waybar's tray (preferStatusNotifierItems);
network module on-click → $TERMINAL -e nmtui.
- nomarchy.keyboard.layout/.variant option → Hyprland kb_layout/kb_variant
(the installer writes it; pairs with system-side xkb for tty/LUKS).
- Media-key/audio on-click moved to wpctl (pamixer was inconsistent).
- Theme parity: summer-day/night carry their legacy bar LAYOUTS as
waybar.jsonc whole-swaps (the original import took waybar.css but not
config.jsonc, so identity themes styled nonexistent modules). Dead
legacy script-modules dropped, Nerd-Fonts-v2 codepoints → FontAwesome/v3
(font-awesome now shipped), logo buttons open nomarchy-menu.
New toggles: nomarchy.{fuzzel,swaync,idle}.enable (all default true).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Plymouth boot splash ported from the legacy branch (modules/nixos/
plymouth/): logo + eased progress + LUKS entry, background tinted from
theme-state.json via the new nomarchy.system.stateFile (wired by
mkFlake/lib.nix; null → Tokyo Night fallback). Default on; OFF on the
live ISO (boot-message visibility on the install medium). Pulls
boot.initrd.systemd, which also drives the keyboard-at-LUKS feature.
- system.nixos.distroName = "Nomarchy" (os-release PRETTY_NAME,
systemd-boot entries, ISO menu label). distroId left "nixos" (feeds
DEFAULT_HOSTNAME + upstream isNixos checks — roadmapped).
- nixpkgs.config.allowUnfree distro-wide (here + both import-nixpkgs
sites) — unblocks claude-code for the menu's ask-Claude module.
- systemd-boot.configurationLimit = 10 so entries don't fill the ESP.
- Live ISO: nomarchy.idle.enable = false — hypridle was suspending the
VM mid-install (the install-hung regression); installed systems keep it.
- flake.nix offline pins (verified 0-leak via a foreign-identity
gap-analysis probe): the repo's own standalone HM gen + inputDerivations,
mustache-go + stdenv (stylix re-renders base16 per switch), microcode-amd/
intel (enableRedistributableFirmware activated updateMicrocode →
source-build cascade), buildEnv's builder.pl, findXMLCatalogs, and the
representativeInstall mirror (xkb/initrd-systemd/microcode).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
installation-cd-minimal forces fonts.fontconfig.enable = false
(mkOverride 500) to slim a console-only installer — but this ISO is the
desktop. Without fontconfig no family resolves: Waybar's nf-md icons
rendered as tofu boxes and Ghostty silently fell back to the wrong
font (reported on the Latitude 5410, reproduced + fix verified in QEMU
via VNC screenshots: icons render, fc-match resolves JetBrainsMono
Nerd Font). Also restore xdg icons/mime/autostart and man pages.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
nixpkgs.config.allowUnfree in the system module (covers the live ISO
via useGlobalPkgs and every mkFlake machine) plus the two explicit
`import nixpkgs` sites (repo + lib.nix) so the standalone HM desktop
sees the same package set. Unblocks claude-code for the menu system's
ask-Claude module, vendor drivers, etc. Opt out with
`nixpkgs.config = lib.mkForce { allowUnfree = false; }`.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Resolves the old Walker/Lua previews-vs-simplicity question in favor of
fuzzel: already shipped, nested menus via dmenu mode, INI theme bakeable
from theme-state.json like every other app.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Roadmap items 1+2 plus the issues found testing on a Latitude 5410:
- Ship the 10 most popular Nerd Fonts by default (iosevka swapped for
mononoki: 1.1 GB vs 27 MB) and warn from nomarchy-theme-sync when a
configured fonts.mono/fonts.ui family isn't installed (fc-match) —
fontconfig substitutes silently otherwise.
- hardware.enableRedistributableFirmware: installed systems shipped NO
firmware blobs (wifi/SOF audio/BT) — nixos-generate-config only emits
microcode lines referencing this flag, it never sets it.
- Live ISO wifi: networking.wireless.enable = mkForce false killed
NetworkManager's supplicant — since 26.05 the NM module drives its
wifi backend THROUGH networking.wireless (dbusControlled). Devices
vanished from nmtui with iwlwifi loaded and no rfkill block.
- Hyprland 0.55: launch sessions via start-hyprland (watchdog; bare
binary warns), add the new gesture keyword (workspace_swipe is gone —
touchpads had no gestures at all), media keys via wpctl/bindel/bindl
plus the missing mic/play/next/prev binds.
- Offline theme switching: pin mustache-go + stdenv + the repo's own
standalone HM generation (incl. home-files inputDerivation) into the
ISO — stylix re-renders base16 templates per switch and an offline
`apply` cascaded into building stdenv from source (1107 drvs).
Verified with tools/vm/gap-analysis.py: 17 config drvs, zero fetches.
- Stylix: track release-26.05 (kills the HM version-skew warning and
the stale home-manager follows), lock updated.
- Roadmap: swaync (no notification daemon ships today).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
main now carries the new distro (theme-state.json engine, mkFlake
downstream API, nomarchy-install with verified offline installs). The
old distro's history stays reachable here and lives on as the `legacy`
branch. Tree is identical to refactor/v1 @ 37819bb.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Installed machines and the downstream template now resolve nomarchy from
git+https://git.bemagri.xyz/bernardo/nomarchy.git?ref=v1 (the lock's
original node carries the ref too), so `nix flake update` follows the
release branch instead of the forge's default branch. README quickstart
points at the real forge.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Promotes the throwaway harness that verified the installer into tools/:
- tools/vm/qmp.py QMP keystroke injection / typing / quit
- tools/vm/vncshot.py GL-safe screenshots via VNC readback (QMP
screendump shows "no surface" with virtio-vga-gl)
- tools/vm/gap-analysis.py drv-graph diff that converged the offline
pin set; run it when offline installs build
from source
- tools/test-install.sh the full offline regression: build ISO, boot
offline, unattended LUKS+swap install via a
config disk (typed long commands drop keys),
wait for poweroff, boot the installed disk,
screenshot the first boot for visual verdict
docs/TESTING.md §4 now points at the script.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The pre-activation log (now persisted) showed activate dying at its
profile operation: as the target user with no daemon in the chroot,
nix can't open the store db ("big-lock: Permission denied"). Run a
temporary nix-daemon inside the chroot and point activate at it via
NIX_REMOTE=daemon. HOME_MANAGER_BACKUP_EXT=bak makes file collisions
(e.g. a stray autogenerated hyprland.conf) move aside instead of
aborting. Verified in the chroot on a real installed disk: HM symlinks
land, collision backed up.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The HM pre-activation never ran: the script was written to /mnt/tmp and
nixos-enter mounts a fresh tmpfs over /tmp inside the chroot, so `bash
/tmp/...` found nothing and the best-effort guard swallowed it. Verified
interactively that every inner step (path: build as root, runuser leg)
works in the chroot — only the staging path was broken. Now staged in
/root, with set -x logging persisted to /var/log/nomarchy-hm-preactivate.log
on the target and the tail echoed into the install transcript on failure.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The chown fix made the flake user-owned, and root's libgit2 then refuses
git+file fetches of it ("not owned by current user") — which silently
killed the seeding step under set -e and would equally break
nixos-install --flake. Use path: refs for the live-side archive and the
nixos-install invocation (path flakes read flake.lock, skip libgit2),
and make seeding failure non-fatal as intended.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
End-to-end VM verification found the installed system perfect (LUKS,
auto-login, swapfile+resume, snapper) except: ~/.nomarchy was root-owned,
so libgit2 refused every `home-manager switch` ("repository not owned by
current user"). chown 1000:users after generation (the account doesn't
exist in the target yet — numeric ids).
The chroot pre-activation now builds via path: instead of git+file —
no libgit2, no ownership sensitivity — and the fallback message suggests
-b bak (a default-config Hyprland session leaves an autogenerated
hyprland.conf in the way otherwise; observed live).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Substituter plumbing through nixos-install proved unreliable (env config
is overridden by its hardcoded flags; the forwarded --substituters flag
still left the plan building gcc from source). Make every ISO path valid
in the target store with one nix copy --all before nixos-install — the
in-target build then resolves everything locally and only the per-machine
config drvs are built.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
nixos-install hardcodes --extra-substituters "auto?trusted=1" as a CLI
flag, and flags override NIX_CONFIG for the same setting — so the env
fix never reached the in-target build. nixos-install forwards a
--substituters flag verbatim; use that.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
nixos-install builds with --store /mnt and passes its own substituter as
"auto?trusted=1", which in that context resolves to the target store
itself — so nothing flowed from the ISO and nix fell back to a
full-source bootstrap (gcc/python/bash tarball fetches → offline death).
Host-store simulation (fresh local?root, substituters cleared) proves
"daemon?trusted=1" makes the whole install substitute + build offline:
1125 paths copied, only the per-machine config drvs built locally.
Also drop --offline from the chroot HM build (that flag disables ALL
substituters; plain empty substituters does what we want without it).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`nix flake archive --to` rejects locally-evaluated source paths ("lacks
a signature by a trusted key"); enumerate the archive tree as JSON and
copy with --no-check-sigs instead.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Round 7 in-VM: autodetection emitted common-cpu-intel, whose nixos-hardware
module pulls the whole Intel graphics stack (intel-media-driver,
compute-runtime, libva, level-zero…) — none of it on the ISO, so the
offline install built graphics drivers from source. The representative
pin now imports the common-* profiles (intel+amd cpu/gpu, laptop, ssd,
pc), covering what the detector emits on ordinary machines.
Model-specific profiles may still need network.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Walking the install-shaped toplevel's drv graph against the ISO's actual
path set (tools/gap analysis) found why offline installs kept building
from source:
- system-path is rebuilt on EVERY machine: buildEnv's package-list order
follows module-graph order, which any hardware profile reshuffles →
pin its builders (texinfo, getconf)
- greetd.toml embeds the auto-login user and is generated by remarshal;
building remarshal offline drags its test closure (matplotlib → ffmpeg
→ x265…, ~1900 drvs) → pin pkgs.remarshal
- second-level tools for the always-rebuilt drvs: kmod.dev +
nukeReferences (modules-shrunk), xorg.lndir (unit dirs), libxslt.bin
(dbus config), python3Minimal (etc)
- the representative pin now has a vfat /boot like a real install
(mtools/dosfstools enter systemPackages via the vfat fs-type gate)
Verified on the host: the remaining offline build set for a foreign
username/hostname/UUID config is 66 trivial config drvs + eval-time file
copies from the pinned nixpkgs tree — zero network. Same check for the
per-username HM activation: 15 drvs, offline-clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The full system build closure adds ~134 GB to the store and would have
produced an unusable multi-ten-GB ISO. Back to targeted inputDerivation
pins; the still-missing second-level build tools get pinned explicitly
next, from a clean committed-tree gap analysis.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Round 6 still hit a from-source cascade: every per-machine drv (units,
initrd pieces, dbus config, modules-shrunk) needs its own build tools
(lndir, builder.pl, kmod-dev, nuke-refs, libxslt-bin, …), one level
deeper than targeted inputDerivation pins reach. Use the documented
mechanism instead: system.includeBuildDependencies = true on the
representative install pin. Bigger ISO, networkless installs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Offline nixos-install still built from source: the drvs that embed the
user's hostname/username/UUIDs (etc, initrd, toplevel, HM activation)
must be REBUILT, and their builders need tools outside every runtime
closure (etc → python3-minimal, initrd → packing tools). Pin each drv's
inputDerivation into the ISO — the standard nixos-test offline trick.
Also: with no network every substituter query is a DNS retry storm that
ends in a nix goal.cc assertion crash (core dump observed); the installer
now probes connectivity and disables substituters when offline, both on
the live system and inside the nixos-enter HM pre-activation.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The offline nixos-install failed building from source: the generated
config enables snapper/auto-login/LUKS-initrd/swapfile-resume on a BTRFS
root, none of which the bare template (ext4 placeholder!) pulls in — so
snapper, nixos-rebuild-snap, btrfs-progs-lib and cryptsetup-bin were
absent from the ISO. The pinned system now mirrors the installer's output
(closure-diff verified).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Lock follows paths are absolute from the root node; nomarchy's nodes move
one level down in the composed downstream lock, so ["nixpkgs"] must
become ["nomarchy","nixpkgs"] — otherwise every follows dangles
("input 'nomarchy/home-manager/nixpkgs' follows a non-existent input").
Verified: the composed lock now resolves the full input tree with
`nix flake metadata --offline`.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
disko builds its format/mount script at install time; offline, the
script's tool closure (file, which, wrapper hooks) wasn't on the ISO and
nix tried to build it from source — which needs the network. Pre-building
one representative script (luks + swap variant) pins every input; the
user's actual invocation then differs only in the final text-generation
derivation, which builds offline in seconds.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Found by running the unattended install in an offline QEMU VM:
- compgen is missing from nixpkgs' non-interactive bash (the package
shebang) — replaced with plain glob tests in hardware-db and prewipe
- disko's eval resolves <nixpkgs> via NIX_PATH (a dead channel on the
ISO) and tried channels.nixos.org — export NIX_PATH to the pinned
nixpkgs source and point the flake registry at an empty baked file
- lock nomarchy by path (the source the ISO carries) instead of
git+https: git inputs clone even when narHash is known, which kills
offline installs; `original` keeps the forge URL so a later
`nix flake update` re-resolves normally. Works from dirty-tree ISOs too.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- LUKS2 is the installer default; in exchange the generated config sets
the new nomarchy.system.greeter.autoLogin (greetd initial_session) —
the disk passphrase already gates the machine.
- @snapshots subvolume + nomarchy.system.snapper.enable: hourly/daily
timeline snapshots of / and the nixos-rebuild-snap helper, ported from
the previous iteration (3bdfc35), guarded to no-op on non-BTRFS roots.
- @swap subvolume with a swapfile sized to RAM by default (disko
mkswapfile handles NOCOW); the installer computes the resume offset
and wires boot.resumeDevice + resume_offset for hibernation.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ported from the previous iteration's installer (3bdfc35), adapted to the
mkFlake world. gum TUI: disk pick, optional LUKS2, user/hostname/timezone,
DMI → nixos-hardware autodetection (hardware-db.sh). disko partitions
GPT + 1 GiB ESP + BTRFS subvolumes; the generated machine flake lands at
~user/.nomarchy (one mkFlake call, /etc/nixos symlinks to it) and
nixos-install makes it bootable (UEFI/systemd-boot, v1 single disk).
Offline by construction: the target flake.lock is composed from the rev
the ISO was built from (compose-lock.py), `nix flake archive` seeds the
target store, and the ISO pre-builds the template system + desktop.
First boot lands themed: the HM generation is pre-activated in chroot.
mkFlake grows two things for this: hardwareProfile now also takes a list
(autodetection emits several common-* modules), and installed systems get
the standalone home-manager CLI from the pinned input. Unattended mode
(NOMARCHY_UNATTENDED=1 + env) documented in docs/TESTING.md §4.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Downstream users now own only system.nix and home.nix; their flake.nix is
generated once from the template and never hand-edited. mkFlake (lib.nix)
wires both layers and maps hardwareProfile = "<name>" to nixos-hardware
modules (new input, pinned here, nixpkgs follows ours; unknown names fail
with did-you-mean suggestions). Flake checks evaluate the template through
mkFlake — including a real profile — so drift fails `nix flake check`.
Also from the live-VM testing session: install the home-manager CLI in the
live ISO and pin flake inputs transitively (offline theme switching needs
stylix's own inputs too), plus swww→awww doc updates.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Found by booting the live ISO (docs/TESTING.md checklist):
- pin HM configType = "hyprlang" (26.05 stateVersion defaults to the new
Lua config and renders these settings as broken hl.$mod(...) calls,
booting Hyprland into emergency mode)
- drop dwindle:pseudotile (removed in Hyprland 0.55; aborts config parse)
- disable splash rendering alongside the logo
- launch awww-daemon and call awww with swww fallback (nixpkgs' swww is
the renamed awww fork; no `swww` binary exists anymore)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Full replacement of the previous iteration, rebuilt around three ideas:
- Pure evaluation: theme-state.json lives inside the flake and is read
via the nomarchy.stateFile option — no --impure, ever.
- All-Home-Manager theming: `nomarchy-theme-sync apply` writes the JSON
and runs `home-manager switch`; every theme change is one atomic,
rollbackable generation. Wallpaper (swww) is the sole runtime piece.
- Flat, downstream-first layout: modules/{nixos,home} with one
options.nix each, exported as nixosModules/homeModules + overlay +
flake template; system (nixos-rebuild) and desktop (home-manager
switch) rebuild paths are fully split.
Ships 21 themes imported from the previous iteration (palettes,
wallpapers, btop themes, six whole-swap Waybar identities), Stylix for
the GTK/Qt/cursor long tail, a live ISO target with offline theme
switching, and docs/TESTING.md with the QEMU verification workflow.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A short, task-indexed guide for navigating the repo: the core/features/themes
mental model, the two app-config namespaces, the four script locations (incl.
themes/engine/scripts/), the build/test loop, how theming works, and a
Walker+elephant deep-dive covering the dmenu vs custom-provider menu paths.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
nomarchy-launch-walker guarded its manual elephant start with
`pgrep -x elephant`, which never matches the Home Manager service's actual
process name `.elephant-wrapped` (Linux truncates comm to 15 chars). So every
menu invocation spawned a second elephant that races elephant.service for the
socket — a latent source of menus intermittently coming up empty.
Gate on `systemctl --user is-active elephant.service` first, with a working
`pgrep -f elephant-wrapped` fallback for non-service setups; do the same for
walker.service. Verified in a headless system.build.vm: opening the theme menu
now keeps the elephant process count at 1 (was 2) and the menu still renders.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
These existed only to ease muscle-memory from Arch-based distros and add
no behavior on a Nix-native system:
- nomarchy-pkg-install — alias for nomarchy-pkg-add
- nomarchy-pkg-drop — alias for nomarchy-pkg-remove
- nomarchy-pkg-aur-add — stub that only printed "AUR doesn't exist on NixOS"
The single code caller (nomarchy-voxtype-remove) was removed in the prior
commit, so the shims have no remaining references. SKILL.md updated to use
nomarchy-pkg-add and to point at `nix search nixpkgs` instead of the AUR.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Voxtype voice-typing was a full feature (5 scripts, a waybar widget, a
keybind, and a NixOS option) that this install does not use. The
nomarchy.system.voxtype option had no consumer — nothing read it — so the
widget and SUPER+CTRL+X bind were never actually gated by it.
Removes the scripts, default config, the SUPER+CTRL+X bind, the waybar
module (main + summer-night theme + css), the dead option, the installer
comment, and the docs. SCRIPTS.md/KEYBINDINGS.md regenerated; OPTIONS.md
and SKILL.md hand-updated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two scripts named nomarchy-update shipped from different derivations
(core/system/scripts + features/scripts/utils), both landing on PATH so
which one ran depended on PATH ordering. The core/system one never stripped
the install-time `?rev=` pin and rebuilt a nonexistent `#default` attr,
silently keeping installs frozen instead of following rolling main. Keep the
features/scripts/utils version (unpins, queries upstream HEAD, calls
nomarchy-sys-update) and drop the stale one. Regenerated docs/SCRIPTS.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The nomarchy-live ISO imported ./core and hosts/nomarchy-live.nix but not
core/system/vm-guest.nix, which is the only place spice-vdagentd/qemuGuest
were enabled. Without those agents a VM's virtio-gpu only advertises its
1024x768 fallback mode, so Hyprland's `monitor = , highres, auto` rule picks
that as the highest mode and the live session boots tiny.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The impermanence module asserts that the persistent-storage volume and
every volume it bind-mounts into are available in early boot. Persisted
user dirs land under /home, so both /persist and the @home subvolume
must have neededForBoot = true. disko and nixos-generate-config leave it
at the default (false), so a fresh install with impermanence enabled
aborted with:
All filesystems used for persistent storage must have the option
"neededForBoot" set to true.
Please fix the following filesystems: /persist /home
Declare neededForBoot in the module that imposes the requirement.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
pam_unix rejects empty passwords by default, so if hypridle locked the
live session there was nothing the user could type to unlock it. Set
allowNullPassword on the hyprlock PAM service so a bare Enter dismisses
the locker, matching the empty live account passwords. Scoped to the
live host so installed systems still reject empty unlocks.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A multi-disk install builds one LUKS container per drive joined into a
single multi-device BTRFS (-d single -m raid1). nixos-generate-config
only emits boot.initrd.luks.devices for the main drive — every subvolume
mounts through /dev/mapper/crypted_main, so the other members are
invisible to it. The installed system then unlocked only the main drive,
the array never assembled, and boot hung. Invisible single-disk, fatal
multi-disk (matches a real-hardware report).
generate_flake_config now writes, into hardware-selection.nix, a
boot.initrd.luks.devices entry for each extra drive (UUID resolved via
disko's partlabel; mapper name mirrors disko-config.nix's sanitize) and
re-asserts the x-systemd.requires= mount options disko sets but which
nixos-generate-config strips.
Verified end-to-end in a 3-disk UEFI QEMU VM: reproduced the bug, applied
the fix, ran a real nixos-install onto the array, and rebooted from the
disks — one passphrase unlocked all three containers, the 3-device BTRFS
assembled, and the system reached login.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`generate_state` loads `nixpkgs.lib` for the state-schema eval via
`builtins.getFlake "$NOMARCHY_REPO"`. On the live ISO that path is a
symlink chain (/etc/nomarchy -> /etc/static/nomarchy -> /nix/store/…-source),
and Nix 2.31+ rejects getFlake on a symlink ("path '…-source' is a
symlink"), aborting every real-hardware install right after hardware
config generation — single or multi disk.
Resolve the repo to its real store directory with `realpath` at
detection so getFlake receives a plain directory. The dev-checkout
branch already used realpath, so only the live-ISO path was affected.
Reproduced and fixed in a live VM (Nix 2.31.4).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The default theme's clock#date wired on-click to `kitty calcurse`, but
kitty isn't a default-installed app (alacritty is the terminal) and
calcurse isn't installed anywhere — so clicking the date did nothing.
The default config.jsonc uses xdg-terminal-exec/alacritty for its
clicks and summer-day's date has no on-click. Drop the dead action
(tooltip: false), matching summer-day.
Found during the Pillar 9 Component 6 (apps) pass, which confirmed
btop/ghostty/kitty/lazygit/tmux ship config-only by design (the
installer/user picks the binary) — not a bug. Logged two follow-ups:
summer-day's wofi --term=kitty launcher, and shipping a calendar app.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The summer-night waybar config defined a custom/battery module
(exec nomarchy-battery-status) but never listed it in any modules-*
slot, so it was orphaned and never rendered. summer-night is the
default theme, so a laptop on a stock install had no battery
indicator — the default fallback config and the summer-day theme
both place battery in modules-right; only summer-night omitted it.
Add custom/battery to summer-night's modules-right. The formFactor
filter (laptopOnlyModules) already lists custom/battery, so it shows
on laptop and is dropped on desktop. Found during the Pillar 9
Component 9 (form-factor split) verification pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Found by driving the menu's observable actions live in a VM with the real
session env imported. All three are also bound to keybindings.
1. nomarchy-hyprland-window-single-square-aspect-toggle (1-Window Ratio,
SUPER CTRL BACKSPACE): read/set layout:single_window_aspect_ratio, which
is "no such option" in Hyprland 0.52.1 — the option lives under dwindle:.
The getoption returned nothing and the keyword set was silently dropped,
so the toggle did nothing. Point it at dwindle:single_window_aspect_ratio.
2. nomarchy-hyprland-workspace-layout-toggle (Workspace Layout, SUPER L):
read .tiledLayout off `hyprctl activeworkspace -j` (no such key → always
null) and switched to "scrolling" (not a built-in layout — hyprctl
layouts lists only dwindle/master), so it silently no-op'd. Rewrite to
toggle the real general:layout between dwindle and master.
3. show_setup_menu offered Power Profile unconditionally, but powerprofilesctl
is absent everywhere (power-profiles-daemon is force-off on laptop in
favour of TLP and never enabled on desktop), so the entry died with
"command not found". Gate it behind `command -v powerprofilesctl`.
Verified (1) and (2) against live Hyprland in the VM: aspect flips
[0,0]<->[1,1], layout flips dwindle<->master.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
All three confirmed real on inspection.
1. nomarchy-refresh-config was dead on every system: it read
/etc/nixos/nomarchy/{core/home/config,features} (the source tree never
lands there — only /etc/nomarchy on VM-guest/live ISO) and fell back to
~/.local/share/nomarchy/config, which nothing deployed despite SKILL.md
documenting it. Deploy the pristine core/home/config tree to
~/.local/share/nomarchy/config via xdg.dataFile and read from there, so it
works without a source checkout; its live caller nomarchy-refresh-fastfetch
now succeeds. Fix two stale SKILL.md examples (waybar/, hypr/) that pointed
at non-existent stock paths.
2. nomarchy-docs-scripts shipped a stale divergent copy in
features/scripts/utils/ (in the user nomarchy-system-scripts package)
beside the canonical bin/utils dev tool — it errored outside the repo.
Grounding it surfaced the identical bug in nomarchy-docs-keybindings.
Delete both duplicates, add them to the generator's self-reference
denylist so they don't show as false `missing`, regenerate docs/SCRIPTS.md.
3. Home ~/.config/nomarchy/state.json was never seeded on non-installer
systems, so nomarchy-installed-summary rendered "—" for theme/font/panel.
Add an idempotent home-manager activation seed in core/home/state.nix that
backfills the resolved values (defaults * existing — user choices and
runtime-only keys like welcome_done always win).
Verified: flake check + full eval matrix clean; refresh-config happy/error
paths and the seed deep-merge proven by hand.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The "mkDefault everywhere user might override" rule is what caused the
home.packages bug. Clarify: mkDefault is for scalars; on list/attrset
options that other modules contribute to, a mkDefault def is dropped
entirely (filterOverrides) rather than merged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Same footgun as the home.packages fix, found by sweeping the tree for
`mkDefault` on list options that other modules contribute to at normal
priority (so filterOverrides discards the mkDefault def entirely):
* themes/engine/sddm.nix — environment.systemPackages = mkDefault
[ nomarchy-sddm-theme ] was dropped, so the SDDM greeter theme package
was never installed (unthemed login).
* themes/engine/plymouth.nix — boot.kernelParams = mkDefault [ quiet
splash loglevel=3 … ] was dropped, so boots weren't quiet/clean.
* features/desktop/waybar/default.nix — home.packages = mkDefault
[ font-awesome ] was dropped, so waybar's icon font was missing.
Verified via eval: nomarchy-sddm-theme now in systemPackages, "quiet" in
kernelParams, font-awesome in home.packages. Left the genuinely-safe
single-definition mkDefaults alone (plymouth.themePackages,
resolved.fallbackDns, hyprsunset.extraArgs) and the hybridGPU videoDrivers
mkDefault (it outranks nixpkgs' mkOptionDefault on real hardware).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Chasing a "DBus.Error … name is not activatable" from notify-send (mako),
the root cause turned out to be far bigger: home.packages in
features/default.nix was wrapped in lib.mkDefault. home.packages is a
list, and the module system's filterOverrides keeps only the
highest-priority definitions — so the moment any other module sets
home.packages at normal priority (features/scripts/default.nix's
`[ nomarchy-scripts ]`, the installer's profile packages), this entire
mkDefault list was DROPPED. Every install was silently missing firefox,
thunar, imv, mpv, swww, mako, hyprlock, rofi, etc. — which broke
notifications (mako never installed → exec-once fails → notify-send dead)
and the lock screen (hyprlock missing), and hid a stale `rofi-wayland`
reference (merged into `rofi` upstream) that only errored once the list
was live again. Remove the mkDefault so the list merges (home.packages =
52 pkgs, up from 31; mako/hyprlock/firefox/… now present) and fix
rofi-wayland -> rofi.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pillar 9 VM test: after the menu-registration fix, the walker background
selector menu opened but still showed "No Results". Its GetEntries ran
`find <dir> -type f`, but home-manager deploys the per-theme background
images as symlinks into the nix store, and `-type f` doesn't follow
symlinks. Add -L (the sibling nomarchy_themes.lua already uses `find -L`).
Verified in the VM: `find -type f` -> 0, `find -L` -> the backgrounds.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pillar 9 VM test: the walker theme picker (and background selector)
returned "No Results". Root cause: nomarchy_themes.lua and
nomarchy_background_selector.lua (elephant lua menu providers named
"nomarchythemes" / "nomarchyBackgroundSelector") were deployed via the
bulk nomarchy config to ~/.config/nomarchy/default/elephant/ — outside
elephant's provider search path, so elephant never registered them and
`nomarchy-launch-walker -m menus:nomarchythemes` (used by nomarchy-theme,
nomarchy-wallpaper, and nomarchy-menu Style submenu) had no backing menu.
Move them into the elephant config (features/apps/elephant/config/menus/)
so they deploy to ~/.config/elephant/menus/. Verified on a fresh VM boot:
`elephant listproviders` now lists menus:nomarchythemes +
menus:nomarchyBackgroundSelector, and the walker theme picker renders all
21 palettes with per-theme preview images.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pillar 9 VM script sweep: nomarchy-haptic-touchpad failed with
"env: 'python3': No such file or directory". It's a python3 script
(#!/usr/bin/env python3) wired as systemd.services.nomarchy-haptic-touchpad
in core/system/hardware.nix (Dell XPS haptic feedback), run from the
nomarchy-system-scripts wrapper — but python3 wasn't in systemScriptDeps,
so the wrapper's PATH lacked it and the service died on every XPS. Add
python3 (stdlib-only script, plain pkgs.python3 suffices). Verified the
rebuilt wrapper resolves python3 and the script runs to its clean
"no device found" exit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New pillar to drive the distro running in a VM and verify what actually
works vs. what only looks right in source: run every nomarchy-* command,
walk every menu entry, switch through all 22 palettes and eyeball
rendering, fire every keybinding, drive the installer. Documents the
method (build -> boot headless -> screendump/sendkey) proven in the first
VM-boot pass, the per-component checklist, and the deliverable format. The
old "Pillar 8 runtime verification" Now-board punch-list folds into it.
AGENT.md's verify step now points at the headless VM screenshot flow.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Booting the VM peeled back two more layers of Hyprland config errors after
the geforce/moonlight fix:
* pip.conf used underscored rule names with stray values —
`keep_aspect_ratio on` and `border_size 0`. Hyprland's rules are
`keepaspectratio` (boolean, no value) and `bordersize 0`.
* retroarch/steam/system.conf wrote `idleinhibit, <mode>` with a comma,
so Hyprland read <mode> as a selector ("Invalid value: always/
fullscreen"). The mode is space-separated: `idleinhibit always`.
Verified clean in a rebuilt VM — the boot-time config-error overlay is
gone. All Omarchy-era rule-syntax holdovers, exposed once Component 5
wired apps.conf to source every app rule file.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Booting the VM showed "line 31: compgen: command not found" from
nomarchy-installed-summary on first boot. compgen is a bash
programmable-completion builtin, and the non-interactive bash the script
gets wrapped with (via makeWrapper + patchShebangs) is compiled without
progcomp. Beyond the visible error, the battery-presence check silently
failed to the else branch, so a laptop was always reported as "desktop".
Replace `compgen -G` with a nullglob array (a shopt, always available).
The installer's identical check runs under the ISO's interactive bash, so
it was unaffected — this script is the only compgen user.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Booting the desktop VM surfaced Hyprland config errors on every start:
"config option <windowrule:name> does not exist" from apps/geforce.conf
and apps/moonlight.conf. Both used an invalid `windowrule { name=…;
class:=…; idle_inhibit=… }` block form (an Omarchy-era holdover) that
Hyprland rejects line-by-line — exposed once Component 5 wired apps.conf
to source all 17 app rule files. Rewrite as single-line windowrulev2
rules matching every other apps/*.conf: idle-inhibit on fullscreen for
GeForceNOW; open-fullscreen + idle-inhibit for Moonlight.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ten state-mutating sites across seven scripts wrote through a predictable,
world-writable temp path (`/tmp/state.json`, `/tmp/system-state.json`)
before the atomic `sudo mv`. Because the `>` redirect runs as the invoking
user (sudo doesn't cover redirects), the shared fixed path is a symlink/
TOCTOU target and collides if two of these run concurrently. Switch each
to a per-invocation `mktemp`; the atomic rename into place is unchanged.
nomarchy-theme-set already used a temp var for its home-state write — this
makes its system-state write consistent and cleans the temp up on failure.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
These three home options (Hyprland inner/outer gaps + active-border width,
read from state.json, wired in features/desktop/hyprland/default.nix) were
the only real options missing from OPTIONS.md — a guardrail violation
(every nomarchy.* option must be documented). Found via a full diff of the
live option tree against the doc's headings.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CI's drift check failed on pure ordering noise: the generator's `sort`
collated rows and caller lists by the ambient locale (en_GB.UTF-8 on the
dev box that committed the file, C in the CI container), so `-` vs `/` and
`-` vs `s` ordered differently (e.g. theme-update vs themes-prebuild,
caller-list order). Force LC_ALL=C in the generator and regenerate
docs/SCRIPTS.md in that collation, so the committed copy and any
regeneration — dev box or CI — are byte-identical.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The per-toggle + per-palette matrix did ~39 full-system evaluations (16
toggle toplevels + 21 palettes each via a full extendModules eval + 2
home) and ran ~2h55m on the small self-hosted runner — long enough to hit
act_runner's 3h container lifetime (sleep 10800) and get killed.
Redesign:
* Combine compatible toggles into 3 configs (laptop-stack, desktop-stack,
impermanence-multi) instead of one eval per toggle. A failure means
re-run the suspect toggle alone; the trace hint covers that.
* Check palettes directly via the lib — the per-palette risk is the pure
base00 -> RGB hex math in plymouth.nix, which needs no module-system
instantiation. Forcing the palette attrset also catches a missing one.
* Drop the home configs; the per-output flake-check step already forces
both homeConfigurations.
~39 full evals -> 3, plus a near-instant palette pass. Runs in ~1m locally
(warm). AGENT.md updated for the new combined-scenario structure.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`nix flake check --no-build` also evaluates packages.allThemeVariants, a
linkFarm whose drvPath forces all 22 home generations into memory at once
— the runner OOM-killed there ("Killed" after allThemeVariants). Replace
it with an explicit per-output eval that forces all 4 system closures
(incl. assertions), both standalone home generations, the installer VM
package, the overlay and the app, and skips only allThemeVariants/default.
Each tryEval is GC'd before the next, so peak memory is one config rather
than twenty-two. The eval matrix already validates every palette
one-at-a-time, so dropping that package from CI loses no real coverage.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diagnosed the persistent "path '…-source' is not valid": it's walker's
flake reading its own source at eval time — importTOML
resources/config.toml in its home-manager module and lib.fileset.toSource
for the package src. On a cold store the walker input source isn't
materialized, and neither --no-build flake check nor `nix flake archive`
makes the fileset-derived src readable. Replace the archive step with a
`nix build` of walker's src attribute, which eagerly realizes it (and the
input it reads from) so the subsequent flake check / eval matrix find
every path valid.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
flake check kept failing on a cold store with "path '…-source' is not
valid" while evaluating Stylix's import-from-derivation in the
home-manager fontconfig text. Nix keeps flake-input sources in a git
cache and only materializes the -source store paths lazily; IFD readFile
needs them as real store paths. Works locally only because prior builds
already materialized them. Add a `nix flake archive` step that copies
every transitive input into the store before the check (and before the
eval matrix, which has the same need).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
flake check kept failing with "path '…-source' is not valid" while
evaluating the home-manager fontconfig text (Stylix/base16.nix do
import-from-derivation). Root cause: the installer pulled the latest Nix
(2.34), whose lazy-trees / git-cache behaviour doesn't materialise
flake-input source paths into the store, so the IFD reads can't find
them. Pin the install to 2.31.5 — the version that wrote flake.lock
locally and evaluates the flake cleanly all session.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
flake check evaluates the home-manager fontconfig text, which via
Stylix/base16.nix does import-from-derivation — eval must realize fetched
`-source` paths. The single-user Nix in the runner container can't set up
the build sandbox (no user namespaces), so realization failed with
"path '…-source' is not valid". Set sandbox = false for the runner
(safe in a throwaway CI container).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Empty group wasn't enough — this Nix version errors with "build users
group 'nixbld' has no members". Create the nixbld group plus 10 build
users (what a multi-user install does) before running the installer.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The single-user installer, running as root in the catthehacker container,
aborts because its bundled nix.conf sets build-users-group=nixbld and the
group doesn't exist ("the group 'nixbld' ... does not exist"). Pre-create
an empty nixbld group so config validation passes; single-user builds run
as root and never use it. Also set NIX_SSL_CERT_FILE at the job level
since we add nix to PATH without sourcing the installer profile that would
otherwise export it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
act_runner v0.2.11 bundles an `act` that supports up to node20, but
DeterminateSystems/nix-installer-action@main moved to the node24 runtime,
so the job died with "runs.using ... got node24". Replace the JS action
with a plain single-user Nix install (--no-daemon, no systemd needed in
the catthehacker container) and set NIX_CONFIG at the job level so the
bare `nix flake check` still gets flakes. A run step has no node-runtime
coupling, so this won't break again when an action bumps its runtime.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Gitea only scans .gitea/workflows and .github/workflows; it does not scan
.forgejo/workflows (that path is Forgejo-only). The CI was therefore
dormant on a Gitea instance regardless of whether Actions was enabled.
git mv to .gitea/workflows/ (scanned by both Gitea and Forgejo). Updated
the live references in STRUCTURE.md (Gitea + act_runner), AGENT.md, and
today's ROADMAP entry; left the dated historical Shipped entries as the
changelog they are.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
nix flake check --no-build only evaluates the four default configs, so a
bug that only fires when a nomarchy.* toggle is flipped sails through —
exactly how the vscode option rename and the impermanence systemd-stage-1
assertion both reached main undetected.
Add bin/utils/nomarchy-eval-matrix: layers each opt-in scenario (16
toggles, both system and home halves) onto nixosConfigurations.default
via extendModules and forces system.build.toplevel.drvPath (which forces
assertion checks); also forces all 21 palettes through the system-side
Plymouth base00->RGB math that the home-only allThemeVariants never
reaches, plus both standalone homeConfigurations. Per-scenario results
come from builtins.tryEval so one failure doesn't mask the rest; verified
it goes red on a re-injected assertion failure and a bad option ref, green
on the current tree.
Wired into .forgejo/workflows/check.yml after flake check (jq via nix
shell so it doesn't depend on the runner image). AGENT.md §5 now tells
future agents to add a scenario when they add/rename an opt-in option.
nomarchy-docs-scripts: filter systemd-service declarations (.services.
nomarchy-) and denylist the eval-matrix repo tool so neither shows as a
dangling "missing" reference; also retroactively clears the latent
nomarchy-flathub-init drift from the gaming-flathub commit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
themes/engine/plymouth.nix enables systemd stage-1 initrd
(boot.initrd.systemd.enable) distro-wide, and Plymouth is imported
unconditionally from core/system/default.nix. systemd stage 1 hard-rejects
boot.initrd.postDeviceCommands with a failed assertion, so every config
with nomarchy.system.impermanence.enable = true — including installs that
chose impermanence at the installer prompt — failed to evaluate.
Convert the Erase-Your-Darlings rollback to a boot.initrd.systemd.services
oneshot unit ordered after the LUKS mapping opens and before sysroot.mount,
and add boot.initrd.systemd.initrdBin for btrfs-progs/coreutils/util-linux/
findutils (the systemd initrd doesn't ship them by default). Script body is
unchanged. Verified eval via extendModules on nixosConfigurations.default;
boot-time wipe semantics still need a real wipe-boot cycle (Pillar 8).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
programs.vscode.extensions was renamed to
programs.vscode.profiles.default.extensions in home-manager. The bare
option still worked via the compat shim but emitted a deprecation
warning on every eval (21× — once per home evaluation in flake check)
and will break when the alias is dropped. userSettings already lived
under profiles.default; extensions now joins it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Before this fix, only the 6 palettes whose theme extensions ship in
pkgs.vscode-extensions had working VSCode theming. Every other palette
had `workbench.colorTheme` set to a name VSCode couldn't find, so it
silently fell back to its built-in default. Including the DEFAULT
summer-night palette (sainnhe.everforest) — the default install had
broken VSCode theming.
Probed the 13 unique extensions against the VSCode marketplace
extensionquery API:
- 10 exist and are pinnable: sainnhe.everforest,
shadesOfBuntu.flexoki-light, qufiwefefwoyn.kanagawa,
oldjobobo.{lumon,miasma,retro-82}-theme, TahaYVR.matteblack,
jovejonovski.ocean-green, monokai.theme-monokai-pro-vscode,
Bjarne.white-theme.
- 3 don't exist on the marketplace and are unpublished custom
Nomarchy themes: Bjarne.{ethereal,hackerman,vantablack}-nomarchy.
Logged as a new Later row.
For the 10, fetched version + sha256 via:
URL='https://${publisher}.gallery.vsassets.io/_apis/public/gallery/publisher/${publisher}/extension/${name}/${version}/assetbyname/Microsoft.VisualStudio.Services.VSIXPackage'
nix store prefetch-file --hash-type sha256 "$URL"
Added a marketplaceExtensions list to features/apps/vscode.nix that
wraps each in pkgs.vscode-utils.extensionFromVscodeMarketplace and
concatenates with the existing nixpkgs-packaged list — so 10 more
palettes (including the default) now get correct VSCode theming on
first launch. Smoke-built sainnhe.everforest end-to-end. Module
comment documents the version-bump procedure.
docs/OPTIONS.md updated: the nomarchy.vscode.devExtensions entry
drops the "still break" caveat for everything except the three
unpublished Bjarne palettes.
`nix flake check --no-build` clean.
features/apps/chromium/default.nix was deploying a 204-byte static
Default/Preferences via Home Manager symlink into Chromium's mutable
profile directory. The deployment is structurally broken — Chromium
expects to write that file at runtime, so either the symlink is
silently replaced on first save (losing the static defaults) or the
write fails silently.
The contents are also redundant + incorrect:
- `extensions.theme.{use_system,use_custom} = false` — already
superseded by the managed-policy approach (BrowserThemeColor
overrides any user-installed theme extension regardless).
- `browser.theme.{color_scheme,user_color} = 2` — hardcoded "dark"
via Chromium's color_scheme enum, conflicting with the dynamic
BrowserColorScheme = isLightTheme ? "light" : "dark" set by
core/system/browser.nix. A user on flexoki-light / summer-day /
catppuccin-latte / rose-pine / white would have had a static-vs-
policy mismatch every time.
Removed the entire features/apps/chromium/ directory (default.nix +
config/Default/Preferences) and dropped the import from
features/default.nix. Chromium theming continues to flow through the
system-level managed policy, which is the canonical chromium-on-NixOS
path.
`nix flake check --no-build` clean.
Moved the Next-column row → Shipped following commit 474bc16
("feat(plymouth): boot splash background follows the active palette").
Next column is now empty.
themes/engine/plymouth/nomarchy.script had
`Window.SetBackgroundTopColor(0.101, 0.105, 0.149)` hardcoded — a
Tokyo-Night-ish #1a1b26 — and nomarchy.plymouth had a matching
`ConsoleLogBackgroundColor=0x1a1b26`. Both were frozen regardless of
the active nomarchy.system.theme.
Replaced the literals with placeholders (@BG_R@, @BG_G@, @BG_B@,
@BG_HEX@) in the source files. themes/engine/plymouth.nix now reads
the active palette via `nomarchyLib.getPalette
config.nomarchy.system.theme`, converts palette.base00 into three
0.0–1.0 floats (Nix has no floating-point math, so `(byte * 1000) /
255` integer division formatted as `0.XXX`), and `sed`-substitutes
both files during installPhase.
Smoke-built the derivation against the default summer-night palette:
$ cat .../nomarchy.script | grep BackgroundTopColor
Window.SetBackgroundTopColor(0.176, 0.207, 0.231);
$ cat .../nomarchy.plymouth | grep ConsoleLogBackgroundColor
ConsoleLogBackgroundColor=0x2d353b
Both match the expected byte→float conversion from base00=2d353b.
Closes the "Plymouth theme variants per palette" Next-column item.
`nix flake check --no-build` clean.
The option surface (`enable`, `paths`) lived in core/home/overrides.nix
since 2026-05-18 but didn't do anything — paths was a reserved
attrset that never reached xdg.configFile.
Now wired: every entry in nomarchy.overrides.paths substitutes the
matching xdg.configFile.<key>.source at lib.mkForce priority, beating
Nomarchy's own lib.mkDefault writes. Other fields on the original
entry (recursive, etc.) survive via the standard module-system merge.
nomarchy.overrides.paths = {
"nomarchy/default/hypr/looknfeel.conf" = ./looknfeel.conf;
"waybar/style.css" = ./waybar.css;
};
Picked the attrset model from the row's two options rather than
runtime ~/.config/nomarchy/overrides/ directory discovery — Nix needs
every managed file declared at evaluation time, and the attrset
matches the explicit-config shape used everywhere else in the
Nomarchy surface.
docs/OPTIONS.md updated: both overrides.{enable,paths} entries get
real content + an example, the configOverrides row now contrasts
bulk-vs-per-file accurately, and the "Where these are defined" footer
drops the (reserved) tag.
`nix flake check --no-build` clean.
New nomarchy.accessibility.enable home option mirroring
nomarchy.system.accessibility.enable. When enabled, core/home/
accessibility.nix contributes a Hyprland extraConfig block via
lib.mkAfter:
- input.repeat_rate = 25 (from 40)
- input.repeat_delay = 1000 ms (from 600)
Holding a key isn't a runaway machine-gun for low-mobility users.
- bindd = SUPER ALT, S, Launch Orca, exec, orca
The system preset already puts orca on PATH.
mkAfter guarantees the slowdown wins over the templated input.conf.
Documented in docs/OPTIONS.md. The third item from the original Next
row — a high-contrast palette — is split into its own Later row
because it's a design task (24-colour WCAG AAA palette + icon family
choice) that wants its own review.
`nix flake check --no-build` clean.
services.flatpak.enable = true (set inside core/system/gaming.nix's
mkIf cfg.enable block) ships flatpak but doesn't add any remotes, so
`flatpak install` and the Discover GUI returned empty results until
the user ran the manual `flatpak remote-add` one-liner. nixpkgs has
no declarative remote-add API yet.
Added systemd.services.nomarchy-flathub-init: a Type=oneshot,
RemainAfterExit=true unit that runs
`flatpak remote-add --if-not-exists flathub
https://dl.flathub.org/repo/flathub.flatpakrepo`
after network-online.target. The --if-not-exists flag keeps it
idempotent across reboots and re-runs.
Lives under the gaming preset (where flatpak is wired today); lift
to a dedicated module when another preset needs flatpak.
Closes the "Gaming — declarative flathub remote" Next-column item.
`nix flake check --no-build` clean.
show_setup_config_menu was an Omarchy holdover where users edited
mutable config files at runtime. In Nomarchy:
- hyprland.conf, hypridle.conf, hyprsunset.conf, walker/config.toml,
waybar/config.jsonc are all Home-Manager-generated from declarative
settings — a `home-manager switch` clobbers any edit.
- hyprlock.conf and swayosd/config.toml point at paths the modules
don't deploy at all; open_in_editor created empty files.
- ~/.XCompose is a HM symlink into /nix/store — read-only.
Dropped the entire submenu function and the " Config" item from
show_setup_menu (and the matching *Config*) case branch). Persistent
settings go through the relevant nomarchy.* option in
/etc/nixos/home.nix (or system.nix); when the nomarchy.overrides.*
loader ships, a successor menu can route through
~/.config/nomarchy/overrides/.
Side effect (caught by the SCRIPTS.md regen): nomarchy-restart-xcompose
is now `unused?` — its only caller was the XCompose case I just
removed. Left for a future Pillar 3 cleanup rather than widening
this PR.
`bash -n` clean.
features/desktop/waybar/default.nix previously set
`programs.waybar.{enable,systemd.enable} = lib.mkDefault true`
unconditionally. The toggle script wrote .waybar to state.json and
pkill/exec'd waybar for instant feedback, but the next rebuild
re-enabled it because the Nix module didn't read the toggle. Result:
the bar came back on every rebuild/reboot regardless of the persisted
state — inconsistent with toggles.idle (gates services.hypridle.enable)
and now toggles.nightlight (gates services.hyprsunset.enable).
programs.waybar.{enable,systemd.enable} now follow
config.nomarchy.toggles.waybar. nomarchy-toggle-waybar flips the
running systemd user unit (`systemctl --user start/stop waybar.service`)
for instant feedback and writes .waybar back to state.json so the next
rebuild realigns. Disabled toggle now means no waybar across rebuilds
+ reboots, matching the option's documented meaning ("Whether the top
bar is enabled").
`nix flake check --no-build` + `bash -n` clean.
features/desktop/nightlight.nix previously set
`services.hyprsunset.enable = lib.mkDefault true` unconditionally and
baked the temperature (4000K when toggles.nightlight, 6500K otherwise)
into extraArgs at Nix-eval time. The toggle script bypassed systemd:
pkill on disable, `hyprctl dispatch exec hyprsunset --temperature 4000`
on enable — racing the systemd-managed instance and hardcoding 4000K
regardless of nomarchy.nightlightTemperature. The "Always enabled, we
control via IPC and state" comment was misleading: no IPC, the
temperature was rebuild-time, and the script forked a parallel
process.
Path (b) from the Later row:
- services.hyprsunset.enable now follows config.nomarchy.toggles.
nightlight — symmetric with services.hypridle.enable ← toggles.idle.
Disabled toggle = no process running.
- extraArgs always reads from config.nomarchy.nightlightTemperature.
Drops the 6500K neutralising fork; when off the unit just doesn't
start.
- nomarchy-toggle-nightlight flips the running systemd user unit via
`systemctl --user start/stop hyprsunset.service` for instant
feedback, reads nightlightTemperature from state.json for the
notify-send line, and writes .nightlight back to state.json so the
next rebuild realigns services.hyprsunset.enable.
`nix flake check --no-build` + `bash -n` clean.
core/home/config/nomarchy/default/hypr/input.conf hardcoded
`kb_layout = us`, so the installer's services.xserver.xkb.layout and
console.keyMap writes (both set from the installer's KEYMAP_LAYOUT
prompt) only reached XWayland apps and the TTY console. Native-Wayland
apps — i.e. almost everything in a Nomarchy desktop — fell back to US
regardless of what the user picked. Surprising for any non-US user.
Path (a) from the Later row:
- Added nomarchy.keymap.{layout,variant} to core/home/options.nix
(defaults "us" / "").
- Deleted the static input.conf from the bulk nomarchy/ deploy.
- Replaced it with an explicit
xdg.configFile."nomarchy/default/hypr/input.conf".text in
core/home/configs.nix that interpolates the option values into
kb_layout / kb_variant.
- Installer's home.nix heredoc now writes
nomarchy.keymap = { layout = "$KEYMAP_LAYOUT"; variant = "$KEYMAP_VARIANT"; };
alongside nomarchy.formFactor, so the layout reaches Hyprland
consistently with system.nix's xkb.layout / console.keyMap.
Documented in docs/OPTIONS.md (new `nomarchy.keymap.layout` /
`nomarchy.keymap.variant` entry). `nix flake check --no-build` clean.
flake.nix was re-importing ./themes/palettes and recomputing
themeNames via builtins.attrNames in its outputs `let`, even though
lib/default.nix already exports both. Two evaluations of the same
data, same result today — drift risk tomorrow as soon as one
computation grows extra logic the other doesn't.
Added `nomarchyLib = import ./lib { inherit lib; }` once and reused
it via `inherit (nomarchyLib) themeNames;` for the allThemeVariants
linkFarm. The local `palettes` binding wasn't read by anything else in
the outputs so it goes away with the rewrite. lib/default.nix is now
the single source of truth for the theme list; modules under core/
and themes/engine/ already import ../../lib the same way and resolve
to the identical evaluation.
`nix flake check --no-build` clean.
The session-manager wiring (uwsm + the Hyprland Wayland-compositor
entry that gives Hyprland a proper graphical-session.target so user
services like nomarchy-wallpaper, walker, and elephant chain off it)
had lived in core/system/virtualization.nix by historical accident —
loaded unconditionally on every install, nothing to do with libvirt
or docker.
Lifted into a dedicated core/system/session.nix and imported from
core/system/default.nix between systemd.nix and virtualization.nix.
virtualization.nix now contains only the libvirt + docker branches
its filename implies.
`nix flake check --no-build` clean. No behaviour change.
themes/templates/ shipped 11 mustache templates, but
nomarchy-theme-set-templates only writes to a path when no file is
already there — and a careful trace shows 9 of the 11 outputs are
either preempted by an earlier write or never read at all:
- kitty.conf.tpl, ghostty.conf.tpl
→ shadowed by the per-palette generators in
themes/engine/files.nix added in 8d3ce2d.
- hyprland.conf.tpl
→ shadowed by files.nix:100, which always writes
~/.config/nomarchy/current/theme/hyprland.conf from the
palette/feature/nord fallback chain.
- hyprlock.conf.tpl, alacritty.toml.tpl, btop.theme.tpl,
chromium.theme.tpl, swayosd.css.tpl
→ output paths nothing reads. alacritty + swayosd are themed
via Stylix / declarative HM options inline. btop reads from
~/.config/btop/themes/nomarchy.theme (loader.nix:72), not
from the theme symlink. chromium is themed via the managed-
policy module in core/system/browser.nix. hyprlock has no
consumer of theme/hyprlock.conf anywhere in the tree.
- hyprland-preview-share-picker.css.tpl
→ orphaned when the share-picker dir was deleted in 20de3d4.
obsidian.css.tpl and keyboard.rgb.tpl stay: the first is consumed by
nomarchy-theme-set-obsidian (copied into every Obsidian vault), the
second by nomarchy-theme-set-keyboard-asus-rog (sets the ROG
keyboard tint via asusctl).
Rewrote Step 6 of docs/creating-themes.md to describe the two live
templates by name and corrected a path bug ("~/.config/nomarchy/themed/"
→ "~/.config/nomarchy/themes/templates/" — the script actually reads
the latter).
`nix flake check --no-build` clean.
Per user decision: not supporting a non-LUKS install path. Removed
the Now-column entry and the Pillar 4 bullet that tracked it. FDE
stays the only supported install shape.
New nomarchy-installed-summary script renders a markdown table via
gum format with the install shape the user should verify before they
start customising:
- theme / font / panel position (~/.config/nomarchy/state.json)
- timezone / DNS / hybrid GPU (/etc/nixos/state.json)
- form factor (BAT* sysfs presence)
- software profiles (presence of marker packages)
- FDE (any crypt entry in lsblk)
- drives (lsblk filtered to disk/part/crypt)
nomarchy-welcome calls it as Step 0 — before the theme/font/panel
pickers — and gates progression on a gum input prompt so the user has
to acknowledge before customisation rewrites anything. The script is
also callable standalone from any terminal: `nomarchy-installed-summary`.
Self-contained — no installer-side changes. Software profiles are
detected heuristically (the installer bakes the user's pick into the
generated home.nix as concrete home.packages rather than persisting a
profile list), which is good enough for verification but won't catch
manually-removed profile packages. gum is in the existing categoryDeps
so no new tools are needed; falls back to plain markdown when gum
isn't on PATH (recovery contexts).
Closes the "Installer: What's installed? summary on first boot"
Now-column item from Pillar 4.
Component 9 (ISOs) closeout entry covers the two inline cleanups
(unreachable else branches in nomarchy-build-{iso,live-iso}) and the
regression-class verification: every tool install.sh calls is provided
by either the explicit host packages, the upstream profiles/base.nix
chain (gptfdisk, util-linux, kbd, systemd), or the nrun nix-run
fallback.
With Components 1–10 all shipped, the "Full QA audit" Now-column item
has done its job. Replaced it with a runtime-verification punch-list
entry that consolidates the "needs runtime verification" notes carried
forward by each component's closeout — boot the live ISO and walk
through waybar/menu/option-toggle paths on real hardware.
nomarchy-build-iso and nomarchy-build-live-iso both ran under set -e
but then wrapped nix build in an if [ \$? -eq 0 ] block with an else
that printed "Error: ISO build failed." and exit 1. set -e aborts the
script the instant nix build returns non-zero, so the else branch was
never reached — the user saw nix build's own error output and the
script exited.
Removed the dead conditional. Behaviour is identical.
Component 8 (Scripts runtime behavior) closeout entry summarises the
four inline fixes: omacom URL in nomarchy-menu "Learn → Nomarchy",
looknfeel.conf path drift, unreachable Overrides case branch, and
schema-vs-script default drift for the theme fallback in
nomarchy-theme-bg-next.
New Later row tracks an interaction-pattern bug in
show_setup_config_menu: five of nine entries edit files that Home
Manager generates from declarative options, so the next rebuild
clobbers the edit. Two more open paths the modules don't deploy.
Either rewire the menu or surface the ephemerality.
The script's jq fallback for .theme was hardcoded to "nord", which
drifted from the lib/state-schema.nix default of "summer-night". With
no state.json (or a blank one), nomarchy-theme-bg-next would look up
backgrounds under themes/palettes/nord/ while the rest of the system
treated summer-night as active — a quiet inconsistency that produced
"no backgrounds found" errors on a system that hadn't yet written a
theme to state.
Matched it to the schema default. lib/state-schema.nix remains the
single source of truth.
Three issues in features/scripts/utils/nomarchy-menu:
(1) "Learn → Nomarchy" called nomarchy-launch-webapp on
https://learn.omacom.io/2/the-nomarchy-manual — an upstream Omarchy
URL, the same one fixed in nomarchy-manual on 2026-05-18. Now calls
nomarchy-manual, which opens the local ~/.local/share/nomarchy/
README.md (or notifies if the source tree isn't synced).
(2) "Style → Hyprland" tried to open ~/.config/hypr/looknfeel.conf,
which is not the path looknfeel.conf is deployed to. The actual file
lives at ~/.config/nomarchy/default/hypr/looknfeel.conf (sourced by
nomarchy.conf via the chain). Updated the path.
(3) The setup-config case statement had an *Overrides* branch but no
matching menu option, so it was unreachable. The overrides loader
(nomarchy.overrides.*) is still in the Next column of the roadmap;
when it ships, both the option AND the case will get added together.
Component 7 (Theme engine + palettes) closeout entry summarises the
three inline fixes (theme-set early-exit on missing dir; bg-set
state.json persistence + path validation; palette dead-surface
cleanup) plus the script-comment correction.
Updated the existing `themes/templates/*.tpl` Later row: the original
claim "no script in the tree consumes them" was wrong —
nomarchy-theme-set-templates does. Replaced with a concrete
categorisation: (a) functionally dead (alacritty/btop/chromium/swayosd
write to paths nothing reads), (b) superseded by Nix-side generators
(kitty/ghostty after 8d3ce2d), (c) still relevant
(hyprland/hyprlock/obsidian/keyboard.rgb plus the now-orphan
share-picker tpl to verify).
The header said "Run once after install (the installer wires this
up)". The installer doesn't actually wire it up — install.sh:1848
prints a final-screen tip telling the user to run it, but doesn't
invoke it (pre-realising every theme adds significant install time
and the user can opt in later). Corrected the comment to match.
Two clusters of palette-tree dead surface:
(1) themes/palettes/{flexoki-light,lumon,retro-82,rose-pine}/apps/
chromium.theme — 9-byte comma-separated RGB strings that no script
or Nix module reads. Chromium theming is driven by managed policies
in core/system/browser.nix (BrowserThemeColor / BrowserColorScheme
derived from the active palette's base00 + light-mode flag), not by
per-palette files. The chromium.theme files were vestigial from an
earlier chromium-theming mechanism.
(2) themes/palettes/summer-day/apps/kitty/{kitty.conf,everforest-
light.conf} — a 76KB stray kitty config in a nested apps/kitty/
subdirectory. features/apps/kitty/config/kitty.conf includes
~/.config/nomarchy/current/theme/kitty.conf (root of theme dir, not
under apps/), and themes/engine/files.nix now generates that file
from the active palette's base16 colors (commit 8d3ce2d). So the
summer-day file was at the wrong path AND superseded by the
generator.
`nix flake check --no-build` passes.
Two related bugs in the theme switcher scripts:
(1) nomarchy-theme-set printed a warning when the theme directory didn't
exist but kept going — it wrote the bad name into state.json and ran
nomarchy-env-update on a broken state. Added an exit 1 after the
warning.
(2) nomarchy-theme-bg-set updated the live ~/.config/nomarchy/current/
background symlink + restarted swaybg but never wrote state.json. The
script is called by the walker background-selector menu
(elephant/nomarchy_background_selector.lua) and by the nomarchy-
wallpaper CLI wrapper, so every wallpaper picked via either path
silently reverted to the active theme's default on the next
home-manager switch — themes/engine/files.nix re-resolves
config.nomarchy.wallpaper at every rebuild. Now writes the chosen path
into state.json's wallpaper field, mirroring nomarchy-theme-bg-next.
Also added a file-exists check so a bogus path fails loudly instead of
leaving a dangling symlink + a failed swaybg process.
Component 6 (Apps) closeout entry covers the three inline theming
fixes (kitty/ghostty per-palette colors, btop color_theme name match,
VSCode theme extensions split). One new Later row tracks the 15
palettes — including the default summer-night (sainnhe.everforest) —
whose theme extensions aren't in nixpkgs and need
pkgs.vscode-utils.extensionFromVscodeMarketplace per-palette pinning.
Chromium's static Default/Preferences symlink was confirmed redundant
with the managed-policy intent in core/system/browser.nix, but left
under the existing Later entry (the user already hedged on deletion
without chromium-internals confirmation).
programs.vscode.profiles.default.userSettings.workbench.colorTheme is
set unconditionally to the active palette's theme name (read from
themes/palettes/<theme>/apps/vscode.json), but the matching theme
extensions were bundled with devExtensions — which defaults to false.
So out of the box, VSCode silently fell back to the built-in dark
theme on every palette.
Split themeExtensions out as always-installed and devExtensions as
opt-in via nomarchy.vscode.devExtensions. themeExtensions covers the 6
palettes whose VSCode theme is packaged in nixpkgs (catppuccin,
catppuccin-latte, nord, tokyo-night, rose-pine, gruvbox).
The other 15 palettes (including the default summer-night, which uses
sainnhe.everforest) still break because their theme extensions are on
the VSCode marketplace but not yet in nixpkgs — handling that needs
pkgs.vscode-utils.extensionFromVscodeMarketplace plus per-palette
publisher/name/version/sha256 metadata. Logged separately.
themes/engine/loader.nix:72 deploys the active palette's btop theme to
~/.config/btop/themes/nomarchy.theme, but btop.conf had
color_theme = "current" — btop looked for themes/current.theme, didn't
find it, and silently fell back to the built-in Default theme. So every
palette rendered btop in the same default colors regardless of the
selected Nomarchy theme.
Renamed the config reference to match the deployed file name.
lazygit and tmux both inherit terminal ANSI colors (verified: the tmux
status bar config uses blue/brightblack/etc., not hex), so the kitty +
ghostty + alacritty theming changes from 8d3ce2d cover them
transitively — no module fix needed.
features/apps/kitty/config/kitty.conf:1 contains
include ~/.config/nomarchy/current/theme/kitty.conf
and features/apps/ghostty/config/config:2 contains
config-file = ?"~/.config/nomarchy/current/theme/ghostty.conf"
Neither file existed for any of the 22 palettes. The kitty include
failed silently and the ghostty include is optional (?-prefix), so both
terminals rendered with built-in default colors regardless of the
active Nomarchy theme.
Stylix has kitty.enable = true in themes/engine/stylix.nix but the
kitty module uses xdg.configFile rather than programs.kitty, so the
Stylix target had nothing to hook into. ghostty has no Stylix target at
all.
Generated both files from the active palette's base16 colors in
themes/engine/files.nix, mirroring the waybar.css pattern already there.
Color mapping reproduces the original colors.toml fields (background,
foreground, cursor, selection_*, color0..15) via base16 indices —
verified against the inverse mapping in themes/palettes/default.nix.
themes/palettes/summer-day/apps/kitty/kitty.conf (a 76KB stray file in
the wrong tree location) is unaffected by this fix — it was already
dead surface since the include path never resolved to it.
Pillar 8 Component 5 (Desktop stack) closeout entry covers the five inline
fixes from this sweep (hyprland apps.conf wiring, NNOMARCHY typo pair,
broken waybar palette overrides, dead bindings files, mako post-fix verify)
plus the doc reconciles (SCRIPTS.md, KEYBINDINGS.md). One new Later row
captures the nightlight/hyprsunset toggle-vs-systemd inconsistency — Nix
always enables the systemd unit with a rebuild-time temperature while the
toggle script pkills/exec's hyprsunset directly and hardcodes 4000K
instead of reading nightlightTemperature.
Runtime verification (boot live ISO; eyeball waybar across panel
positions × form factors × all 22 palettes, walker launcher modes, the
screensaver at idle) remains on the user before declaring Component 5
fully closed.
nomarchy-menu:330 and nomarchy-launch-screensaver:16 referenced
\$NNOMARCHY_TOGGLE_SUSPEND and \$NNOMARCHY_TOGGLE_SCREENSAVER with a
double-N. The real env vars injected by features/scripts/default.nix:69-73
are single-N. Both reads always resolved to the empty string, so:
- nomarchy.toggles.suspend = false; still showed "Suspend" in the
system menu (the condition is "!= false", so empty != false → true).
- nomarchy.toggles.screensaver = false; still launched the screensaver
on hypridle's 150s timeout (the gate "== false" never hit on empty,
so the early-exit was skipped).
Both toggles documented in docs/OPTIONS.md were vacuous in practice.
themes/{catppuccin,lumon,nord,retro-82}/style.css fully replaced the
default style.css (no @import) but defined only 2–14 lines — just
@define-color declarations and, for nord, a minimal window#waybar block.
The default style ships ~110 lines covering #workspaces, #tray, #cpu,
#custom-nomarchy's Nomarchy-font override, margins/padding, the indicator
.active states, etc. So picking any of those four palettes produced a
waybar with zero structural styling.
The default style at features/desktop/waybar/config/style.css already
@imports ../nomarchy/current/theme/waybar.css — which themes/engine/
files.nix:30-34 generates with @background/@foreground/@accent from the
active palette. So removing the broken overrides restores per-palette
colors via the default-style path. summer-day and summer-night are kept
because their 100+-line style.css files are intentional, self-contained
visual redesigns (the case the themes/engine/loader.nix:76-79 comment
explicitly carves out for "themes that need significantly different
styling").
bindings.conf was explicitly labeled "Deprecated bindings file. New
installations include everything directly." and was sourced by nothing.
plain-bindings.conf referenced \$terminal/\$browser/\$fileManager/\$music/
\$messenger/\$passwordManager — Hyprland variables that aren't defined
anywhere in the loaded config tree — and was likewise sourced by nothing.
Both files have been superseded by features/desktop/hyprland/config/
bindings.conf (the user-overrideable, mkDefault-deployed one at
~/.config/hypr/bindings.conf) and the entries already inside
default/hypr/bindings/{utilities,media,clipboard,tiling-v2}.conf.
Regenerated docs/SCRIPTS.md; the diff drops the stale plain-bindings.conf
callers and incidentally corrects four scripts whose Origin column was
out of date after they moved from core/system/scripts/ to
features/scripts/utils/ (nomarchy-env-update, nomarchy-pkg-add,
nomarchy-pkg-remove, nomarchy-preflight-migration).
9 of the 17 .conf files under core/home/config/nomarchy/default/hypr/apps/
were deployed but never sourced. apps/system.conf carried the load-bearing
"tag +floating-window" rule that the bluetui/impala/btop/satty/screensaver
classes rely on, plus the "fullscreen, class:org.nomarchy.screensaver" rule
hypridle's 150s on-timeout depends on. apps/pip.conf carried the picture-
in-picture pin/float/size rules. Neither set of rules fired today — the
screensaver came up tiled, PiP windows didn't pin, and the floating-window
helpers shipped degraded.
Sourcing all 17 unconditionally; every rule is class- or title-gated so
the conditional ones (steam, qemu, 1password, etc.) no-op cleanly when the
app isn't installed — same pattern as the already-sourced telegram /
retroarch / localsend entries.
Two behavioral wrinkles found during the Pillar 8 desktop-stack sweep
that need a design decision before they can be fixed. Logged as Later
rows so the audit doesn't lose them.
1. The Hyprland Wayland keymap is hardcoded to `us` in
`core/home/config/nomarchy/default/hypr/input.conf:3`, ignoring the
installer-chosen layout for native Wayland apps. Fix needs either a
templated input.conf driven by a new home option, or session-level
`XKB_DEFAULT_LAYOUT` propagation. Either path touches the installer
heredoc and the home modules, so not a same-PR fix.
2. `nomarchy.toggles.waybar` is exported only as an env var consumed
by the runtime toggle script. The Nix module always sets
`programs.waybar.enable = lib.mkDefault true`, so the toggle is
session-only — waybar comes back on every rebuild/reboot.
Inconsistent with `toggles.idle` which correctly gates
`services.hypridle.enable`. Needs a behavioral call (persistent
gate vs intentional runtime-only with a clearer name).
Six unreferenced files surfaced under features/desktop/hyprland/config/
during the Pillar 8 sweep:
- `looknfeel.conf` and `autostart.conf` were deployed to ~/.config/hypr/
but never sourced by nomarchy.conf. The substantive versions live in
core/home/config/nomarchy/default/hypr/ and are sourced from there.
Removed the deployment lines in features/desktop/hyprland/default.nix
alongside the file deletes.
- `hyprlock.conf`, `hyprsunset.conf`, `xdph.conf` weren't deployed at
all and weren't referenced anywhere. Pure leftovers.
The entire `features/desktop/hyprland-preview-share-picker/` directory
was also orphan: no `default.nix`, no Nix module imports the
`config.yaml`. Only mention was inside the (now-deleted) `xdph.conf`.
Deleted the directory.
No behavioral change — these files weren't being used. Just removes
dead surface that confuses contributors looking for the "real" config
location.
`core/home/config/nomarchy/default/mako/core.ini` defines the Nomarchy
notification UX — urgency rules, app filters (Spotify silenced),
do-not-disturb mode, and button handlers for "Setup Wi-Fi" / "Update
System" / "Learn Keybindings" notifications. The file was deployed via
the bulk `nomarchy/` dir to
`~/.config/nomarchy/default/mako/core.ini`, but mako reads
`~/.config/mako/config` by default and `autostart.conf` launches it
without `--config`. So mako ran with stock defaults and the entire
themed UX was inert.
Added an explicit `xdg.configFile."mako/config".source` line in
core/home/configs.nix pointing at the existing themed file. mako now
picks up the Nomarchy rules out of the box.
Found during Pillar 8 audit of the desktop stack.
core/system/virtualization.nix wires `programs.uwsm` + the Hyprland
session config at the top of the file — loaded unconditionally on every
install, with no actual relationship to libvirt/docker. Cosmetic
mislocation, not a behavior bug; logged as a Later row so it can be
fixed in a dedicated session module without growing this audit PR.
Found during Pillar 8 audit of core/system modules.
These three settings.Login fields were set at default priority, so a
downstream system.nix that wrote (e.g.) `services.logind.settings.Login.HandlePowerKey = "poweroff"`
would collide with Nomarchy's value instead of overriding it. Same
mkDefault treatment as the other lid-switch settings in this block.
Found during Pillar 8 audit of core/system modules.
`lib/state-schema.nix` declared `system.features.makima = false` but
the field was never wired anywhere: no matching option in
core/system/options.nix, no consumer in core/system/state.nix, no
references in the wider tree. Schema-only ghost — removed.
Found during Pillar 8 audit of core/system modules.
The persistence block at core/system/impermanence.nix:75 read
`users.nomarchy = { directories = [...]; }` — the username was a
literal, not a reference. For any user not literally named "nomarchy"
the block was silently inert and ~/.ssh, ~/.gnupg, ~/.local/share/keyrings,
Documents, Downloads, Pictures, Videos, Projects were wiped on every boot.
Adds `nomarchy.system.impermanence.user` (str, default "nomarchy") and
uses it via `users.${cfg.user}`. The installer now writes the chosen
username alongside `enable` and `mainLuksName` so impermanence installs
with non-default usernames are correct out of the box.
docs/OPTIONS.md: fixes the wrong path on the impermanence row
(documented `impermanence.enable`, real option is
`nomarchy.system.impermanence.enable`) and adds entries for
`mainLuksName` and `user`.
Found during Pillar 8 audit of core/system modules.
Two unreferenced asset files removed; two larger concerns deferred to
roadmap rows because they need more thought than a focused audit
allows.
Deleted:
- `features/apps/alacritty/config/alacritty.toml` — the alacritty
module uses `programs.alacritty.settings` (Nix attrset) exclusively;
nothing references the on-disk file. The neighbouring (already-empty)
`themes/` directory goes with it.
- `themes/templates/mako.ini.tpl` — no script reads it.
Deferred to ROADMAP "Later":
- `features/apps/chromium/Default/Preferences` is deployed as a Home
Manager symlink into chromium's mutable profile directory. Either
silently replaced on first save or silently failing to write —
either way the static defaults don't survive. The actual chromium
theming work happens via managed policies in
core/system/browser.nix. Needs chromium-internals knowledge to
decide whether to remove or rework, so flagged rather than
unilaterally deleted.
- `themes/templates/*.tpl` (the remaining 10 templates) are also
apparently orphan — deployed via xdg.dataFile but unconsumed by any
script. Likely vestigial from a pre-stylix templating system.
Logged as a separate row to decide deletion vs documentation as
user-reference assets.
Found during Pillar 8 audit of features/apps.
Two clusters of documented-but-non-functional options surfaced during
the Pillar 8 audit, both setting toggles that have zero runtime effect.
1. `nomarchy.toggles.skipVsCodeTheme` was declared in
core/home/options.nix, defaulted from lib/state-schema.nix, and
surfaced as `NOMARCHY_TOGGLE_SKIP_VSCODE_THEME` env var in
features/scripts/default.nix — but `features/apps/vscode.nix` always
sets `workbench.colorTheme` unconditionally, and no script reads the
env var. Setting the toggle to true did nothing. Removed from
options, schema, state, env-var export, and OPTIONS.md.
2. `nomarchy.themeLoader.apps.{waybar,mako,kitty,alacritty}` were
declared in themes/engine/loader.nix but only `btop` is actually
wired (line 87 gates the per-theme btop.theme deploy). The other
four had no consumer. The actual theming pipeline for those apps is
elsewhere: waybar themes inline from `colorScheme` in waybar.nix;
kitty and alacritty are themed by stylix targets in
themes/engine/stylix.nix; mako has no theme integration at all.
Removed the four dead options + updated OPTIONS.md to list only
btop with a note about where the other apps' theming lives.
`features/default.nix` had a let-block that read
`~/.config/home-manager/user-packages.json` at eval time via
`builtins.pathExists` + `builtins.readFile`, parsed it as JSON, and
filtered to valid pkgs — then never appended the result to
`home.packages` or anywhere else. The `userPackages` variable was
completely orphan.
Two problems with the dead code: (1) it was an undocumented hidden
mechanism (no docs mentioned `user-packages.json`), (2) it made flake
evaluation impurely depend on a user's home directory for no payoff —
flake outputs would silently differ between machines depending on the
presence of that file, even though nothing in the build used it.
Removed the let-block entirely. The nomarchyLib import stays.
Found during Pillar 8 audit of features/apps.
`themes/engine/sddm.nix` defaulted `services.displayManager.autoLogin`
to `enable = true; user = "nomarchy";` (both mkDefault). The installer
flow overrode both with the real username at normal priority, so this
was invisible there — but a hand-migrated user (per docs/MIGRATION.md)
who imported `nomarchy.nixosModules.system` without setting
`autoLogin.user` would auto-login as a nonexistent "nomarchy" user and
SDDM would error. `docs/MIGRATION.md` even documented the override as a
post-import chore.
Flipped the default to `enable = lib.mkDefault false`. Installer
generates `enable = true` directly so its flow is unchanged. Migration
flow now gets the safe default — opt-in instead of opt-out — and the
docs row is updated to reflect the new shape.
The hardcoded "nomarchy" username fallback for `autoLogin.user` is the
same class of bug as the impermanence persistence block was. A future
roadmap row to consolidate "primary user" across impermanence,
autoLogin, and any future modules might be worthwhile, but it's
deferred — this commit is the immediate fix.
Found during Pillar 8 audit of first-boot UX.
`nomarchy-welcome` wrote a "starter" `~/.config/home-manager/home.nix`
for users without one. Two problems:
1. Wrong path. The installer-generated canonical home.nix lives at
`/etc/nixos/home.nix` and is imported via the flake (both
home-manager.users and the standalone homeConfigurations). Nothing
in the installer flow ever reads `~/.config/home-manager/home.nix`
— it's a dead file.
2. Broken content. The starter is missing `home.username`,
`home.homeDirectory`, `home.stateVersion`, and doesn't import
`nomarchy.nixosModules.home`. Even on a hand-migration path it
wouldn't evaluate as a standalone HM config.
So in the installer flow it's dead, and in the migration flow it's
broken. Removed Step 4 entirely. The git-init step (was Step 5) is
now Step 4. Hand-migrated users follow `docs/MIGRATION.md`, which has
the correct home.nix template.
Found during Pillar 8 audit of first-boot UX.
Two unused helpers and a missing comment in the lib/ surface, found
during the Pillar 8 sweep.
- `readState` in `lib/default.nix` was exported but has no external
callers — only `readHomeState` and `readSystemState` use it
internally. Removed from the export list; the function stays in the
let-block (still wraps the two public readers).
- `getWithDefault` in `lib/state-schema.nix` was a complete dead
function: declared as a path-walking fallback helper but never called
anywhere in the tree. core/{system,home}/state.nix use inline
`togglesState.<key> or schema.<scope>.<key>` instead. Removed.
- Added a header comment to `lib/state-schema.nix` explaining the
schema's boundary — it lists every state.json field consumed by a
Nix option, but state.json may also hold runtime-only fields
(`welcome_done` from `nomarchy-welcome`) that are intentionally
off-schema because no Nix option reads them. Future readers will
otherwise think welcome_done is an orphan.
Logged a Later-column roadmap row for consolidating `flake.nix`'s
palette/themeNames re-imports with `nomarchyLib` so the theme list has
one source of truth instead of two.
Two declared-but-non-functional option subsystems in core/home were
documented in OPTIONS.md and actively misleading users.
1. `nomarchy.behavior.hyprland.{bindings,input,windowRules,autostart}`
were declared in core/home/behavior.nix with a `behaviorConfigs`
mapping let-binding — both completely unread elsewhere in the tree.
The actual hypr/*.conf files are deployed by
features/desktop/hyprland/default.nix with `lib.mkDefault`,
unconditionally. Setting `behavior.hyprland.bindings = false` had
zero effect. OPTIONS.md's "Disable Nomarchy's default Hyprland
keybindings" example was a lie. Removed the four dead options,
deleted behavior.nix entirely, dropped the import from
core/home/default.nix, and rewrote the OPTIONS.md example to use
`xdg.configFile."hypr/bindings.conf".source = ./mine` (which
actually works against the existing `lib.mkDefault` priority).
2. `nomarchy.overrides.{enable,paths}` advertised a file-based override
loader that doesn't exist. The module created
`~/.config/nomarchy/overrides/{hypr,waybar,apps}` directories and
wrote a README claiming "place files here to override upstream
defaults" — but `getOverrideOrDefault` was never called and `paths`
was never populated. Rewrote core/home/overrides.nix to keep just
the option declarations (so configs that already set these still
evaluate) and marked them clearly as reserved/no-op in OPTIONS.md.
Removed the misleading README write and dir-creation. Logged a
Next-column roadmap row for implementing the loader properly.
While here:
- Clarified `nomarchy.configOverrides` (the *working* bulk-redirect
mechanism) vs `nomarchy.overrides.*` (the reserved one) in OPTIONS.md
— they're different things and the "See Overrides below" link was
pointing at the broken subsystem.
- Fixed OPTIONS.md `nomarchy.iconsTheme` / `nomarchy.isLightMode`
default text — both are derived from the active theme in
core/home/state.nix, not the static literals the docs claimed.
- Updated docs/AGENT.md §2 and docs/STRUCTURE.md to reflect the
behavior.nix removal and the overrides.nix reservation.
Found during Pillar 8 audit of core/home modules.
Laptop, Desktop, Accessibility, and Gaming presets all shipped on
2026-04-26 but were still tagged (Next). Reorders the pillar so the
two genuinely open items (dGPU auto-detect, Surface support) lead.
Pillar 3 audited script existence; Pillar 8 audits feature behavior.
Adds a per-component sweep methodology (10 components, one PR each on
wave/qa-<component>) and lists it on the Now board so the next session
can pick it up without re-deriving scope.
Cleanup pass on Pillar 4: removes two "(Now)" entries (software-profile
multi-select, form-factor → laptop preset) already in the Shipped log,
and promotes the two remaining open items ("What's installed?" summary,
non-LUKS branch) to the Now board.
- Update lib/state-schema.nix to default both home and system themes to 'summer-night'.
- Fix 'nomarchy-theme-list' and 'nomarchy-theme-set-templates' to resolve themes and templates from '~/.local/share/nomarchy' instead of the obsolete '$NOMARCHY_PATH' (fixing failures on Live ISO).
- Update 'nomarchy-welcome' to properly convert Title Case theme display names back to kebab-case identifiers and add input validation to prevent crashes.
- Fix installer impermanence symlink by using a relative path ('../persist/etc/nixos'), ensuring it resolves during 'nixos-install' both inside and outside the chroot.
- Deploy '~/.XCompose' symlink via Home Manager and add 'nomarchy-restart-xcompose' to the menu.
- Relocate 'Nomarchy.ttf' to 'core/branding/' and move user-level scripts ('pkg-add', 'pkg-remove', 'env-update', 'preflight-migration') to 'features/scripts/utils/' to align with the distro architecture.
- Remove obsolete '$NOMARCHY_PATH' exports and redundant 'bashrc' template.
- Export theme templates via 'xdg.dataFile' for script accessibility.
Audited every entry in `installer/hardware-db.sh` against
`inputs.nixos-hardware.nixosModules` and found **21 of 43 entries (49%)
referenced modules that don't exist** in the upstream attribute set —
those installs would fail at eval time with "attribute not found"
errors on real hardware. Specifically:
- Framework 13 per-gen: nixos-hardware uses `framework-11th-gen-intel`,
not `framework-13-11th-gen-intel`. Fixed all four generations.
- Framework 13 AMD AI 300: `framework-amd-ai-300-series` (no "13-").
- Framework Intel Core Ultra: added `framework-intel-core-ultra-series1`.
- Framework 16 AMD AI 300: added `framework-16-amd-ai-300-series`.
- Framework generic fallback now uses the `framework` umbrella module.
- ThinkPad X1 Carbon: modules are `lenovo-thinkpad-x1-Nth-gen`,
not `-x1-carbon-genN`. Fixed gens 6/7/9/10/11; added X1 Nano.
- ThinkPad P14s: requires arch+gen suffix; switched to the AMD gen3/4/5
modules (the prior `lenovo-thinkpad-p14s` had no attribute).
- Surface Pro 6/7/8/10: all share `microsoft-surface-pro-intel`. Pro 9
keeps its dedicated module. Pro 3 fixed to `-pro-3`. Surface Book
2/3 and Intel-based Surface Laptop 3/4/5: no nixos-hardware module
— rows dropped; generic chassis+cpu+gpu detection still emits
sensible `common-pc-laptop`.
- ASUS ROG Strix G513 → `asus-rog-strix-g513im` (correct attr name).
- ASUS ROG Zephyrus GA403 didn't exist — dropped. Added `ga402x`,
`gu603h`, `g533zw`.
- ASUS Zenbook generic `asus-zenbook-ux` was non-existent — dropped
(too vague; available modules are per-model like `asus-zenbook-ux481`).
- Dell Latitude 5400 / 7480: no modules — replaced with the existing
`dell-latitude-7420`, `7430`, `7490`.
Added:
- ROG Ally / Ally X support (`asus-ally-rc71l` for `RC71L`,
`RC72LA`, and the "ROG Ally" product string). nixos-hardware
currently ships one module for both revisions.
Documented (in a footer comment) the devices nixos-hardware doesn't
cover so they're known-unsupported rather than accidentally missing:
- Valve Steam Deck → Jovian-NixOS as a separate flake input.
- Snapdragon X laptops → aarch64 only; Nomarchy installer is x86_64.
- Raspberry Pi → same as above.
Bug discovered along the way: the DB's pipe-separated row format
collides with bash regex alternation. A row like
`Microsoft|Surface Pro (10|8|7|6)|_|module` parses as 7 fields, with
"7" extracted as the module name. Surface Pro variants are now one
row per version.
CI gate added (`.forgejo/workflows/check.yml`): a new step extracts
every 4th-pipe-field from `HARDWARE_DB` and `comm -23`s it against
`inputs.nixos-hardware.nixosModules`. Any future entry pointing at a
non-existent module fails CI with a clear error. Closes the regression
class entirely.
Verified locally: bash -n + shellcheck --severity=error pass on
hardware-db.sh; the CI step's exact commands pass against the new DB.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The script hardcoded `xdg-open https://learn.omacom.io/2/the-nomarchy-manual`
— an upstream Omarchy page. Users hitting "Help → Manual" in nomarchy-menu
were sent to an unrelated site, and there's no nomarchy.org canonical
docs URL to point at instead.
Now opens `$HOME/.local/share/nomarchy/README.md`, which lives on every
installed system (per SKILL.md's "Out of Scope" note about
`~/.local/share/nomarchy/`) and links every doc in `docs/`. Falls back
to a notify-send "run nomarchy-update?" message if the source tree
isn't synced.
Pillar 6 entry in docs/ROADMAP.md updated to (Shipped).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two stale-doc cleanups in one commit. Both surfaced during the
post-Phase-B audit pass.
1. `docs/STRUCTURE.md` "Root Directory" listed three files that don't
exist anywhere in the tree:
- `GEMINI.md` (replaced long ago by `docs/AGENT.md`)
- root-level `STRUCTURE.md` (this file actually lives in `docs/`)
- `TODO.md` (long since replaced by `docs/ROADMAP.md`)
Replaced with the actual root layout (flake.nix, flake.lock,
README.md, .forgejo/, .githooks/) plus a `docs/` sub-tree that
names every doc in the directory — the missing pieces the deleted
bullets were trying to point at, now correctly located.
2. `docs/ROADMAP.md` Pillar 6 had three "Next" bullets that already
shipped on 2026-04-26 (the welcome wizard, TROUBLESHOOTING.md, and
the docs-index goal — README.md now links every doc in `docs/`).
Moved all three to `(Shipped)`.
Also rewrote the `nomarchy-manual` bullet — "orphaned reference
today" was stale (the script is called from nomarchy-menu and
nomarchy-theme-install per docs/SCRIPTS.md). The real remaining
issue is its hardcoded `xdg-open https://learn.omacom.io/...` —
an Omarchy URL that opens an unrelated upstream page when a user
triggers the menu's Help entry. The bullet now names that
specifically.
No code touched; doc-only.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Closes the last source-of-truth split after the state-defaults
centralization batches. The installer's heredoc was the only remaining
place that hardcoded the state.json literal — adding a default to the
schema previously required a parallel edit here, and silent drift was
exactly the bug class we kept fixing.
Before:
cat > /mnt/etc/nixos/state.json <<JSON_EOF
{
"theme": "nord",
"timezone": "${_state_tz}",
"dns": "DHCP",
...
}
JSON_EOF
After:
nix eval --impure --raw --expr "
let
flake = builtins.getFlake \"$NOMARCHY_REPO\";
lib = flake.inputs.nixpkgs.lib;
schema = import \"$NOMARCHY_REPO/lib/state-schema.nix\"
{ inherit lib; };
state = schema.system // { timezone = \"$_state_tz\"; };
in builtins.toJSON state
" | nrun jq '.' > /mnt/etc/nixos/state.json
Uses the flake's own pinned `inputs.nixpkgs` (matching what the rest of
Nomarchy resolves against), so the schema evaluates with the same `lib`
the consumer modules see. `nrun jq` pretty-prints for human inspection.
Behavioural notes:
- Output is identical to the old heredoc modulo alphabetical key
ordering — `builtins.toJSON` sorts keys, the heredoc was in
declaration order. Toggle scripts read/write via `jq` so it's
invisible to them.
- Dry-run path unchanged. `execute_dry_run` already bind-mounts a fake
/mnt for the generator; the generator's absolute paths still resolve.
- New schema fields show up automatically on the next install; no
parallel edit needed.
- `bash -n` + `shellcheck --severity=error` clean.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two related fixes that together close the "minimal wiring" gap behind
`nomarchy.system.features.hybridGPU`.
1. Complete the NVIDIA driver stack inside hardware.nix's hybridGPU
mkIf block.
Before: `hybridGPU = true` enabled supergfxd and... that was it.
supergfxd manages mode switching by black/unblacklisting the nvidia
kernel module, but without the rest of the NVIDIA stack actually
loaded the dGPU has no driver to drive. Hyprland/Wayland silently
stayed on the iGPU regardless of mode.
After: hybridGPU=true also wires
services.xserver.videoDrivers = ["nvidia"] (loads the driver
under Wayland too)
hardware.graphics.{enable,enable32Bit}
hardware.nvidia.modesetting.enable (required for
Wayland)
hardware.nvidia.powerManagement.enable
hardware.nvidia.package = config.boot.kernelPackages
.nvidiaPackages.stable
boot.kernelParams += "nvidia-drm.modeset=1"
All wired with lib.mkDefault so a downstream system.nix can pin a
beta driver, flip to the open kernel module, or set
`hardware.nvidia.prime.{offload.enable, intelBusId, nvidiaBusId}`
for render-offload. The bus IDs are per-machine (find via
`lspci -D`) so they stay user-supplied; docs/OPTIONS.md has the
full recipe.
2. Add lib.mkDefault to every state.json-derived assignment in
core/system/state.nix and core/home/state.nix.
Same priority bug on both sides: assignments like
`features.hybridGPU = systemState.features.hybridGPU or false`
landed at default priority. A downstream system.nix saying
`nomarchy.system.features.hybridGPU = true` would then conflict
with the state-derived value at the same priority, and Nix would
refuse the merge with "conflicting definition values" — the
user's override couldn't take effect.
Verified by an explicit eval: extending the default nixosConfig
with `nomarchy.system.features.hybridGPU = true` now resolves
cleanly and the full driver stack engages.
Side-effect: core/system/state.nix now reads from
lib/state-schema.nix like the home side does, completing the
schema-centralization started two batches ago.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Four resume-flow papercuts in `installer/install.sh` that hurt the
"interrupted install" path the most.
1. `--resume` with no state file is no longer silent.
The most common operator confusion: reboot the live ISO, forget
/tmp/ is tmpfs, re-run with --resume, watch the installer start
over from scratch without saying anything. Now: loud error, tmpfs
explanation, exit 1.
2. Validate the saved TARGET_DRIVE still exists on resume.
Live ISO USB sticks get unplugged between sessions, dev hosts
sometimes have non-deterministic /dev/sdX numbering. Without the
guard the install proceeds and fails with cryptic disko / mount
errors deep in execute_installation. Now we fail at load_state
with the actual reason and a clean recovery path.
3. Resume now shows what's being resumed.
`save_state` stamps an ISO-8601 timestamp; `load_state` prints
"Resumed from <path> (saved Xm ago)" plus a "Target: /dev/X → user
@ host" summary line. Lets the user Ctrl-C before any destructive
prompt fires if they're resuming onto the wrong machine.
4. `--help` documents the tmpfs limitation.
Saved state lives in /tmp/ which is tmpfs on the live ISO; --resume
only works within the same boot. The man-page now says so instead
of letting users discover it the hard way.
`format_age` is the one new helper — pretty-prints "Xs/Xm/Xh Ym/Xd"
relative to now, falls back to the raw timestamp if `date -d` can't
parse the input. shellcheck --severity=error passes.
Out of scope (potential future work):
- Persistent state across reboots (would need a writable USB / external
drive — chicken/egg with the installer setting up the only persistent
storage in the first place).
- `--show-state` flag to inspect a saved file without running.
- State-file schema versioning.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Kills a recurring bug class: state defaults previously lived in three
parallel places that drifted apart over time.
- lib/state-schema.nix (the canonical schema, referenced
nowhere except a description string)
- core/system/options.nix (default = "..." clauses on options)
- core/home/options.nix (same, on home options)
- core/home/state.nix (`or "..."` fallbacks for state.json reads)
When `state.json` is missing a key, three files have to agree on the
fallback. They keep silently drifting:
- The OOTB QA audit shipped fixes for this pattern.
- Earlier this session, `chore: switch default theme summer-night → nord`
fixed core/system/options.nix and core/home/state.nix — but missed
core/home/options.nix, which still defaulted nomarchy.theme to
"summer-night". Every consumer of the home option
(features/default.nix, vscode.nix, waybar, hyprland, theme engine)
resolved to the wrong theme when state.json was blank.
This change:
- Imports lib/state-schema.nix into all three consumers and replaces
every hardcoded default with `schema.<scope>.<key>`.
- Fixes the lingering nomarchy.theme = "summer-night" home-side bug as
a side-effect.
- Touches roughly 25 literals across the three files.
Verified `nix flake check --no-build` passes and every centralized value
evaluates to the exact literal it previously had. Off-schema option-only
defaults (isLightMode, formFactor, cursor.*, iconsTheme, keyring.enable,
etc.) are left hardcoded — they have no state.json counterpart, so
there's no source-of-truth split to resolve.
Out of scope (follow-up):
- Have installer/install.sh generate /mnt/etc/nixos/state.json from
the schema instead of hardcoded JSON — would close the last
split-brain surface (the installer can still drift from schema).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Pillar 7 first step. `.forgejo/workflows/check.yml` runs on every push
to main and every PR. Three sequential checks in one job:
1. `nix flake check --no-build`
Catches eval regressions: broken option references, missing imports,
stale module argument shapes. The same command AGENT.md tells humans
to run by hand before declaring a change done.
2. `bash -n` + `shellcheck --severity=error` over every `nomarchy-*`
bash script.
Mirrors what `.githooks/pre-commit` does locally, but across the
whole tree on every push — so a branch that bypasses the hook (via
`--no-verify` or a fresh clone without `core.hooksPath` set) still
gets gated. Severity is capped at error to match the hook; the long
tail of style/info warnings can be cleaned up incrementally.
3. `docs/SCRIPTS.md` drift check.
Regenerates the audit doc to a temp file and `diff`s against the
committed version. Fails loudly with the fix command if a script
add/remove/rename didn't include the regeneration step.
Dry-run results on the current tree:
- `nix flake check --no-build`: pass (only pre-existing warnings).
- shellcheck across 159 scripts at severity=error: pass.
- SCRIPTS.md drift: clean.
Activation:
Forgejo Actions isn't enabled on the repo yet, so the workflow lands
dormant. To activate: enable Actions on the repo in Forgejo's settings
and register a `forgejo-runner` on any Docker-capable Linux host. The
workflow uses `ubuntu-latest` and installs Nix itself via
`DeterminateSystems/nix-installer-action`, so no special runner image
is needed.
Deferred to a follow-up batch (needs binary cache infra):
- Building ISOs in CI (`nomarchy-installer`, `nomarchy-live`, default).
- Release pipeline (`vYY.MM.x` tags publishing ISOs as artifacts).
- `nixosTest` per palette with golden-image screenshot diffs.
`docs/STRUCTURE.md` now documents `.forgejo/` and `.githooks/` so future
agents and contributors can find both.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Pillar 3 Phase B, batch 4 (final). Triages the last 13 `unused?` rows:
five deletes and eight SKILL.md surfacings.
Deleted (no callers anywhere, work duplicated inline or marginal value):
- `nomarchy-restart-hyprctl` and `nomarchy-restart-mako`: stale comments
claimed "used by theme switching" but no Nomarchy script calls them.
Theme-set and refresh paths call `hyprctl reload` / `makoctl reload`
directly (see nomarchy-refresh-hyprland).
- `nomarchy-restart-tmux`: 3-line pgrep+source-file wrapper. Users can
`tmux source-file ~/.config/tmux/tmux.conf` themselves.
- `nomarchy-battery-present`: the battery monitor reads
`/sys/class/power_supply/BAT*` inline; the helper never got wired in.
- `nomarchy-sudo-keepalive`: intended to be `source`d from longer-running
scripts (nomarchy-update, etc.) but nothing sources it. Resurrect from
git history if a future caller actually needs it.
Surfaced in SKILL.md (now tagged `kept` by the audit):
- Themes: `nomarchy-theme-{remove,refresh,bg-install}`
- System: `nomarchy-sudo-{passwordless-toggle,reset}`,
`nomarchy-restart-trackpad` (intel_quicki2c THC reload — a real laptop
bug fix worth documenting)
- New Virtualization section: `nomarchy-windows-vm {install,launch,stop,status}`
- Enriched Troubleshooting's generic `nomarchy-refresh-<app>` example with
literal `nomarchy-refresh-fastfetch` so the audit catches it.
Verified `nix flake check --no-build` still passes and zero callers
reference the deleted scripts.
**Phase B is now complete.** Final audit state: 164 → 159 scripts, all
tagged `kept`, `unused?` = 0, missing references = 0. The audit table is
now a clean reference of what Nomarchy ships, not a triage backlog.
Logged in `docs/ROADMAP.md` Shipped.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Pillar 3 Phase B, batch 3 — Batch A of the unused? clusters.
Deleted two dead webapp URI handlers:
- `nomarchy-webapp-handler-hey`
- `nomarchy-webapp-handler-zoom`
Neither was registered as a MimeType handler anywhere — a grep across
`*.desktop` files in `core/`, `features/`, `themes/`, `installer/`, and
`hosts/` returned zero matches. Without a `.desktop` registration the
system never routes `mailto:`/`zoom:`/`zoomus:` URIs to them, so the
handlers were unreachable code.
Kept the six remaining install/remove pairs (they're real CLI tools, just
unwired into any menu) and surfaced them in `SKILL.md` "Common Tasks" so
AI assistants can discover them on user request and the audit tags them
`kept`:
- Custom App Launchers: webapp-{install,remove,remove-all},
tui-{install,remove,remove-all}
- Voice dictation: voxtype-{install,remove,status}
Menu-wiring these (e.g. a "Setup → Apps" submenu in nomarchy-menu) is a
separate Pillar 6 onboarding job, not scoped here.
Regenerated `docs/SCRIPTS.md` — script count 166 → 164, `unused?` 21 → 13.
Logged in `docs/ROADMAP.md` Shipped.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Pillar 3 Phase B, batch 2. Five `unused?` scripts that either duplicate
NixOS-native facilities or reference infrastructure Nomarchy doesn't
ship. All five had no callers anywhere in the tree.
- `nomarchy-rollback`: ran `nixos-rebuild rollback` after listing
`snapper` snapshots. NixOS already exposes the previous generation in
the boot menu and `nixos-rebuild --rollback`; Nomarchy uses
impermanence, not snapper.
- `nomarchy-snapshot`: wrapped `snapper create/restore`. Same reason —
snapper isn't part of Nomarchy. The script's "nomarchy-update can use
this" comment never came true; nomarchy-update has no reference to it.
- `nomarchy-migrate-state`: one-time migration from old
`~/.config/home-manager/state.json` and `/etc/nixos/state.json` to the
unified `~/.config/nomarchy/state.json`. The installer now seeds the
unified file directly; no current install needs the migration.
- `nomarchy-config-direct-boot`: added an EFI boot entry for a Nomarchy
UKI. We don't build a UKI (no references anywhere in `core/` or
`hosts/`), so the script targeted nonexistent infrastructure.
- `nomarchy-npx-install`: generated npx wrappers in `~/.local/bin/`. An
Arch idiom — on NixOS the path is `nix-shell -p nodejs` or a
declarative `home.packages` entry.
Kept `nomarchy-build-iso` and `nomarchy-build-live-iso` (the user-flagged
useful build wrappers) and surfaced them in README §2 in place of the
raw `nix build` command, which both removes the audit's `unused?` flag
on them and shortens the docs.
Regenerated docs/SCRIPTS.md (171 → 166 scripts; 28 `unused?` → 21).
Logged in docs/ROADMAP.md Shipped.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Both the system option (`core/system/options.nix:theme`) and the home-side
state evaluator (`core/home/state.nix`) defaulted to "summer-night". The
installer-written state.json now seeds "nord" (see preceding installer
commit), and `lib/state-schema.nix` already defaults to "nord". Align the
hardcoded fallbacks here so a missing or blank state file lands on the
same theme everywhere instead of a now-inconsistent split.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The installer generates `nixosConfigurations.<hostname>` (see
installer/install.sh: `nixosConfigurations.$HOSTNAME`), but the system
update script was rebuilding `.#default` and using `--impure`. The
`#default` literal worked only on dev hosts that happened to be named
"default" and silently broke every toggle script on real installs.
Now resolves `$(hostname)` at runtime and aborts with a clear error if
empty. Dropped `--impure` — the flake doesn't need it.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Several installer reliability fixes that were left uncommitted:
- Impermanence + multi-disk LUKS: disko-config.nix names the main LUKS
mapping `crypted` for single-disk and `crypted_main` once extraDrives is
non-empty. The impermanence rollback hook used to hardcode `crypted`,
which made every multi-disk install fail to mount root in initrd. Added
a `nomarchy.system.impermanence.mainLuksName` option and wired the
installer to write the correct value into the generated system.nix
based on the drive count.
- Password no longer cleartext in /etc/nixos: installer now hashes the
user password with `mkpasswd -m sha-512` and emits
`initialHashedPassword` instead of `initialPassword`. Added mkpasswd to
the live ISO. Cleartext is unset immediately after hashing.
USER_PASSWORD_HASH is deliberately not persisted in --resume state —
configure_user re-prompts on resume.
- Revision pinning that actually works on the live ISO: `inputs.self`
strips .git in the Nix store copy, so `git rev-parse HEAD` would silently
return empty on a real install and the generated flake would track main.
Live ISO now writes `/etc/nomarchy-rev` from `inputs.self.rev` at build
time; install.sh reads it first, falls back to git, and aborts with a
loud confirmation prompt if both are empty (instead of silently
installing an unpinned system).
- Generated `/mnt/etc/nixos/state.json`: toggle scripts (nomarchy-tz-select,
nomarchy-setup-{fido2,fingerprint}, nomarchy-toggle-hybrid-gpu,
nomarchy-wifi-powersave) `jq` this file in place and fail hard if it
doesn't exist. Fresh installs now ship a schema-conformant file matching
lib/state-schema.nix.
- Unmount /mnt before exiting `finish()` regardless of reboot choice. Clean
unmount avoids dirty BTRFS on reboot; on "no", leaving /mnt mounted
blocked a second installer run on the same live ISO.
- Removed obsolete `installer/disko-btrfs-luks.nix` (superseded by
`disko-config.nix` per commit 3aadc36) and dropped its dangling
`docs/STRUCTURE.md` reference.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The audit's "missing references" table held 15 rows — 2 real doc bugs and
13 grep false-positives — making Pillar 3 Phase B triage noisier than it
needed to be.
- Wrote themes/engine/scripts/nomarchy-theme-next so SKILL.md's documented
"cycle to next theme" command actually resolves.
- Scrubbed three stale `nomarchy-dev-*` references from SKILL.md (skill
frontmatter, body, and Out-of-Scope list) — they hallucinated a workflow
that doesn't exist and broke AI-assisted use of the skill.
- Added a line-context filter to both nomarchy-docs-scripts generators
that drops `nomarchy-*` tokens appearing in Nix pname/derivation idents,
/tmp/ and /etc/sudoers.d/ paths, nixosConfigurations.* / packages.*
flake outputs, mktemp -t prefixes, systemd unit vars, ./result/bin/run-
binaries, and docker container references.
- Added a small token-level denylist for five residual non-script
identifiers (nomarchy-plymouth, nomarchy-sddm-theme, nomarchy-live,
nomarchy-rev, nomarchy-windows) that survive line filtering because
they appear as bare Nix list refs, comment backticks, or compose-heredoc
identifiers.
Regenerated docs/SCRIPTS.md; the "Missing references" section is now
empty. Logged in docs/ROADMAP.md Shipped.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Scrubbed remaining upstream references and solidified Nomarchy's identity:
- Replaced 'Omarchy' and 'Spirit of Omarchy' with brand-independent terms in README.md and scripts.
- Updated nomarchy-welcome banner to 'The Professional NixOS Desktop'.
- Set nomarchy-version codename to 'Sovereign'.
- Verified core/system/branding.nix for OS-release and bootloader labels.
- Verified SDDM and Plymouth metadata for correct branding.
- Updated ROADMAP.md board.
Fixes identified during the thorough distro review:
- Restore automatic wallpaper switching by removing image filters from deployed themes.
- Fix broken 'Style' menu entries by creating missing about.txt and screensaver.txt branding files.
- Clean up conflicting keybindings by removing deprecated tiling.conf and updating doc generator.
- Remove legacy Nord theme hack from nomarchy-theme-set.
- Fix JSON parse error in summer-day waybar theme.
- Move temporary LUKS keyfile to /tmp/ so Disko omits it from runtime config
- Explicitly add x-systemd.requires and x-systemd.device-timeout=0 to BTRFS mount options
- Ensures all LUKS devices are decrypted before BTRFS attempts to mount
- installer: set recursive ownership of /etc/nixos to main user post-install
- themes: fix NOMARCHY_PATH and discovery logic for Lua theme menu
- scripts: update CLI wrappers (font, theme, wallpaper) to use Walker menus
- core: remove obsolete NOMARCHY_PATH and cleanup dead code
- features: add pkgs.lua for Walker and remove obsolete switcher.nix
- docs: update ROADMAP.md, SCRIPTS.md and STRUCTURE.md
The post-install standalone HM activation kept failing in new ways
(daemon access, git ownership, missing PATH on first boot). Wire HM as
a NixOS module in the generated flake instead, so first-boot dotfiles
are activated by `nixos-install` itself with proper system context. The
standalone `homeConfigurations.<user>` is kept alongside for fast
iteration via `nomarchy-env-update`. Also:
- Drop the chroot HM activation block from the installer entirely.
- Move `nomarchy-env-update` from `features/scripts/utils/` to
`core/system/scripts/` so it ships in `nomarchy-system-scripts` and
exists on a freshly-installed system regardless of HM state.
- Set system-wide git `safe.directory` for /etc/nixos and the
impermanence-relocated /persist/etc/nixos so the user-mode HM run
doesn't trip on the root-owned flake repo.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
HM activation inside `nixos-enter` failed with `big.lock: Permission
denied` because the chroot has no systemd and therefore no nix-daemon —
the user-level `nix run` fell back to single-user mode and couldn't
write /nix/var/nix/db. Launch nix-daemon manually for the activation
window and force NIX_REMOTE=daemon. Also mark /etc/nixos (and the
impermanence path) as a git safe.directory so HM doesn't trip over
git's dubious-ownership check on the root-owned repo. Make
nomarchy-env-update self-bootstrap via `nix run home-manager` when
home-manager isn't on PATH so the recovery hint actually works on a
freshly-installed system.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Pass --yes-wipe-all-disks to disko so the silent gum-spin path no
longer hangs forever waiting on a hidden "yes" confirmation prompt
(added in disko 1.13's destroy,format,mount mode).
- Stop threading an externally-built pkgs into the user flake's
nixosSystem; configure nixpkgs through the module system instead so
core/system/default.nix's nixpkgs.config.allowUnfree stops conflicting
with the assertion "system configures nixpkgs with an externally
created instance".
- Enable boot.loader.systemd-boot in the generated system.nix so the
installed system has an actual bootloader (disko already lays out a
1 GiB ESP at /boot).
- Bump nix.settings.download-buffer-size to 512 MiB to silence the
"download buffer is full" warning on large NAR fetches.
- Activate home-manager via `runuser -l` instead of `runuser -u … --
env HOME=…`. The latter only switches uid and leaves \$USER=root, so
HM's activation script saw root, warned, and wrote dotfiles into
/root/ — meaning the user's first login had no Hyprland config.
- Revert default Hyprland monitor line back to highres (live ISO and
user default) — preferred falls back to EDID's 1024x768 in QEMU and
on several laptop panels, which is the bug highres was put there to
defeat.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- nrun git git init -q passed 'git' as a subcommand to git itself,
failing the post-disko repo init. Drop the duplicated arg.
- disko's luks module reads passwordFile at the top level; placing it
under settings.* meant it was silently ignored and disko fell back
to askPassword=true, prompting the user again on luksOpen. Move the
option to the right scope.
- configure_impermanence now uses local rc, nrun gum confirm, and an
explicit case (0/1/130) with a final return 0 so a No answer no
longer aborts the installer.
- run_disko_with_retry hides disko's chatty output behind a gum spin
by default and surfaces the captured log on failure. Set
NOMARCHY_VERBOSE_DISKO=1 to stream output live.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
colors.toml and icons.theme were copy-pasted from summer-night (a dark
theme). Repalette to Everforest Light, mark as light via light.mode,
switch icons to Yaru-blue, fix the broken Waybar import, and add the
btop/neovim/vscode/rofi/walker app configs the sibling themes ship.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Fix critical bash dynamic scoping bug in install.sh (Impermanence/Form Factor).
- Polished Live ISO with auto-login and passwordless sudo.
- Repurposed nomarchy-toggle-suspend to directly execute systemctl suspend.
- Updated nomarchy-launch-wifi to use nmtui in alacritty.
- Optimized nomarchy-welcome to avoid redundant rebuilds via --no-update flag.
- Enabled nomarchy-welcome in Hyprland autostart.
- Wrapped Live ISO-modifying steps in welcome wizard to prevent failures.
- Removed obsolete hardware auto-detection from nomarchy-on-boot.
- Hardened script doc generator against false-positive wildcard tokens.
- Regenerated docs/SCRIPTS.md and updated docs/ROADMAP.md.
Updated the default Hyprland monitor configuration to 'monitor=,preferred,auto,1'. This ensures the best resolution is selected automatically while maintaining a 1x scale by default.
Set boot.kernelPackages to pkgs.linuxPackages_latest by default. This ensures Nomarchy users benefit from the latest hardware support and security features in the mainline kernel.
- Refactor generated flake.nix to use the Appliance Model.
- Downstream flake now only defines the 'nomarchy' input.
- Dependencies (nixpkgs, home-manager) are inherited from nomarchy.inputs to ensure maximum stability and version alignment with upstream.
- Fix disko-config.nix signature by adding '...' to handle unexpected CLI arguments.
- Update disko mode to 'destroy,format,mount' for the modern API and to avoid deprecation warnings.
- Fix infinite loops in 'configure_impermanence' and 'confirm_form_factor' caused by misinterpreting 'No' (rc=1) as an abort.
The nomarchy-* prefix is a name convention, not a language guarantee:
nomarchy-haptic-touchpad is Python. Without a shebang filter, the
pre-commit hook would run `bash -n` on it and abort every commit
that touched the Python helper. Filter to scripts whose shebang
matches `bash` before linting; everything else passes through.
Found via the set -e sweep (1e94818) — the survey caught
nomarchy-haptic-touchpad as a "broken" bash script when it was
just non-bash.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sweep across the three script directories: features/scripts/utils,
core/system/scripts, themes/engine/scripts. 142 of 169 bash scripts
gained `set -e`; 27 already had it; the one Python helper
(nomarchy-haptic-touchpad) was skipped via shebang detection.
Why: bash's default behavior is to continue past a failed command,
which means a script that does "do A; do B; do C" leaves the system
in a half-applied state when B fails - and the user gets no signal.
Several recent fix commits (theme partial-apply, waybar reload race,
installer prewipe silent failures) all trace back to this. set -e
turns silent corruption into a loud abort the user can act on.
The 11 scripts with explicit `|| true` markers stay safe under set -e
because || true coerces the exit to zero; the markers continue to
mean "I deliberately tolerate this failure here."
Deliberate exception: nomarchy-menu runs WITHOUT set -e. It is an
interactive UX loop where action branches do `cmd; back_to <self>`
so a failed action would abort the script under set -e and the menu
would disappear without feedback. Soft-failure - menu re-displays,
user picks again - is the right semantic. Documented inline.
Validation: bash -n on every modified script (zero failures). The
new pre-commit hook (27f5663) was just updated to filter by shebang
so it doesn't try to bash-syntax-check the Python helper - that
filter was uncovered by this sweep.
Risk: set -e can surface latent bugs in scripts that previously
relied on silent continuation. If anything breaks, it's a real bug
that was already broken and is now visible. Easy per-script revert
if any UX glitches show up.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds two-tier linting before the existing docs/SCRIPTS.md regenerate
step:
- bash -n on every changed nomarchy-* script. Catches syntax errors
that would otherwise be discovered at runtime by an unlucky user.
Always fatal.
- shellcheck --severity=error when shellcheck is on PATH. Catches
unquoted-var, use-before-define, missing-shebang, and other
bug-shaped patterns. Only error-level issues block - the long
tail of pre-existing warnings stays as a known cleanup task,
not a commit blocker. Hook silently skips this step when
shellcheck isn't installed (so contributors without it can still
commit).
Catches the class of bug that's bit us repeatedly: a script ships,
the runtime path that exercises the broken line is rare, and the bug
sits latent until a user trips it. Cheaper to catch at commit time.
Caveat: 156 nomarchy-* scripts already have shellcheck warnings
(severity warning/info/style); we deliberately ship around them via
the --severity=error gate. A future per-script audit can dial the
severity up as scripts get cleaned up.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Previous behavior: `pkill -x $1` (no wait) followed by an immediate
background `setsid uwsm-app`. The new instance attached its wayland
surface while the old one's surface was still mapped. Layer-shell
apps got the same visible ghosting that waybar showed on theme switch
before the SIGUSR2 fix (386da51), and non-layer apps got brief double
instances.
Fix:
- Quote $1 (was unquoted, breaks if app name has whitespace - rare
but cost-free to fix while we're here).
- After SIGTERM, poll pgrep for up to ~1.5s in 100ms ticks.
- If anything is still alive after the poll window, SIGKILL it -
prevents a misbehaving process from holding the surface forever.
- Only spawn the new instance after the old one is confirmed gone.
Affects every caller that hits the non-systemd-managed restart path
(menu's update-process actions, voxtype install/remove, font-change
follow-ups, etc.).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
nomarchy-theme-set chains six optional "tell each app the theme changed"
steps. Each used `command -v X && X || true`, which collapsed two very
different outcomes into the same silent path:
- X isn't installed -> skip (correct, expected, fine)
- X exists but returned non-zero -> skip (wrong - user just got a
half-applied theme with zero feedback about which app didn't refresh)
Replaced the inline guards with a small helper that distinguishes
absent from failed and accumulates real failures into a list. At the
end of the run, if anything failed, we notify-send a single message
naming the apps that didn't refresh ("Did not refresh: Waybar, btop")
and echo the same to stderr. The theme apply itself still completes -
we don't abort the chain on one failure - so the user gets the partial
benefit AND the diagnostic.
Same pattern as the waybar SIGUSR2 fix (386da51): make the hot path
loud about real problems while staying quiet about expected
no-installed states.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
After the cancel-branch sweep, the remaining UX inconsistency was in
action branches: changing one setting kicked the user out of the menu,
forcing a relaunch to change the next. Brought 16 actions across 5
submenus into the same return-to-self pattern theme/background got.
Classification rule applied:
- Quick-state actions (toggle, set, restart-service - finishes in
milliseconds, no window opens) -> back_to <self>, so the user can
chain "toggle nightlight, then toggle gaps, then restart waybar"
without rerunning nomarchy-menu each time.
- Window-opening actions (editor, floating terminal, audio/wifi/bt
launcher, browser, hyprpicker overlay, screenshot, screenrecord,
share dialogs, lock/shutdown/logout) stay as one-shot exits -
re-popping the menu over the new window would be visual noise.
Submenus changed:
- show_toggle_menu (8 toggles): screensaver, nightlight, idle, top
bar, workspace layout, window gaps, 1-window ratio, display scaling.
- show_setup_power_menu: powerprofilesctl set returns; cancel still
goes up to show_setup_menu (different destinations on each branch,
so the if/else stays).
- show_font_menu: nomarchy-font-set returns; cancel still goes up.
- show_setup_system_menu: the suspend toggle (quick) returns;
hibernate enable/disable (terminal) still exit.
- show_update_process_menu (5 service restarts): hypridle, hyprsunset,
swayosd, walker, waybar.
For dynamically-rendered menus (show_setup_system_menu rebuilds its
options each invocation based on current state) this also gives free
visual feedback - the toggle's label flips between "Enable Suspend"
and "Disable Suspend" when the menu re-renders.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Audit of all show_*_menu functions after the theme/background fix found
11 more cancel branches that called their parent directly instead of
back_to. None are reachable from current keybindings (today's direct
invocations target submenus that already use back_to), so the bug is
latent — but any future `nomarchy-menu <area>` keybinding into one of
these would bounce the user into the parent on Esc instead of exiting
cleanly, the exact bug that prompted the previous commit's fix to
show_theme_menu / show_background_menu.
Mechanical sweep:
*) show_main_menu ;; -> *) back_to show_main_menu ;; (5 sites)
*) show_setup_menu ;; -> *) back_to show_setup_menu ;; (3 sites)
*) show_update_menu ;; -> *) back_to show_update_menu ;; (3 sites)
Behavior under nested navigation (BACK_TO_EXIT=false) is unchanged:
back_to falls through to calling the parent function by name. Only
direct-invocation cancel paths gain the correct exit-0 behavior.
Action branches and go_to_menu's dispatch table intentionally still use
direct calls — those are forward navigation, not cancel.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The menu navigation contract: a submenu invoked directly via keybinding
(BACK_TO_EXIT=true, set by go_to_menu when nomarchy-menu is launched
with a target argument) should `exit 0` after the user's action; a
submenu invoked from a parent menu (BACK_TO_EXIT=false) should call
`back_to <parent>` to return where the user came from. back_to() honors
both modes.
Three submenus violated the contract:
- show_theme_menu and show_background_menu shell out to walker's
Elephant plugin and don't call back_to. After picking a theme or
wallpaper from Main -> Style -> Theme, the script exits silently
instead of returning to Style; the user has to relaunch the menu
from scratch to change anything else.
- show_hardware_menu's cancel branch called show_trigger_menu directly
instead of back_to show_trigger_menu, which would have bounced a
direct-keybinding caller into Trigger instead of exiting cleanly.
Adds the missing back_to call to the two walker-backed submenus
(parented to show_style_menu) and converts the hardware cancel branch
to back_to. The 16 other show_*_menu functions already conform.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The disk phase was the dominant source of incomplete installs. Six
concrete failure modes addressed in one pass:
1. Live-ISO USB excluded from the disk picker. select_disk previously
filtered loop|ram|zram|sr but not the device the installer booted
from; picking it would format the boot media mid-install. New
detect_live_iso_devices walks /, /iso, /run/initramfs/live,
/nix/.ro-store, /nix/store and resolves each backing device to its
parent disk via lsblk -no PKNAME. Override with
NOMARCHY_INSTALL_ALLOW_ISO_TARGET=1 for the developer case.
2. 10 GiB minimum-capacity preflight. Disko fails late and obscurely
on undersized media; surface it while the picker is still open.
3. prewipe_target_drive rewritten:
- Enumerates every active dm-crypt mapping via dmsetup ls and
closes those whose backing device is on the target drive. The
old version only knew about the hardcoded names "crypted" /
"crypted_main" so an aborted multi-disk run or a non-Nomarchy
install would leave a holder open and silently break the wipe.
- Drops `|| true` from wipefs / sgdisk / dd. After the LUKS and
swap teardown above, a real failure means something is still
holding the device — surface that instead of papering over it.
- udevadm settle bounded to 30s so a flapping USB can't hang.
- Post-wipe sanity check: refuse to hand the disk to disko if
anything is still mounted off it.
4. run_disko_with_retry wraps the disko call. On failure, shows the
last 30 lines of output via gum style and offers Retry /
View full log / Abort. set -e is suspended for the disko call so
the exit code can be inspected. The previous bare `disko --mode
disko` aborted the whole installer with output scrolled past.
5. Sed-templated disko-golden.nix + disko-btrfs-multi.nix pair
replaced by a single disko-config.nix Nix function of
{ mainDrive, extraDrives ? [] } called via --argstr / --arg.
Templating Nix via shell-escaped string substitution caused at
least one production bug (3aadc36 fixed embedded-newline
escaping); function arguments are the right shape and eliminate
the entire class of escaping concerns. Single-disk path is
`extraDrives = []`; multi-disk gets BTRFS `-d single -m raid1`
plus the additional /dev/mapper/* devices. Hosts that shipped
/etc/disko-golden.nix now ship /etc/disko-config.nix.
6. EXIT trap added so the tmpfs LUKS key file (/dev/shm/nomarchy-
luks.key) is removed even if the script aborts between key-write
and the explicit unset. Replaced redundant `shred -u` on tmpfs
with `rm -f` (already in RAM).
Verification: bash -n on install.sh, nix-instantiate parse + strict
eval on disko-config.nix in both single and multi shapes, full
nix flake check --no-build evaluating all three NixOS configurations
(default, nomarchy-installer, nomarchy-live) plus the installerVm.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Theme-switching ran systemctl --user restart waybar.service, which tears
down waybar's wayland layer-shell surface and creates a new one
back-to-back. Hyprland needs a frame to clear the destroyed surface; the
new instance attaches its surface immediately, so for a frame or two the
old waybar pixels remain visible behind/under the new bar - the
"artifacts and old colors on top of new" symptom most visible on the
fresh compositor of the live ISO.
Switch to SIGUSR2 reload, which makes waybar re-read config.jsonc and
CSS (including @import-ed files like ~/.config/nomarchy/current/theme/
waybar.css that theme-switch rewrites) without destroying the surface.
Full systemctl start is kept for the cold-start case.
Drive-by: replace the `systemctl list-unit-files` presence check with
`systemctl cat` - list-unit-files returns 0 even on no-match, so the
old check would always pick the systemctl branch and never fall through
to the pkill fallback on systems where waybar isn't a systemd unit.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Mirror of nomarchy.system.gaming.enable. When on, injects a Hyprland
windowrulev2 = fullscreen, class:^(steam_app_).*$ so games launched
through Steam grab the whole screen instead of opening windowed.
Gated via lib.mkIf so the rule is absent when the option is off
(AGENT.md guardrail: features must be option-gated). The rule is
appended to wayland.windowManager.hyprland.extraConfig (types.lines)
so it composes cleanly with the existing source-line entry point in
features/desktop/hyprland/default.nix.
Closes the "Gaming - Hyprland window rule" Next-column roadmap row.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Implement a step-based state machine in main loop to support 'Back' navigation via Esc.
- Refactor all prompts to use safe exit-code capture (rc -eq 130/1) and handle 'not submitted' output.
- Add input flushing after Esc events to prevent cascading backtrack signals.
- Add short-circuit checks to every wizard stage for reliable skip-forward behavior.
- Fix sed error when generating multi-disk configurations by escaping newlines in additional_disks.
- Add explicit 'Set a hostname' message to the hostname prompt.
- Convert unsafe short-circuit lists to safe if statements to prevent set -e crashes.
- Change default monitor rule from 'preferred' to 'highres' in monitors.conf.
- Explicitly force 'highres' in the live ISO (nomarchy-live) to avoid low-res fallbacks on some hardware.
- Update roadmap.
- Added nomarchy.panelPosition option and state persistence.
- Updated Waybar to respect the panelPosition setting.
- Refactored nomarchy-welcome to use state.json instead of a flag file.
- Added prompts for theme, font, panel position, and starter home.nix generation.
- Updated documentation and roadmap.
- Allow selecting multiple drives in the TTY installer using gum choose --no-limit.
- Add installer/disko-btrfs-multi.nix template for BTRFS RAID/Single setups.
- Dynamically generate multi-disk disko configurations with LUKS-on-every-disk.
- Default to BTRFS 'single' data and 'raid1' metadata for maximum capacity across mismatched drives (e.g., 20GB + 120GB SSDs).
- Update roadmap and structure documentation to reflect the new capabilities.
- Rename installerIso and installerIsoGraphical to nomarchy-installer and nomarchy-live.
- Update host configurations with proper Nomarchy branding and volume IDs.
- Fix nomarchy-test-live-iso QEMU launch by using -drive if=pflash for UEFI firmware.
- Add nomarchy-build-live-iso utility script.
- Scrub remaining Omarchy references in Plymouth, installer messages, and docs.
- Regenerate docs/SCRIPTS.md to reflect new and renamed utilities.
Previously the review screen only offered Confirm/Abort, so a typo or
wrong-disk choice meant aborting the whole run and starting over (or
hand-editing /tmp/nomarchy-install.state.sh). On --resume the situation
was worse: every prompt re-runs (each short-circuits when its var is
set), the user lands on a review they can't change.
review_configuration() now offers Continue / Edit a field / Abort. Edit
opens a multi-select of every saved field; chosen fields clear and the
next loop iteration in main() re-prompts only those. The LUKS passphrase
short-circuits when already set, so editing other fields doesn't
re-prompt for it.
Net flow change:
- Fresh install: same prompts, then review with Edit option (typo fixes
without restarting).
- --resume: state loads, every prompt skips (vars set), lands straight on
review — exactly what the roadmap entry called for.
Verified via `bash -n`. Live VM dry-run not exercised in this session.
Covers: option-already-declared (duplicate mkOption), attribute-missing
(forgot to import nomarchy.nixosModules.system), Stylix target conflict
(needs lib.mkForce, not bare bool), home-manager .hm-bak churn (left over
from backupFileExtension after first install), and impermanence path
missing (dir not in environment.persistence list).
Each entry has the literal error text, the cause, and a copy-paste fix.
Linked from README.md and docs/MIGRATION.md so users hit it before
guessing.
Opt-in `nomarchy.system.gaming.enable` (default false). Wires
`programs.steam` (with `remotePlay` and `localNetworkGameTransfers`
firewall holes opened via `mkDefault`), `programs.gamemode` (the
launching user must be in the `gamemode` group), and
`services.flatpak`.
Two pieces of the original roadmap entry split into separate
Next-column rows so the system-side preset ships now:
1. Hyprland fullscreen-on-Steam-launch window rule (home-side).
2. Declarative flathub remote (nixpkgs has no API for this; needs
either an overlay or a one-shot systemd unit).
The flatpak service is enabled but the user must add flathub
manually after first boot — documented in OPTIONS.md.
Opt-in `nomarchy.system.accessibility.enable` (default false —
accessibility is a personal preference, not hardware-derived). Wires
`services.gnome.at-spi2-core`, installs `pkgs.orca`, and sets
`XCURSOR_SIZE` to a configurable `accessibility.cursorSize` (default
32, up from NixOS's 24).
The original roadmap entry bundled Hyprland-side bits (slower
key-repeat, Orca launch keybinding, high-contrast palette). Those
require touching home-manager / theme files and a new palette
directory; split into a separate Next-column row so the system-side
preset ships now and the desktop integration follows independently.
Adds an 8th guardrail and replaces §5.4 with an explicit "if you change
X, update Y" mapping covering options, scripts, keybindings, structure,
installer, themes, roadmap, conventions, and flake-level changes.
Each row names the doc to touch. The closing line forces a one-pass
check before declaring a change done — eliminates "docs catch-up" PRs
and keeps the distro and its docs from drifting apart.
Mirror of the laptop preset for the desktop form factor. New
`nomarchy.system.desktop.enable` defaults to `formFactor == "desktop"`,
so the installer's existing formFactor write auto-flips it on without
installer changes (same pattern as laptop).
The module pins `powerManagement.cpuFreqGovernor` to `"performance"`
(via mkDefault) and enables `services.zfs.{autoScrub,trim}` so a
future ZFS pool gets sensible maintenance for free. The ZFS knobs are
no-ops until the user adds zfs to `boot.supportedFilesystems`.
Battery widget filtering is already driven by `formFactor` itself in
`features/desktop/waybar/default.nix`, so the preset doesn't repeat
it. Closes the "Desktop preset module" Next item.
Two detector bugs fixed:
1. grep_includes missed *.lua, *.ini, *.desktop, *.json — so callers in
elephant providers (lua), mako on-button-* hooks (ini), and any future
MimeType-registered URL handlers (.desktop) were invisible. Adding them
reclassifies nomarchy-notification-dismiss and nomarchy-theme-bg-set
from `unused?` to `kept` (true callers in mako/core.ini and the
elephant background_selector lua).
2. The all_refs regex `nomarchy-[a-z0-9][a-z0-9-]+` greedily captured
trailing dashes, producing junk missing-tokens like `nomarchy-pkg-`,
`nomarchy-cmd-`, `nomarchy-restart-`, etc. from glob references like
`for c in nomarchy-pkg-*`. Tightened to require an alphanumeric end
character. Also restricted to grep_includes so the binary tmpfile
path `nomarchy-menu-rows` no longer leaks in.
New .githooks/pre-commit re-runs the generator and stages docs/SCRIPTS.md
whenever a nomarchy-* script changes. Enable per clone with
`git config core.hooksPath .githooks` (now mentioned in docs/AGENT.md).
Net audit shift after regen: unused? scripts 31→29, missing tokens 30→28,
no false-positive prefix tokens remain.
New `nomarchy.system.laptop.{enable,thermald}` options. `enable`
defaults to `formFactor == "laptop"`, so the installer's existing
formFactor write auto-flips the preset on without installer changes.
The module wires TLP (governors + 75/80 charge thresholds),
force-disables power-profiles-daemon (mutually exclusive with TLP),
enables upower and thermald (x86_64), adds the brightnessctl udev
rule so the existing brightness scripts work without root, and sets
a logind lid-switch policy that resolves to suspend-then-hibernate
when `hibernation.enable` is on, plain suspend otherwise.
Closes the "Form-factor → laptop preset auto-enable" Now item and
the "Laptop preset module" Next item from docs/ROADMAP.md in one
change.
- Implement nomarchy-skill, nomarchy-manual, nomarchy-backup, nomarchy-install
- Implement nomarchy-install-docker-dbs (stub)
- Port nomarchy-docs-keybindings and nomarchy-docs-scripts to packaged scripts
- Add installerVm to flake.nix nixosConfigurations, packages, and apps
- Update nomarchy-test-installer to use nix run .#installerVm
- Add docker support to virtualization.nix and options.nix
- Add glow to script dependencies
- Finalize docs/SCRIPTS.md update
- Implement nomarchy-version, nomarchy-debug, nomarchy-reinstall, nomarchy-rollback, nomarchy-upload-log
- Implement nomarchy-refresh-hyprland and nomarchy-refresh-waybar
- Update docs/SCRIPTS.md with 'kept' status for new scripts
- Move 18 Hyprland/desktop scripts from features/desktop/scripts/ to packaged directories
- Add nomarchy.hardware.fwupd option (default false) and enable service
- Implement nomarchy-update-firmware wrapper for fwupdmgr
- Add hyprland, swayosd, and fwupd to nomarchy-system-scripts dependencies
- Update docs/SCRIPTS.md with 'kept' status for ported scripts
Phase B verdict on two unused? entries in the theme-engine scripts.
- nomarchy-theme-set-obsidian: real script that copies the active
theme's obsidian.css into every Obsidian vault under
~/.config/obsidian/obsidian.json. Wires it into nomarchy-theme-set
next to the btop/opencode hot-reloads. Self-gates twice (no
obsidian.css → exit 0; no .obsidian dir → continue), so it's a
no-op for users without Obsidian.
- nomarchy-theme-set-vscode: delete-dead. Its own comment admitted
it was "mostly a placeholder"; its only action (nomarchy-env-update)
is already done unconditionally upstream by nomarchy-theme-set.
The NOMARCHY_TOGGLE_SKIP_VSCODE_THEME env var it gated on is
exported by features/scripts/default.nix:73 from
nomarchy.toggles.skipVsCodeTheme, but with this script gone there
are no consumers; the toggle survives as a public option until a
follow-up wires it through the VSCode module properly.
SCRIPTS.md regenerated: unused? 34 → 32, kept 165 → 166. nix flake
check clean.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Phase B verdict on two unused? scripts — both inline comments
claimed they were "used by the Nomarchy theme switching", but the
switcher (themes/engine/scripts/nomarchy-theme-set) only restarted
walker, waybar, and the wallpaper service. So btop and opencode
stayed on the old palette after `nomarchy-theme-set <foo>` until
the user closed and reopened them by hand.
Wires both into nomarchy-theme-set, alongside the existing walker /
waybar restart calls. The check-then-call (`command -v ... &&`)
matches the surrounding style — a missing helper is a no-op, not a
fatal.
SCRIPTS.md regenerated: unused? 36 → 34, kept 163 → 165.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Phase B verdict on four core/system/scripts/nomarchy-hw-* entries
flagged `unused?` in the Phase A inventory. Wide grep confirmed
the only references were the audit doc itself.
Removed:
- nomarchy-hw-framework16 (superseded by `nomarchy-hw-match "Laptop 16"`
in nomarchy-on-boot)
- nomarchy-hw-surface (no caller; "Surface" string would route
through nomarchy-hw-match if needed)
- nomarchy-hw-intel (no caller; vendor detection isn't a public
API — installer/hardware-db.sh handles install-time dispatch and
nomarchy.hardware.* options handle build-time)
- nomarchy-hw-intel-ptl (same — Panther Lake GPU detection isn't
used anywhere)
Kept: nomarchy-hw-match (the dispatcher), nomarchy-hw-asus-rog
(called by nomarchy-on-boot), nomarchy-hw-vulkan (called by
nomarchy-voxtype-install).
SCRIPTS.md regenerated: unused? 40 → 36; nix flake check clean.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Scope: small Phase B improvement to the discovery heuristic so the
audit table stops false-flagging documented user-CLI tools as unused.
The generator now grep -r searches *.md, *.txt, *.sample alongside
*.nix / *.conf / *.sh, and explicitly walks README.md. SCRIPTS.md,
ROADMAP.md, and AGENT.md are excluded from the search (they document
the scripts but aren't callers — including them would promote every
script to `kept`).
Status histogram: 158 → 163 kept, 45 → 40 unused?, 75 → 85 missing
(the missing bump comes from grepping aspirational scripts named in
ROADMAP — wait, that doc is excluded — so the new missing rows are
references in MIGRATION/STRUCTURE/creating-themes that name scripts
which don't exist).
Per-script triage of the remaining 40 unused? rows is the next Phase
B batch.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
bin/utils/nomarchy-docs-scripts walks features/scripts/utils,
core/system/scripts, and themes/engine/scripts; emits a populated
SCRIPTS.md with three tables:
- Scripts (136): location, top callers, status (kept / unused?).
- Missing references: tokens grepped from code with no script file
(75 rows tagged missing).
- Menu items: every case arm in nomarchy-menu's show_*_menu
functions, mapped to its target command and tagged.
Status histogram: 158 kept, 75 missing, 45 unused?. Phase B opens
per-batch PRs that refine missing → port-from-omarchy /
delete-dead / stub-with-notify, and unused? → kept / delete-dead.
Roadmap and AGENT.md updated to point at the generator and
explain the Phase B workflow. Now-column row replaced with the
Phase B handoff.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replaces the bare `NAME SIZE` lsblk listing in select_disk with a
six-column table — NAME, SIZE, TYPE, VENDOR, MODEL, SERIAL — aligned
via column -t. TYPE is derived from ROTA + TRAN (NVMe / USB / SSD /
HDD). Empty vendor/model/serial fields render as `--` instead of
collapsing the alignment. Filters loop, ram, zram, sr devices.
Roadmap row moves to Shipped.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
bin/utils/nomarchy-docs-keybindings parses every bindd= / bindeld=
line in the core + feature binding files into a six-section Markdown
table (Utilities, Tiling, Tiling v2, Clipboard, Media keys, Apps).
233 bindings rendered. code:NN keycodes and XF86* media keys are
prettified.
README's keybinding table is slimmed to five highlights and now
links the generated doc; the roadmap's Now-column row moves to
Shipped.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Self-contained handbook so a fresh agent (or future-me) can land
useful work on the first turn: vision, repo layout, guardrails,
how to find work, the per-change workflow, common patterns, and
hard-don't-do rules. Points at ROADMAP.md / SCRIPTS.md as the
durable work queue.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Keeps every long-form doc under docs/ — only README.md remains at the
repo root. Updates the two references (README.md, docs/ROADMAP.md).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
ROADMAP.md is the durable mid-term plan: vision, guardrails, Now/Next/
Later board, and seven pillars (audit, installer, power/presets,
onboarding/docs, test/CI/release, process). SCRIPTS.md is the
scaffolding for the Pillar 3 script & menu audit — methodology,
generator commands, and a snapshot of currently orphaned callers.
The two open items in TODO.md (software-profile multi-select, richer
disk metadata) move into the roadmap's Now column; the rest of TODO.md
was already shipped, so the file is removed.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Surface the in-place migration path next to the clean-install wizard
so existing NixOS users discover it without spelunking the repo.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Catalogues every nomarchy.{system,hardware,…} and nomarchy.* (home) option
so downstream flake users can see what's available without grepping
options.nix. Linked from the Configuration & Usage section of README.md.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- autostart nm-applet --indicator under uwsm-app
- install networkmanagerapplet system-wide
- swap Nautilus for Thunar in file-manager bindings
- close-window bound to SUPER+Q (was SUPER+W)
- switch the active monitor preset from retina/2x to 1x 1080p/1440p
- summer-night waybar: drop custom/battery + backlight from modules-right
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Installer prompts for keyboard layout (with optional variant) and locale
via curated short list + Other… fallback into the full localectl list;
applies to the live session immediately (loadkeys + hyprctl) so the
rest of the install types correctly. Generated system.nix emits
console.keyMap, i18n.defaultLocale, and services.xserver.xkb.{layout,
variant}.
- New nomarchy.{system,}.formFactor enum (laptop|desktop, default laptop).
Installer auto-detects via /sys/class/power_supply/BAT* and lets the
user flip the answer. Waybar drops the battery widget on desktop;
battery-monitor service is gated on the same option.
- Lift waybar tray out of the collapsed group/tray-expander in the default
theme so nm-applet's icon is visible without expanding the drawer.
- Live ISOs (TTY + graphical) get baseline mkDefault keyMap/locale so the
installer's runtime override always wins.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Tier A removals — small, half-wired modules nobody had asked for:
- makima (Copilot-key remapper): drop core/system/makima.nix, the
features/apps/makima/ keyboard.toml, the nomarchy-restart-makima script,
the `nomarchy.system.features.makima` option, the state-file binding,
the import in core/system/default.nix, and the "Key Remapping" entry
in nomarchy-menu. ~50 LoC + a service nobody asked for.
- Typora theme dir (core/home/config/Typora/) — Typora is a paid tool
Nomarchy doesn't even ship; the SUPER+SHIFT+W keybinding pointed at a
binary that wasn't on PATH.
- xournalpp settings (core/home/config/xournalpp/) — referenced
/usr/share paths that don't exist on NixOS.
- core/home/config/environment.d/fcitx.conf — manual env vars are
redundant once fcitx5 routes through NixOS's i18n.inputMethod.
Optionalization — three half-wired features now sit behind explicit
toggles, all default off (except keyring which keeps its existing
default-on):
- nomarchy.system.inputMethod.enable: new core/system/input-method.nix
uses NixOS's i18n.inputMethod with fcitx5 + mozc/chinese/table addons.
Drops the Hyprland exec-once line — i18n.inputMethod handles autostart.
- nomarchy.system.voxtype.enable: marker option for users who install
voxtype out-of-band (it's not in nixpkgs). Today it just documents
intent; the existing keybinding + waybar widget no-op gracefully.
- nomarchy.apps.opencode.enable: gates the existing
features/apps/opencode/default.nix xdg.configFile so the opencode
config only deploys when the user opts in.
Installer:
- system.nix and home.nix templates now surface the new toggles in their
"Optional Nomarchy modules" comment blocks.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Five opt-in modules lifted from bernardo/nixos and adapted to Nomarchy's
nomarchy.system.* option namespace. All default off (except keyring which
defaults on); evaluation of the existing VM/ISO is unchanged when the
toggles are unset.
- core/system/snapper.nix: BTRFS timeline snapshots (5h/7d), nixos-rebuild-snap
wrapper that pre-snaps before each switch using the running hostname.
Auto-skips when / isn't BTRFS so impermanence/non-BTRFS hosts are safe.
- core/system/hibernate.nix: suspend-then-hibernate on lid/idle/power-key
with configurable idleMinutes (default 30). Description warns swap is
required.
- core/system/containers.nix: rootless Podman with dockerCompat + dns +
podman-compose, podman-tui, dive. Better default than the docker daemon
for a desktop distro.
- core/system/virtualization.nix: extends the existing uwsm/Hyprland file
with a libvirt + virt-manager + OVMF branch behind
nomarchy.system.virtualization.libvirt.enable.
- core/system/pam.nix: GNOME Keyring auto-unlock at SDDM/login/hyprlock
plus gcr-ssh-agent so SSH keys flow through the keyring instead of a
separate ssh-agent. Default on.
- core/system/options.nix: declares the five new options.
- core/system/default.nix: imports the four new files.
- installer/install.sh: surfaces all five toggles as commented one-liners
in the "Optional Nomarchy modules" section of the generated system.nix.
Verified via the existing dry-run / generator smoke test.
Verified each toggle lights up the right NixOS option (services.snapper,
logind IdleAction, virtualisation.podman/libvirtd, pam.sddm.enableGnomeKeyring)
via nix eval against extendModules. VM and live-ISO toplevels still build.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Upstream Nomarchy is hosted on the self-hosted Gitea at
git.bemagri.xyz/bernardo/Nomarchy.git, not github.com/bemagri/nomarchy.
- installer/install.sh: generated `nomarchy.url` now uses
`git+https://git.bemagri.xyz/bernardo/Nomarchy.git` (with `?rev=<sha>`
for the pinned form).
- MIGRATION.md: matches; the `hardware_detect` clone snippet now points
at the same URL.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
nix-colors is a pure data flake (base16 schemes) — it declares no inputs,
so `inputs.nixpkgs.follows = "nixpkgs"` against it produces a warning on
every evaluation. Drop the override and just `url`-import it.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Documents the layered-adoption path: add nomarchy as a flake input, build
a single pkgs with nomarchy.overlays.default, wire nomarchy.nixosModules
into both nixosSystem and a standalone homeManagerConfiguration, and
rebuild. Preserves the user's hardware-configuration, hostname, and
account; no reformat.
Calls out the conflicts an existing config will hit (DM, Hyprland, audio,
NetworkManager, user groups, /etc/os-release rebrand, autoLogin) with
explicit mkForce/drop-this-line guidance. Points at the live-ISO clean
install as the alternative for users who'd rather start fresh.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- boot.initrd.kernelModules force-loaded amdgpu+radeon+nouveau+i915 in the
live ISO, panicking on hosts where more than one driver matched the GPU.
Move them to availableKernelModules so udev only loads what fits.
- Override services.getty.helpLine with a Nomarchy welcome that points at
`sudo /etc/install.sh` (+ --dry-run / --resume variants). Replaces the
upstream "NixOS" message.
- Ship installer/hardware-db.sh as /etc/hardware-db.sh so the installer's
auto-detection works when invoked from the live ISO.
- Add live-ISO-only Hyprland exec-once: notify-send + an alacritty
"Nomarchy Installer" terminal that prints the install commands. The
graphical autologin user now sees a pointer instead of guessing how to
start the install.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds command-line flags and safety rails on top of the existing install.sh.
CLI:
- `--dry-run` generates the flake into /tmp/nomarchy-dryrun.* and parse-checks
every produced file without touching the disk. Skips LUKS / user password
prompts and the destructive confirmation; sets safe stub values.
- `--resume` reloads non-secret answers from /tmp/nomarchy-install.state.sh
(saved via `declare -p` after each step) and skips already-answered prompts.
Passwords are NEVER persisted — the user re-enters them.
- `--help` documents the flags.
Safety:
- Bail early in check_environment if /sys/firmware/efi is absent. The disko
config assumes UEFI + ESP; on a BIOS-booted host we'd partially install
before failing.
- After nixos-install, run `nixos-rebuild dry-build --flake /etc/nixos#$HOSTNAME`
inside `nixos-enter` to surface evaluation errors while the live ISO is
still around to fix them.
- ENABLE_IMPERMANENCE now defaults to "" so the resume path can distinguish
"not yet asked" from a deliberate "false" answer.
Generated config:
- system.nix gets `zramSwap.enable = true;` — near-free memory headroom on
small machines, harmless on big ones (kernel only uses it under pressure).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Hardware:
- New installer/hardware-db.sh: flat regex table mapping sys_vendor +
product_name to nixos-hardware modules (Framework, Dell, Lenovo, Surface,
ASUS, Apple T2, System76).
- install.sh:select_hardware now auto-detects CPU vendor, GPU vendor, chassis
type, and known model, then offers Accept / Add / Override. Manual menu
retained as a fallback.
- Fixes a latent bug where HARDWARE_MODULES used literal "\n" inside a
heredoc, producing invalid Nix.
Downstream flake:
- Capture the running Nomarchy commit and pin `nomarchy.url` to it so the
installed system can't drift onto a newer breaking main.
- Prompt for a real hostname; nixosConfigurations.<hostname> replaces the
generic .default. networking.hostName lands in system.nix.
- Generated flake now derives a single `pkgs` from nixpkgs + Nomarchy's
`overlays.default` and shares it between nixosSystem and the standalone
homeManagerConfiguration so dotfile-fast-iteration with nomarchy-env-update
stays separate from `nixos-rebuild` while still seeing Nomarchy packages.
- `nix flake lock` runs in /mnt/etc/nixos before nixos-install so first boot
consumes the resolved set.
- Post-install, run home-manager switch inside `nixos-enter` via runuser so
the user's first login already has dotfiles. Failure is non-fatal.
Disk layout:
- /boot bumped to 1 GiB (was 512 MiB; tight with multi-generation kernels).
- New @snapshots subvolume at /.snapshots for snapper/btrbk/rollback.
- LUKS passphrase moved from /tmp/secret.key to /dev/shm/nomarchy-luks.key
(tmpfs), shredded after disko, LUKS_PASSWORD unset.
Branding:
- New core/system/branding.nix sets system.nixos.distroId = "nomarchy" and
distroName = "Nomarchy". /etc/os-release now reports Nomarchy, so fastfetch
and other os-release readers show the right name.
Cleanup:
- flake.nix exposes `overlays.default = nomarchyOverlay` for downstream use.
- Trailing duplicated `main "$@"` + orphan `}` removed from install.sh.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Migrate VM and graphical ISO to home-manager.nixosModules.home-manager;
drop the standalone-HM sudo-based activation script (ran HM against
/root because HOME wasn't reset) in flake.nix, core/system/vm-guest.nix,
hosts/live-iso.nix.
- Run swaybg as nomarchy-wallpaper.service instead of a silent Hyprland
exec-once so failures surface in systemctl.
- Skip the battery monitor unit on hosts without /sys/class/power_supply/BAT*
(VMs, desktops).
- Don't wrap walker --dmenu in uwsm-app; redirect setsid background std-fds
in nomarchy-launch-walker so $(menu ...) in nomarchy-menu doesn't hang.
- Restart waybar/walker via systemctl --user rather than pkill + uwsm-app
to stop the post-theme-switch color race.
- Wire nomarchy-restart-walker/-waybar into nomarchy-theme-set so themes
that only change the imported CSS reload correctly.
- Waybar: pin #custom-nomarchy to the Nomarchy font and use the U+F000
codepoint so the logo shows across all themes.
- Auto-install the correct icon-theme package per palette via a new
nomarchyLib.iconThemePackage helper in lib/default.nix; Everforest now
actually renders for summer-night.
- Pre-cache every theme's HM generation: new packages.allThemeVariants
flake output and nomarchy-themes-prebuild script so theme switches are
cache-only (no Stylix rebuild, no downloads).
- Add nomarchy-test-live-iso to boot the graphical ISO in QEMU the same
way nomarchy-test-vm does, with virtio-gpu support added to live-iso.nix.
- Installer-generated home.nix/system.nix now ship a curated, commented
app menu (btop/fastfetch/chromium on by default) plus optional system
services (Docker, libvirtd, Tailscale, Syncthing, Flatpak, Steam).
- nomarchy-test-vm now wipes the stale nomarchy.qcow2 before launch.
- Remove obsolete GEMINI.md and PLAN.md.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Update app configurations (btop, kitty, ghostty, opencode, lazygit, tmux) to manage individual files instead of entire directories.
- Use 'recursive = true' for chromium and elephant configurations to allow multiple modules to contribute to the same directory.
- This prevents conflicts when the theme loader tries to add themed config files into directories that were previously managed as monolithic symlinks.
- Add 'nixpkgs.config.allowUnfree = true' to core/system/default.nix for global NixOS configuration.
- Add 'nixpkgs.config.allowUnfree = true' to features/default.nix for Home Manager package set.
- This resolves evaluation errors when VS Code is included in the build.
- Move 32+ app-specific scripts from features/apps/scripts/ to features/scripts/utils/ for centralized packaging.
- Create individual Nix modules for orphaned app configurations (btop, kitty, tmux, etc.) in features/apps/ using xdg.configFile.
- Fix broken paths in core/system/makima.nix and features/apps/vscode.nix.
- Update VSCode configuration to use the modern 'profiles.default.userSettings' API, resolving deprecation warnings.
- Merge duplicate 'nomarchy-launch-walker' scripts into a single robust utility.
- Remove stale root 'config/' directory.
- Update README.md and docs/creating-themes.md to reflect the new architecture and keybindings.
- Ensure all modules are correctly imported and verified via nix flake check.
- Fix QEMU syntax and root filesystem conflicts in vm-guest.nix.
- Repair numerous broken relative paths and imports across the codebase.
- Set 'summer-night' as the default distro theme with full branding integration.
- Implement declarative system-wide font installation including the 'nomarchy' font.
- Fix Waybar startup by dynamically generating theme-aware CSS.
- Restore Hyprland keybindings (Super+Return, Super+Space) and wallpaper loading.
- Add missing scripts: nomarchy-launch-walker, nomarchy-toggle-waybar, nomarchy-refresh-config.
- Enable UWSM and correctly disable conflicting Hyprland systemd services.
- Reorganize directory structure into core/, features/, and themes/
- Colocate application Nix logic, configs, scripts, and theme overrides
- Implement 'Inversion of Control' for theming: apps now pull theme-specific layouts
- Update flake.nix and shared library paths to match the new structure
- Document the new Feature-Centric architecture in README.md
Stylix unconditionally imports all target modules, which expect certain
program options to exist even when those programs aren't enabled. This
causes evaluation errors.
Add stylix-compat.nix with stub options for:
- programs.neovim.initLua (maps to extraLuaConfig)
- programs.opencode.tui/themes
Also:
- Disable neovim/neovide stylix targets (we deploy theme lua via theme-loader)
- Set autoEnable = false to only enable explicitly listed targets
- Enable programs.neovim to satisfy stylix's neovim target
- Update stylix to latest version
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Theme System:
- Move all theme app configs to apps/ subdirectory (20 themes)
- Add theme-loader.nix for dynamic theme config deployment
- Simplify stylix.nix to focus on base theming only
Override System:
- Add overrides.nix for file-based config overrides
- Add behavior-configs.nix for non-visual configuration
- Split hypr/nomarchy.conf into behavior vs visual sections
Module Improvements:
- Add lib.mkDefault to all customizable settings
- Add modules/lib/ with shared utilities and state schema
- Update all home and system modules for downstream overridability
Installer:
- New minimal TTY installer (installer/install.sh)
- Golden path: BTRFS + LUKS2 (disko-golden.nix)
- New installer-iso.nix for TTY-only installation
- Keep graphical installer as installerIsoGraphical option
Cleanup:
- Remove obsolete install.sh, disko-ext4.nix, install-nomarchy.sh
- Update live-iso.nix references
- Add .claude/ to .gitignore for local IDE settings
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Restore custom SDDM theme.
- Clean up all remaining Hyprland windowrulev2 and layerrule syntax errors.
- Optimize VM configurations with 4GB RAM and virtio-vga graphics for stability.
- Remove diagnostic debug services and autologin debug settings.
- Ensure correct session autologin to hyprland-uwsm.
- Increase VM memory to 8192MB to avoid OOM kills during session startup.
- Migrate all 'windowrule' to 'windowrulev2' in core configuration.
- Fix 'no_anim' syntax in layer rules.
- Re-enable graphics while keeping serial logs for debugging.
- Temporarily disable 'nomarchy-on-boot' and 'nomarchy-welcome' in Hyprland to isolate hang.
- Increase VM memory to 4096MB and cores to 4.
- Enable serial console debugging for VM.
- Enable qemuGuest service.
- Restore 'quiet' and 'splash' to kernel parameters to enable Plymouth.
- Re-enable graphics and virtio drivers for VM configurations.
- Add Interface=Qt5 to SDDM theme metadata for proper loading.
- Set default autoLogin.session to hyprland-uwsm to ensure correct session start.
- Enable services.xserver for improved display manager compatibility.
- Add virtio_gpu to initrd.kernelModules for early graphics support in VMs.
- Add 'render' group to VM users to ensure GPU acceleration.
- Move autologin defaults to sddm.nix to ensure they are consistently applied.
- Ensure SDDM theme has better session detection for uwsm.
- Enable programs.uwsm for session management.
- Improve SDDM theme session detection logic to be case-insensitive and more robust.
- Perform a global cleanup of 'Nnomarchy' and 'org.nnomarchy' typos across all scripts and configurations.
- Optimized assets/branding/icon.txt with multi-color placeholders for fastfetch.
- Updated config/fastfetch/config.jsonc to use the new multi-colored icon.
- Added icon and svg branding assets to modules/home/theme-files.nix.
- Fixed 'Nnomarchy' typo to 'Nomarchy' and updated branding paths in several scripts.
- Corrected screensaver and welcome scripts to use the centralized branding assets.
- Relocate themes to assets/themes/ and update all references.
- Implement custom SDDM theme and Plymouth theme enhancements.
- Add themed templates for Alacritty, Hyprland, Waybar, and other apps.
- Introduce Makima key remapper module and configuration.
- Add Voxtype and Walker configurations.
- Implement systemd power management and timeout optimizations.
- Add Nautilus-python extensions for LocalSend.
- Update branding assets and ASCII art integration.
- Remove unused pkg and cmd helper scripts
- Optimize Plymouth settings for cleaner boot in all environments
- Skip hardware auto-detection logic in Live ISO environment
- Refactor installer with Gum for a professional interactive experience
- Create custom Nomarchy Plymouth theme with centered logo
- Establish assets/branding directory for official logos and ASCII art
- Update Fastfetch config with official ASCII art and declarative stats
- Declaratively link branding assets via Home Manager
- Consolidate imperative system settings into /etc/nixos/state.json
- Implement nomarchy.system options for DNS, Wifi powersave, Timezone, and hardware features
- Add declarative browser policies for Chromium/Brave based on theme
- Update toggles scripts to mutate system JSON and run sys-update --impure
- Remove obsolete imperative browser theme and redundant system modules
2026-04-04 19:22:47 +01:00
441 changed files with 9447 additions and 10053 deletions
# Nomarchy - A NixOS-based distribution with Omarchy flavour - Agent Build Blueprint
## System Architecture Overview
Nomarchy uses a **Modular Merging Architecture**. The distro is distributed as a Nix Flake that exports two primary modules: `nixosModules.system` and `nixosModules.home`. This approach ensures strict separation between the "Upstream" core and the "Downstream" user configuration.
Users interact with the system by importing the distro's modules into their own `flake.nix` and then layering their own personal modules (`system.nix` and `home.nix`) on top. This follows the native NixOS design pattern for declarative, multi-module systems.
## Directory Structure
*`flake.nix` (Master entry point with core modules and test configurations)
*`installer/install-nomarchy.sh` (Interactive installer generating a clean downstream flake)
*`modules/system/default.nix` (Distro-wide OS defaults: SDDM, Plymouth, Audio, Network)
*`modules/home/default.nix` (Distro-wide user environment: Hyprland, Waybar, Styling)
*`bin/` (The collection of Omarchy productivity and config scripts)
*`themes/` (Distro-wide color palettes and backgrounds)
## Core Components & Logic
### 1. Separation of Concerns (Upstream vs. Downstream)
* **Upstream:** The `nomarchy/` directory contains the core logic. It is treated as an immutable input by the target system.
* **Downstream:** The user's `/etc/nixos/flake.nix` imports the upstream modules. The user's personal customizations are kept in `/etc/nixos/system.nix` and `/etc/nixos/home.nix`.
* **Merging:** NixOS automatically merges definitions. For example, if both the distro and the user add packages to `home.packages`, the final system includes the union of both lists.
### 2. The Interactive Installer (`installer/install-nomarchy.sh`)
The installer is designed to bootstrap a fresh system with this modular structure:
1.**Repo Detection:** Identifies the location of the Nomarchy source.
2.**Scaffolding:** Creates a new downstream `flake.nix` that imports Nomarchy core modules.
3.**User Files:** Generates skeleton `system.nix` and `home.nix` files for the user to customize.
4.**Flake Updates:** Uses the public Git repository as the upstream source, allowing users to update their system via `nix flake update`.
### 3. Home Manager Integration
* **Hybrid Declarative State:** While the distro is declarative, it uses state files in `~/.config/home-manager/` (managed by the distro's scripts) to allow for instant UI feedback (theming, fonts, wallpapers) without needing a full system rebuild for every small tweak.
* **Script Wrapping:** All scripts in `bin/` are wrapped with their specific dependencies (`swayosd`, `pulseaudio`, `jq`, etc.) to ensure they work reliably across different hardware.
### 4. Dynamic Theming
* **Palettes:** Themes are defined in simple `colors.toml` files. The distro dynamically generates Base16 palettes from these, allowing for infinite theme expansion without modifying Nix code.
## Verification & Build
***Test Installer:**`./bin/nomarchy-test-installer` (Builds a VM of the installer environment).
***Check Integrity:**`nix flake check --impure` (Verifies all configurations evaluate).
***Build ISO:**`nix build .#nixosConfigurations.installerIso.config.system.build.isoImage` (Generates the flashable USB image).
**Nomarchy** is a professional-grade NixOS distribution that replicates the Omarchy Wayland workflow with a strictly declarative, flake-based architecture. It provides a highly polished, "it just works" experience for power users who want a beautiful Hyprland environment without sacrificing the reliability of NixOS.
**The rock-solid reproducibility of NixOS 26.05. The out-of-the-box polish of
Omarchy/Omakub.** One JSON file rules the look of the entire desktop; every
theme change is a Home Manager generation — atomic, rollbackable, never
partial.
## ✨ Key Features
- **Purely Declarative:** Your entire desktop—themes, fonts, wallpapers, and toolchains—is defined in code.
- **Modular Merging Architecture:** Distro code and user code are strictly separated. You can update the distro core without ever touching or breaking your personal configurations.
- **Interactive "Smart" Installer:** Detects hardware, sets up networking, localizes timezones/keymaps, and offers multi-select software profiles.
- **Erase Your Darlings (Optional):** Optional BTRFS root-wipe on boot ensures your system stays pristine and 100% declarative.
- **Dynamic Theming Engine:** 20+ built-in themes with instant UI feedback via IPC (swww, waybar, and stylix).
- **Portable State (`nomarchy-sync`):** Easily backup and sync your declarative config and dynamic state (theme/wallpaper choices) to a private Git repo.
---
## 📥 Installation
### 1. Try it in a VM (Recommended)
Verify the experience without touching your hardware:
```bash
./bin/nomarchy-test-installer
```
This builds a full graphical VM of the installer environment. Once inside, click the **Install Nomarchy** icon or run `sudo /etc/install-nomarchy.sh`.
### 2. Build the Installer ISO
To install on physical hardware, generate your own bootable image:
| `username` | — (required) | Login name; flows into `system.nix` and names the HM config |
| `hardwareProfile` | `null` | One [nixos-hardware](https://github.com/NixOS/nixos-hardware) module name, or a list of them (pinned + tested by Nomarchy; unknown names fail with suggestions) |
| `system` | `"x86_64-linux"` | Platform |
(Power users can skip `mkFlake` and wire `nixosModules.nomarchy` /
`homeModules.nomarchy` / `overlays.default` by hand — the wrapper is sugar.)
| `nomarchy.stateFile` | — (required) | Path to your theme-state.json |
| `nomarchy.terminal` | `"ghostty"` | Terminal for keybinds and `$TERMINAL` |
| `nomarchy.keyboard.layout` | `"us"` | XKB layout for the Hyprland session (installer writes the matching `services.xserver.xkb` in system.nix; the console + LUKS prompt follow via the distro default) |
| `nomarchy.keyboard.variant` | `""` | XKB variant for the session |
| `nomarchy.keyboard.devices` | `{}` | Per-device layout overrides (Hyprland `device` blocks keyed by `hyprctl devices` name) — e.g. an external keyboard with its own layout/variant |
| `nomarchy.keyboard.layouts` | `[]` | Extra candidate layouts; when set, a watcher prompts (rofi) for a layout on a newly-connected keyboard and remembers it per-device |
| `nomarchy.nightlight.enable` | `false` | Scheduled blue-light filter (hyprsunset) — warm at night (`.temperature`, default 4000K) between `.sunset`/`.sunrise`, no shift by day |
# Launch a floating terminal with the Nomarchy logo presentation, then execute the command passed in, and finally end with the nomarchy-show-done presentation.
echo "IMPORTANT: Remove './nomarchy-features/fido2.nix' from your imports and run 'sys-update'."
else
echo "FIDO2 support not found."
fi
exit 0
fi
if [ -f "$FEATURE_FILE" ]; then
echo "FIDO2 support is already configured in $FEATURE_FILE"
else
sudo mkdir -p "/etc/nixos/nomarchy-features"
cat <<EOF | sudo tee "$FEATURE_FILE" > /dev/null
{ config, pkgs, ... }:
{
security.pam.u2f = {
enable = true;
control = "sufficient";
cue = true;
# authFile = "/etc/fido2/fido2"; # Default is ~/.config/Yubico/u2f_keys
};
}
EOF
echo "Created $FEATURE_FILE."
echo "IMPORTANT: To finish enabling FIDO2 support, add './nomarchy-features/fido2.nix' to your imports list in /etc/nixos/system.nix or /etc/nixos/flake.nix,"
echo "then run 'sys-update'."
fi
# Enrollment is still an imperative action
if command -v pamu2fcfg &> /dev/null; then
echo "Let's register your FIDO2 key now."
mkdir -p ~/.config/Yubico
pamu2fcfg > ~/.config/Yubico/u2f_keys
echo "FIDO2 key registered."
else
echo "pamu2fcfg not found. Please run 'nomarchy-pkg-add pam-u2f' or 'sys-update' if you just enabled it."
echo "IMPORTANT: Remove './nomarchy-features/fingerprint.nix' from your imports and run 'sys-update'."
else
echo "Fingerprint support not found."
fi
exit 0
fi
if [ -f "$FEATURE_FILE" ]; then
echo "Fingerprint support is already configured in $FEATURE_FILE"
else
sudo mkdir -p "/etc/nixos/nomarchy-features"
cat <<EOF | sudo tee "$FEATURE_FILE" > /dev/null
{ config, pkgs, ... }:
{
services.fprintd.enable = true;
# NixOS's fprintd module automatically configures PAM for login/sudo if enabled.
}
EOF
echo "Created $FEATURE_FILE."
echo "IMPORTANT: To finish enabling fingerprint support, add './nomarchy-features/fingerprint.nix' to your imports list in /etc/nixos/system.nix or /etc/nixos/flake.nix,"
echo "then run 'sys-update'."
fi
# Enrollment is still an imperative action
if command -v fprintd-enroll &> /dev/null; then
echo "Let's enroll your fingerprint now."
fprintd-enroll "$USER"
else
echo "fprintd-enroll not found. Please run 'sys-update' first if you just enabled it."
# Display the Nomarchy logo in the terminal using green color.
# Used by various presentation scripts to show branding.
clear
echo -e "\033[32m"
cat <~/.local/share/nomarchy/logo.txt
echo -e "\033[0m"
echo
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.