Bernardo Magri 374f70c76f
All checks were successful
Check / eval (push) Successful in 4m15s
docs(backlog): re-measure #146 — zero genuine hypridle zombies post-fix
Re-ran the measurement per the entry's own recipe on the dev box (user
journal persistent to 2026-05-01, full window). Post-linger-fix
(2026-07-16 ~18:00 on) every `Disconnected from pollfd id 1` is a clean
teardown bracketed by a logind `Session N logged out` + a systemd
`Stopped hypridle` within <=11s (07-16 20:13, 07-17 17:04, 07-18 13:35,
07-19 16:46). Zero genuine zombies. Live daemon healthy (ss Recv-Q 0,
NRestarts=0, active, ExecMainPID==pid). Caveat: all four were deliberate
logouts, no compositor crash post-fix yet, so the crash path is confirmed
by argument not observation, and 4 days < the "few weeks" bar. Set an
explicit close condition (~2026-08-10 if still 0). Not closing today.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 19:48:00 +01:00

Nomarchy

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.

┌──────────────────────────────────────────────────────────────────────────┐
│                  state.json  (single source of truth)              │
│                  lives INSIDE your flake checkout, git-tracked           │
└───────────────────────────────────┬──────────────────────────────────────┘
                                    │
        nomarchy-state-sync apply gruvbox                                   
            1. merges the preset into the JSON (atomic write)               
            2. runs `home-manager switch` (no sudo, no system rebuild)      
                                    │
                                    ▼ pure read (nomarchy.stateFile)
┌──────────────────────────────────────────────────────────────────────────┐
│  Home Manager bakes EVERYTHING into one read-only generation:            │
│    Hyprland (colors/gaps/borders)      Waybar (palette or whole-swap)    │
│    Kitty (full ANSI palette)           btop (asset or generated)         │
│    Stylix → GTK, Qt, cursors, fonts                                      │
└───────────────────────────────────┬──────────────────────────────────────┘
                                    ▼
              wallpaper via awww (the one runtime piece:                    
              applied post-switch + at session start, `bg next` cycles)     

1. Layout

Flat on purpose. Two module trees, one options file each, no hidden layers.

.
├── flake.nix                      # inputs + the downstream API (exports below)
├── lib.nix                        # nomarchy.lib.mkFlake — one-call downstream wrapper
├── state.json               # ★ THE single source of truth (git-tracked!)
├── themes/                        # 28 presets: <slug>.json + optional <slug>/ assets
│   ├── nord.json                  #   palette (required, works alone)
│   └── nord/                      #   assets (optional, fixed filenames)
│       ├── backgrounds/           #     wallpapers (pinned nomarchy-wallpapers input, not this repo)
│       ├── btop.theme             #     hand-made config drop (else generated)
│       └── waybar.css             #     whole-swap: replaces the generated bar style
├── modules/
│   ├── nixos/                     # the distro, system side
│   │   ├── default.nix            #   Hyprland session, Pipewire, greetd, fonts
│   │   ├── options.nix            #   nomarchy.system.* toggles
│   │   ├── plymouth.nix           #   boot splash, tinted from the JSON
│   │   ├── file-manager.nix       #   Thunar GUI + gvfs/tumbler/udisks2
│   │   └── power.nix              #   power-profiles-daemon/TLP, thermald, charge limit
│   └── home/                      # the distro, user side
│       ├── default.nix            #   entry point
│       ├── options.nix            #   nomarchy.* option surface
│       ├── theme.nix              #   JSON ingestion + wallpaper hook
│       ├── stylix.nix             #   GTK/Qt/cursors/fonts from the same JSON
│       ├── hyprland.nix           #   all JSON-driven
│       ├── waybar.nix
│       ├── kitty.nix
│       ├── btop.nix
│       ├── rofi.nix               #   launcher + nomarchy-menu (calc, emoji, clip…)
│       ├── keybinds.nix           #   single source: Hyprland binds + SUPER+? sheet
│       ├── swaync.nix             #   notifications, same JSON
│       ├── idle.nix               #   hyprlock + hypridle, same JSON
│       ├── battery-notify.nix     #   low-battery toasts at the bar's thresholds
│       ├── yazi.nix               #   flagship TUI file manager + plugins
│       ├── osd.nix                #   swayosd volume/brightness OSD
│       ├── shell.nix              #   zsh + starship + bat/eza/zoxide
│       ├── keys.nix               #   gpg-agent fronting SSH, pinentry-qt
│       └── fastfetch.nix          #   themed Nomarchy logo + system info
├── hosts/
│   ├── default/                   # reference machine (thin: boot, user, hostname)
│   └── live.nix                   # bootable live ISO (try the distro, no install)
├── pkgs/
│   ├── nomarchy-state-sync/       # state writer + rebuild dispatcher (Python)
│   ├── nomarchy-install/          # live-ISO installer (gum + disko + mkFlake)
│   ├── nomarchy-doctor/           # read-only health sheet, one command per failure
│   └── nomarchy-battery-notify/   # low-battery toast watcher backing battery-notify.nix
├── templates/downstream/          # machine flake SoT (`flake init` + installer copy/patch)
├── docs/                          # human docs — map: docs/README.md
│   ├── VISION.md                  #   product themes toward v1.0 (not a queue)
│   ├── ROADMAP.md                 #   design/decision records + shipped log
│   ├── HARDWARE.md                #   firmware, profiles, drivers
│   ├── TESTING.md · RECOVERY.md · OVERRIDES.md · MIGRATION.md
├── agent/                         # agent instructions + loop state — map: agent/README.md
│                                  #   BACKLOG (executable queue), LOOP, VERIFICATION, …
├── AGENTS.md                      # agent entry, any vendor (CLAUDE.md symlinks here)
├── .claude/                       # Claude Code adapter: permissions + subagent defs
└── tools/                         # maintainer-only
    ├── import-palettes.py         #   converts old-distro themes → JSON + assets
    ├── test-live-iso.sh           #   build the ISO + boot it in QEMU
    ├── test-install.sh            #   full offline-install regression in QEMU
    └── vm/                        #   headless-VM helpers (QMP keys, VNC shots,
                                   #   offline-pin gap analysis)

