# 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/ # 24 presets: .json + optional / assets │ ├── nord.json # palette (required, works alone) │ └── nord/ # assets (optional, fixed filenames) │ ├── backgrounds/ # wallpapers (auto-picked, SUPER+SHIFT+T cycles) │ ├── 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](docs/REQUIREMENTS.md)** (UEFI x86_64, disk planning for the Nix store + BTRFS snapshots — 128 GiB is the comfortable floor). Boot the full desktop from a USB stick or VM without installing anything: ```sh 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](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](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: ```sh 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](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: ```nix 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](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.) Two deliberately separate rebuild paths: ```sh 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: ```sh 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-rebuild` → `nomarchy-rebuild`, `home-update` → `nomarchy-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](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+` 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](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](docs/HARDWARE.md) | | `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](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](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 ` 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](docs/RECOVERY.md#4-how-generations-are-kept-and-cleaned-up). 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//`) 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 | | `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 | Four themes ship a `waybar.css` identity (summer-day, summer-night, executive-slate, boreal). Custom user themes can live in `$NOMARCHY_PATH/themes/` (preset lookup) and `nomarchy.themesDir` (eval-time asset probe). ## 5. Day-to-day ```sh nomarchy-state-sync list # 24 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](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: ```sh # 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//` 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 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](agent/BACKLOG.md)** | What to do next (agent queue) | | **[docs/VISION.md](docs/VISION.md)** | Product themes toward **v1.0** | | **[docs/ROADMAP.md](docs/ROADMAP.md)** | Design history + shipped log | | **[docs/README.md](docs/README.md)** · **[agent/README.md](agent/README.md)** | Maps | | **[agent/LOOP.md](agent/LOOP.md)** | Autonomous iteration protocol | Kept out of the README body so this stays a focused entry point. License: [MIT](LICENSE).