Rule of thumb: modules/ is the distro (reusable, no machine specifics), hosts/ is a machine, themes/ is data, pkgs/ is code, tools/ is maintainer-only. If a new file doesn't obviously belong to one of those, it probably shouldn't exist.

2. Try it first (live ISO)

Before you install: check docs/REQUIREMENTS.md (UEFI x86_64, disk planning for the Nix store + BTRFS snapshots — 128GiB is the comfortable floor).

Boot the full desktop from a USB stick or VM without installing anything:

nix build .#nixosConfigurations.nomarchy-live.config.system.build.isoImage
# → result/iso/nomarchy-live-….iso — dd to a stick, or boot it in QEMU:
tools/test-live-iso.sh

The live session auto-logs-in, seeds the flake at ~/.nomarchy, and pins the locked inputs into the ISO store — so theme switching (including the home-manager switch it triggers) works offline, exactly like on an installed system. Verification checklist: docs/TESTING.md.

It's a working desktop, not a demo shell: Chromium, LibreOffice, Text Editor, Amberol and Snapshot are in the launcher alongside the terminal tooling — so you can read these docs in a browser while you try it, or boot the stick to rescue a machine that won't start and get a document off it. Installed machines get their app list from the template's home.packages instead, which is yours to edit.

Like what you see? nomarchy-install (in a terminal) walks you through installing to disk: pick a disk, LUKS2 full-disk encryption by default (in exchange the desktop logs in passwordless — the passphrase already gates the machine), user + hostname + timezone, hardware autodetection (DMI → nixos-hardware profile — see docs/HARDWARE.md), a hibernation-ready swapfile sized to RAM, then disko partitions (GPT + ESP + BTRFS subvolumes incl. @snapshots — snapper timeline snapshots are on) and nixos-install runs — without a network when the ISO was built from a clean tree (the target's flake.lock is composed from the rev the ISO carries). The installed machine gets the standard downstream layout: the flake at ~/.nomarchy (/etc/nixos symlinks to it), one mkFlake call, your system.nix/home.nix. First boot lands in the fully themed desktop — the installer pre-activates the Home Manager generation. UEFI only for now.

3. Using Nomarchy on your machine (downstream)

Nomarchy is consumed as a flake input — you never fork or edit this repo:

mkdir my-machine && cd my-machine
nix flake init -t "git+https://git.bemagri.xyz/bernardo/nomarchy.git?ref=v1"

The template's own README then walks the first-run steps: a real hardware-configuration.nix, git init (flakes only see tracked files), the two first rebuilds, and landing the checkout at ~/.nomarchy so the theme CLI finds it. (The installer does all of this for you.)

Already running NixOS on this machine? No reinstall needed — adopt Nomarchy in place, reusing your existing hardware-configuration.nix: docs/MIGRATION.md.

You own two files day-to-day: system.nix and home.nix (plus state.json, written by the CLI). Your flake.nix is set up once — later by the installer — and never hand-edited; it's a single call:

outputs = { nomarchy, ... }:
  nomarchy.lib.mkFlake {
    src = ./.;
    username = "me";
    hardwareProfile = "framework-13-7040-amd"; # optional, nixos-hardware name
  };
mkFlake arg Default Purpose
src — (required) Your flake directory (./.)
username — (required) Login name; flows into system.nix and names the HM config
hardwareProfile null One 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.)

Two deliberately separate rebuild paths:

sudo nixos-rebuild switch --flake .#default   # system: rare
home-manager switch --flake .#me              # desktop: every theme change, no sudo

Day-to-day you'll use the shipped shortcuts instead:

nomarchy-pull      # nix flake update — refresh inputs (nomarchy, nixpkgs, …);
                   # optional git pull only if ~/.nomarchy tracks a remote
nomarchy-rebuild   # system rebuild against the CURRENT lock (sudo inside;
                   # BTRFS snapshot first when snapper is on; nvd diff after)
nomarchy-home      # home-manager switch — desktop only, no sudo, no lock bump
                   # (prints nvd + a "What changed" toast when the generation moved)
nomarchy-what-changed  # plain-language last rebuild (system + desktop; --summary for one-liners)

~/.nomarchy is your machine flake (system.nix / home.nix). Newer Nomarchy code arrives when flake.lock updates the nomarchy input (nomarchy-pull), not by git pull of that directory (many installs have no remote there — only local commits from auto-commit). With auto-commit on (menu: System Auto-commit), all three commands above also sweep any pending hand edits into a commit before they switch, so git -C ~/.nomarchy log mirrors your generations.

When Run
Pull newer Nomarchy / nixpkgs / inputs nomarchy-pull
You changed system.nix (or after pull) nomarchy-rebuild
You changed home.nix / theme / desktop nomarchy-home
Full upgrade (inputs + both layers) nomarchy-pull && nomarchy-rebuild && nomarchy-home

Order matters for distro updates. nomarchy-home rebuilds against the current flake.lock — it never updates inputs. A new Nomarchy revision lands only when the lock is updated (nomarchy-pull). So for an upstream desktop change: pull → rebuild → home. Home before pull rebuilds against the old inputs and silently skips new home-side changes. After a home-side keybind/config change, also hyprctl reload (or relogin) so the session re-reads it.

Legacy aliases still work: sys-update → pull+rebuild, sys-rebuildnomarchy-rebuild, home-updatenomarchy-home.

Override anything via the nomarchy.* surface or plain NixOS/HM options: appearance (gaps/colors/fonts) changes through nomarchy-state-sync, behaviour (input/misc/monitor/chrome) is mkDefault so a plain home.nix assignment wins, and bind/exec-once lists concatenate. Full guide with examples: docs/OVERRIDES.md.

Where each option goes. nomarchy.system.*, nomarchy.hardware.*, and nomarchy.services.* are NixOS options — set them in system.nix. Everything else under nomarchy.* is a Home Manager option — set it in home.nix. The two tables below are split along exactly that line.

home.nix (Home Manager — the desktop):

Option Default Purpose
nomarchy.stateFile — (required) Path to your state.json
nomarchy.terminal "kitty" Terminal for keybinds and $TERMINAL (Kitty is the only shipped/themed emulator)
nomarchy.kitty.enable true Nomarchy's Kitty config (palette/font/opacity from theme-state)
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 [] Optional layouts to put first in the new-keyboard picker; the watcher runs by default, offers every installed XKB layout, and remembers the choice per-device. Pick/change one anytime at System Keyboard
nomarchy.nightlight.enable false Scheduled blue-light filter (hyprsunset) — warm at night (.temperature, default 4000K) between .sunset/.sunrise, no shift by day; set BOTH .latitude/.longitude for geo mode (wlsunset computes sunrise/sunset from your location; the fixed times are ignored); off by default — enable it from the menu (System Night light; the first enable rebuilds), then toggle on/off instantly from the menu or the Waybar moon indicator (writes settings.nightlight.on in your flake, no rebuild) and it survives reboot, so it stays reproducible
nomarchy.updates.enable false Passive update awareness: a background check (.interval, default daily) that flags when flake inputs (nixpkgs, the Nomarchy input, …) are behind upstream — and, with Flatpak on, when apps have updates (.flatpak) — via a Waybar indicator + notification. Never changes anything; click the indicator to run the upgrade flow
nomarchy.hyprland.enable true Nomarchy's Hyprland config
nomarchy.waybar.enable true Nomarchy's Waybar
nomarchy.rofi.enable true Themed rofi launcher + nomarchy-menu dispatcher
nomarchy.swaync.enable true swaync notifications, themed
nomarchy.batteryNotify.enable true Low-battery toasts at the bar's thresholds — 25% low, 10% critical (stays up until dismissed); silent no-op on machines without a battery
nomarchy.dockAudio.enable true On a fresh external-monitor plug, PipeWire/WirePlumber are reprobed after hardware settles and the highest-priority available HDMI/DP/USB sink becomes default, with a toast and journal result. A later manual speaker choice sticks until the next physical plug; unplug falls back to built-in. Manual route: System Audio
nomarchy.firstBootWelcome.enable true One dismissible “you're set” toast on the first session (SUPER+M / SUPER+T / SUPER+? + network pointer); marker is settings.firstBootShown in the flake checkout. Also fires at most one follow-up “Hardware tips” toast pointing at System Firmware / Fingerprint when fwupdmgr / fprintd-list are on PATH (settings.hardwareHintsShown)
nomarchy.idle.enable true hyprlock + hypridle (lock 5 min; display off 10 min in every dock/lid state — input wakes it compositor-side, #127; suspend 15 min battery-only via nomarchy-suspend)
nomarchy.idle.fingerprint false Unlock the lock screen with a fingerprint as well as the password, and say so on the input field. Set it alongside nomarchy.hardware.fingerprint.pam in system.nix: hyprlock is configured from Home Manager, which cannot read the NixOS option — and hyprlock does not take a fingerprint through PAM at all (its PAM stack runs only on submit), so it uses its own fprintd backend that this switch turns on
nomarchy.yazi.enable true yazi TUI file manager, themed + curated plugins
nomarchy.osd.enable true swayosd on-screen display for volume/brightness/mute
nomarchy.shell.enable true zsh + starship prompt + bat/eza/zoxide (zsh is the default login shell)
nomarchy.btop.enable true btop with per-theme colors
nomarchy.stylix.enable true GTK/Qt/cursor theming
nomarchy.fastfetch.enable true fastfetch fronted by the themed Nomarchy logo
nomarchy.keys.enable true gpg-agent fronting SSH + pinentry-qt
nomarchy.displays.enable true nwg-displays interactive monitor arranger (helper for nomarchy.monitors)
nomarchy.viewers.enable true Document/image viewers: zathura (Stylix-themed PDF) + imv
nomarchy.mime.enable true Default "open with" associations (PDF/image/video/text/browser/directory); entries for apps you removed are skipped, so it degrades with the suite
nomarchy.monitors [] Declarative per-output layout → Hyprland monitor rules (applied on hotplug); ,preferred,auto,1 wildcard kept as fallback
nomarchy.displayProfiles {} Named layouts for the same outputs (docked/undocked/…), switched from System Display Profiles: instant via hyprctl, persisted in-flake (settings.displayProfile), baked over nomarchy.monitors at the next rebuild. Profiles that disable the laptop panel use the same safe ordering as Dock mode (external on → workspace handoff → internal off). The menu's Auto-switch row (settings.displayProfileAuto) applies the matching profile from Hyprland hotplug events. Workspace pins (workspaces = { "1" = "DP-3"; }) are moved instantly and baked as rules
nomarchy.launchOrFocus [] Launch-or-focus binds: SUPER+<key> focuses the app's window (case-insensitive class match) or launches it; entries land in the SUPER+? cheatsheet, and a bind whose app was removed notifies instead of failing silently
nomarchy.themesDir Nomarchy's themes/ Where per-theme app overrides are probed
nomarchy.package overlay's nomarchy-state-sync The theme/state tool package, overridable if you fork it

Greeter keyboard layout: tuigreet runs on a kernel VT, which has exactly one keymap — console.useXkbConfig follows services.xserver.xkb.layout (nomarchy.keyboard.layout), not Hyprland's per-device kb_layout binding. Per-device layouts (nomarchy.keyboard.devices) only take effect once the Hyprland session starts, so at the login prompt an external keyboard types the system layout, not its remembered one — if your password comes out wrong there, type it as if on the system layout, or use the laptop's own keyboard. A VT constraint, not a bug.

Always-on, no toggle by design: services.cliphist, services.udiskie (automount + safe-removal toasts) and services.easyeffects (mic noise cancellation) ship unconditionally — small, low-risk pieces with no Nomarchy-specific config behind them (the toggle-vs-package rule in agent/CONVENTIONS.md). Disable one from home.nix with the plain HM option, e.g. services.easyeffects.enable = lib.mkForce false; — see docs/OVERRIDES.md.

system.nix (NixOS — the machine):

Option Default Purpose
nomarchy.system.plymouth.enable true Branded boot splash, background from the theme JSON (recolors on system rebuilds)
nomarchy.system.stateFile null state.json for the system-side consumers (the Plymouth tint); lib.mkFlake wires it for you — set it only when composing the modules by hand
nomarchy.system.fileManager.enable true Thunar GUI + gvfs/tumbler/udisks2 (the "open folder" handler)
nomarchy.system.greeter.enable true greetd/tuigreet
nomarchy.system.greeter.autoLogin state Auto-login this user into Hyprland. Owned by System Auto-login (settings.greeter.autoLogin; installer seeds it on LUKS machines) — set it here only to pin it against the menu
nomarchy.system.audio.enable true PipeWire stack; WirePlumber priority rules provide HDMI/USB preference/fallback, while the Home Manager dock watcher performs the fresh-display reprobe and explicit default selection
nomarchy.system.bluetooth.enable true Bluetooth + blueman
nomarchy.system.autoTimezone.enable false Automatic timezone (geoclue + automatic-timezoned) — the clock follows your location; toggle from System Auto timezone (a menu enable rebuilds: it has to unset the static time.timeZone)
nomarchy.system.snapper.enable false Hourly/daily BTRFS timeline snapshots + nixos-rebuild-snap (installer enables it; no-op unless root is BTRFS)
nomarchy.system.power.enable true Active power management (see below)
nomarchy.system.power.backend "ppd" "ppd" (power-profiles-daemon + menu/Waybar switcher) or "tlp" (deeper battery tuning, no switcher) — mutually exclusive
nomarchy.system.power.laptop false Marks a laptop, gating battery-only features; the installer sets it when a battery is present. Clamshell uses logind's HandleLidSwitchDocked=ignore; the desktop additionally holds a low-level lid-switch inhibitor across dock/undock, releasing only after the internal panel is restored and the lid opens. Normal future undocked lid-close suspend remains enabled
nomarchy.system.power.thermal.enable false thermald (Intel-only); the installer enables it on a GenuineIntel CPU
nomarchy.system.power.batteryChargeLimit null Stop charging at this % (e.g. 80) where the hardware supports it; needs power.laptop
nomarchy.system.power.suspendThenHibernate true (state) On battery, suspend falls through to hibernate after 1 hour (suspend-then-hibernate + HibernateDelaySec=1h; never while on AC). Needs a hibernate resume path (boot.resumeDevice). Toggle: System Preferences Suspend then hibernate (settings.power.suspendThenHibernate; system rebuild for logind lid policy). Idle and the Power menu use nomarchy-suspend (live state).
nomarchy.hardware.intel.enable false Intel enablement above nixos-hardware (GuC/HuC firmware via i915.enable_guc=3.guc toggles just that; the installer unsets it on xe-driver GPUs); the installer sets it on an Intel CPU/GPU
nomarchy.hardware.intel.computeRuntime false Opt-in: Intel GPU compute — OpenCL/Level-Zero (intel-compute-runtime) + oneVPL (vpl-gpu-rt)
nomarchy.hardware.amd.enable false AMD enablement above nixos-hardware (amd-pstate EPP + radeonsi VA-API, each toggleable via .pstate / .vaapi); installer-set on an AMD CPU/GPU
nomarchy.hardware.amd.rocm.enable false Opt-in: ROCm HIP/OpenCL GPU compute (multi-GB); pair with .gfxOverride (e.g. "11.0.0") for an unlisted iGPU
nomarchy.hardware.fingerprint.enable false fprintd for a detected fingerprint reader (installer-set); enroll with fprintd-enroll
nomarchy.hardware.fingerprint.pam false Opt-in: use the fingerprint for login + sudo (PAM)
nomarchy.hardware.fingerprint.parallel true With PAM on: password or fingerprint at the same prompt (whichever comes first); false = stock sequential pam_fprintd. Password alone always stays sufficient
nomarchy.hardware.npu.enable false Opt-in/experimental: load the on-die NPU driver (amdxdna/intel_vpu); userspace runtime is BYO
nomarchy.hardware.latestKernel false Opt-in: ship linuxPackages_latest instead of the default kernel — for very new hardware whose drivers landed recently
nomarchy.hardware.camera.hideIrSensor false Hide a dual-sensor webcam's IR node from PipeWire's v4l2 path so apps only ever see the colour camera (the "second, dark Integrated Camera"); installer-set on a paired RGB+IR webcam. /dev/video* stays open, so Howdy-style face unlock still works; .irMatch overrides the IR-name regex. Does not hide IR from libcamera / portal / Flatpak pickers — see HARDWARE.md §7
nomarchy.hardware.i2c.enable false I2C devices support — access to /dev/i2c-* (RGB controllers, sensors, DDC/CI monitor control)
nomarchy.hardware.i2c.ddcci false (distro default: true) the ddcci-driver kernel module, exposing external monitors as standard backlight devices via DDC/CI so brightness keys and swayosd natively control them
nomarchy.services.tailscale.enable false Opt-in: Tailscale mesh VPN — the login user is made the operator, so tailscale up/down/set and the System VPN menu work without sudo
nomarchy.services.syncthing.enable false Opt-in: Syncthing file sync as the login user (GUI at 127.0.0.1:8384)
nomarchy.services.podman.enable false Opt-in: rootless Podman (docker aliased to it)
nomarchy.services.flatpak.enable false Opt-in: Flatpak + the Flathub remote
nomarchy.services.pika.enable false Opt-in: Pika Backup (GUI Borg backups)
nomarchy.services.steam.enable false Opt-in: Steam via programs.steam (32-bit libs, controller udev, Remote-Play ports)
nomarchy.services.libvirt.enable false Opt-in: libvirt/KVM + virt-manager GUI (login user added to libvirtd)
nomarchy.services.obs.enable false Opt-in: OBS Studio + a v4l2loopback virtual camera (selectable as a webcam in Zoom/Teams)
nomarchy.services.docker.enable false Opt-in: rootful Docker (user in docker group); don't enable alongside podman
nomarchy.services.kdeconnect.enable false Opt-in: KDE Connect phone integration (opens its firewall ports)
nomarchy.services.gamemode.enable false Opt-in: Feral GameMode performance daemon (gamemoderun)
nomarchy.services.adb.enable false Opt-in: Android adb/fastboot tools (systemd handles device udev rules)
nomarchy.services.wireshark.enable false Opt-in: Wireshark Qt GUI (user in wireshark group, capture without root)
nomarchy.services.ollama.enable false Opt-in: Ollama local LLM runtime on 127.0.0.1:11434 (CPU; GPU via services.ollama.acceleration)
nomarchy.services.printing.enable false Opt-in: CUPS + Avahi/mDNS network printer discovery
nomarchy.services.openrgb.enable false Opt-in: OpenRGB daemon for RGB peripheral/motherboard lighting
nomarchy.services.restic.enable false Opt-in: scheduled daily restic backup (set .repository + .passwordFile; backs up .paths, default /home; 7/4/6 retention; list/restore via the restic-nomarchy wrapper)

Beyond the nomarchy.* surface, the system layer turns on the usual desktop services with lib.mkDefault (override natively) and enforces baseline security/stability defaults. A few worth calling out: security.apparmor.enable is on by default for mandatory access control confinement. Kernel panics are configured to auto-reboot the machine after 10 seconds (panic=10 oops=panic) rather than hanging indefinitely. services.fwupd.enable is on by default for firmware updates via LVFS — it only refreshes metadata, never flashes on its own, so run fwupdmgr update to apply; disable with services.fwupd.enable = false; on machines without real firmware (VMs/headless). Full hardware story (profiles, fingerprint, NVIDIA, unsupported machines, contributing a DMI line): docs/HARDWARE.md. And services.earlyoom is on by default so running out of memory kills the offending process (with a desktop notification) instead of freezing the desktop — process-level on purpose, since a Hyprland session is one cgroup and systemd-oomd would kill all of it (oomd is disabled accordingly). Opt out with services.earlyoom.enable = false;. zramSwap is also on by default (zstd, 50% of RAM, priority 100): day-to-day memory pressure pages into compressed RAM instead of the disk, keeping the disk swapfile free for hibernation — the installer creates a RAM-sized encrypted swapfile and wires resume, so Power Hibernate works out of the box (swap size 0 at install = no swap). Opt out with zramSwap.enable = false;; to add hibernation to a machine installed before it existed, follow docs/MIGRATION.md § Enabling hibernation.

4. How theming works

Pure JSON ingestion

The trap with "read a mutable file from Nix" is pure evaluation: flakes cannot read arbitrary $HOME paths without --impure (the old prototype required it — never again). Nomarchy's convention: the state file lives inside the consuming flake and is wired via nomarchy.stateFile = ./state.json;. Reading it is pure — it's flake source. It must be git-tracked (nomarchy-state-sync runs git add --intent-to-add after every write as a safety net).

One change = one generation

nomarchy-state-sync apply <theme> merges the preset into the JSON and runs home-manager switch (override the command with $NOMARCHY_REBUILD, or pass --no-switch to only write). Everything is baked: Hyprland, Waybar, Kitty, btop, and — via Stylix, mapped onto base16 roles — GTK, Qt, cursors and fonts. No runtime patching means no partial states, and home-manager generations is also your theme history. Waybar even restyles in place: it re-reads style.css when the symlink flips.

Cleanup is automatic. A weekly timer (nomarchy-gen-prune) removes system and Home Manager generations that are older than 14 days and beyond the three most recent past gens — so you always keep the current generation plus at least three rollbacks, even on a rarely rebuilt machine. Manual: sudo nomarchy-gen-prune --dry-run. Full story: docs/RECOVERY.md §4.

The wallpaper is the one runtime piece (awww — nixpkgs' swww — is imperative; nothing in Nix consumes the path): applied at session start, after every switch via a tiny activation hook, and again when a monitor is hotplugged; cycled instantly with bg next.

Config the menu writes (not just themes)

The in-flake-state model isn't only for appearance. Feature toggles you flip from the menu are written into a settings.* section of the same state file — git-tracked, reproducible, never stashed in ~/.local/state. The menu is just an ergonomic writer for your flake, so version-controlling your downstream reproduces the machine, settings and all. Where a toggle can take effect without a rebuild it does: the menu writes the key (--no-switch) and flips the running service, which reads the live flake state at session start, so the choice is both instant and survives reboot.

Night light is the first to use this — enable it from the menu (System Night light; the first enable rebuilds to install hyprsunset), then on/off is instant. Internally it's two keys: settings.nightlight.installed (sticky — gates the unit, the first enable rebuilds) and settings.nightlight.on (the instant on/off). Expect more nomarchy.* toggles to migrate to this pattern.

Auto-commit (opt-in): System Auto-commit makes every apply/set mutation also git commit state.json in your flake — only that file, so unrelated dirty work is never swept up — turning your settings history into git log. Off by default; the toggle is instant (nothing in Nix consumes the flag), the off-write is itself committed so history stays consistent, and wallpaper cycling (bg next) is deliberately excluded — the current wallpaper rides along with the next real commit.

Per-theme app assets (themes/<slug>/)

Recoloring covers 95% of theming; the rest is one optional assets directory per theme — a single place to look, unlike the old distro's split:

Asset Mechanism
backgrounds/ wallpapers; empty wallpaper in the state means "first one"; bg next cycles. Pinned in a separate nomarchy-wallpapers flake input, not this repo (ROADMAP § "Faster switches") — merged in at build time
btop.theme baked into the generation (generated from the palette when absent)
waybar.css whole-swap: replaces the generated bar style entirely (probed at eval time, self-contained)
waybar.jsonc whole-swap for the bar layout (must be plain JSON)
rofi.rasi whole-swap: replaces the generated launcher/menu theme entirely

Eight themes ship a waybar.css identity: the three Nomarchy-exclusive day/night pairs — boreal ↔ boreal-dawn, executive-slate ↔ executive-ivory, kiln ↔ kiln-clay — plus summer-night ↔ summer-day. Custom user themes can live in $NOMARCHY_PATH/themes/ (preset lookup) and nomarchy.themesDir (eval-time asset probe) — including their own backgrounds/, which wins over the pinned nomarchy-wallpapers input (checked first); an explicit wallpaper path in state.json works too.

5. Day-to-day

nomarchy-state-sync list               # 28 presets (nord, gruvbox, rose-pine, …)
nomarchy-state-sync apply kanagawa     # whole desktop, one generation (~a switch)
nomarchy-state-sync set ui.gapsOut 16  # tweak one knob (also a switch)
nomarchy-state-sync bg next            # cycle wallpapers — instant, no rebuild
nomarchy-state-sync bg auto            # back to the theme's default wallpaper
nomarchy-state-sync get colors.accent
nomarchy-pull                          # flake input update (no rebuild)
nomarchy-rebuild                       # rebuild the system, current lock
nomarchy-home                          # rebuild just the desktop layer
nomarchy-doctor                        # read-only health sheet (also: menu  System  Doctor)

Something broke anyway? Every rebuild is a generation and (on BTRFS) every hour is a snapshot — the undo story, from a bad theme to a machine that won't boot, is docs/RECOVERY.md.

Keybinds: SUPER+Return terminal · SUPER+Space launcher · SUPER+T theme picker · SUPER+SHIFT+T next wallpaper · SUPER+X power menu · SUPER+E file manager (yazi) · SUPER+N notifications · SUPER+CTRL+V clipboard history · SUPER+SHIFT+C color picker (hyprpicker) · SUPER+Q close · SUPER+1..9 workspaces · SUPER+ALT+arrow move workspace to monitor · Print region screenshot.

Shell aliases (zsh, gated on nomarchy.shell.enable) — alias lists them all; the curated set:

# navigation
..   ...   ....                 # cd up 1 / 2 / 3 levels

# git
g    gst                        # git · git status -sb
ga   gaa                        # git add · git add -A
gc   gcm                        # git commit · git commit -m
gco  gsw   gb                   # checkout · switch · branch
gd   gds                        # diff · diff --staged
gl   glg                        # log graph (last 20 · all branches)
gp   gpl   gf                   # push · pull · fetch --all --prune

# nix  (lifecycle: nomarchy-pull / nomarchy-rebuild / nomarchy-home — full names)
ns   nr                         # nix shell · nix run   (e.g. ns nixpkgs#ripgrep)
nfu  nfc                        # nix flake update · check  (prefer nomarchy-pull for day-to-day)
nsearch    ngc                  # nix search · nix-collect-garbage -d (prefer: sudo nomarchy-gen-prune)

# misc
path                            # print $PATH, one entry per line
reload                          # exec zsh (reload the shell)

6. Extending

  • New theme: drop a JSON into themes/ (schema = any existing preset), plus an optional themes/<slug>/ assets directory.
  • New themed value: add the key to state.json and consume it in the Nix modules. One place — there is no second renderer to keep in sync.
  • Importing more old-distro palettes: tools/import-palettes.py <palettes-dir> themes/ (roles are first-class: when ANSI color0==color8 the tool derives an overlay step; light themes do not use dark ANSI black as surface — hand-tune after import; do not bulk-reimport shipped JSON without a hierarchy pass).
  • New opt-in feature (convention): when a feature is off by default and needs the user to set nomarchy.* options (e.g. night light, per-device keyboard layouts, monitor layout, power management), ship a commented example of it in templates/downstream/home.nix or system.nix. New installs then enable it by uncommenting + tweaking, rather than hunting the docs for the option name.
  • Applications: the starter complete-workstation set (LibreOffice, VS Code, GIMP, Inkscape) lives in the home.packages list in your home.nix — it's a plain package list, so curate it directly: delete a line to slim the machine, uncomment an extra (a browser, email, full TeX Live), or add your own. No nomarchy.apps.* toggles — a package list is already its own opt-out, so the distro doesn't impose these or wrap them.
  • A newer individual app (unstable channel, #134): Nomarchy carries a second, newer nixpkgs channel and exposes it as unstable through its overlay, so any package can be taken from it by prefixing unstable. in home.packages — e.g. unstable.lmstudio — with no second flake input of your own to manage. Three costs, stated plainly: (a) you own this combination — nothing upstream tests a pinned Nomarchy plus an unstable app together; (b) an unstable package brings its own second toolchain into your closure (measured: unstable.lmstudio shares only ~690 of ~4k store paths with the pinned set); (c) it moves whenever the flake lock moves (nomarchy-pull), not on its own schedule. Home packages only — that's the supported surface, not system.nix or the module set.

Roadmap & known issues

Doc Role
agent/BACKLOG.md What to do next (agent queue)
docs/VISION.md Product themes toward v1.0
docs/ROADMAP.md Design history + shipped log
docs/README.md · agent/README.md Maps
agent/LOOP.md Autonomous iteration protocol

Kept out of the README body so this stays a focused entry point.

License: MIT.

Description
NixOS based distribution with Omarchy flavour
Readme MIT 308 MiB
Languages
Nix 71.4%
Python 11.9%
Shell 11.8%
CSS 4.9%