# 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. ``` ┌──────────────────────────────────────────────────────────────────────────┐ │ theme-state.json (single source of truth) │ │ lives INSIDE your flake checkout, git-tracked │ └───────────────────────────────────┬──────────────────────────────────────┘ │ nomarchy-theme-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) │ │ Ghostty (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 ├── theme-state.json # ★ THE single source of truth (git-tracked!) ├── themes/ # 21 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 │ └── 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 │ ├── ghostty.nix │ └── btop.nix ├── hosts/ │ ├── default/ # reference machine (thin: boot, user, hostname) │ └── live.nix # bootable live ISO (try the distro, no install) ├── pkgs/ │ ├── nomarchy-theme-sync/ # state writer + rebuild dispatcher (Python) │ └── nomarchy-install/ # live-ISO installer (gum + disko + mkFlake) ├── templates/downstream/ # `nix flake init -t` starter for users ├── docs/TESTING.md # how to verify changes (incl. AI-agent rules) └── tools/ # maintainer-only ├── import-palettes.py # converts old-distro themes → JSON + assets └── test-live-iso.sh # build the ISO + boot it in QEMU ``` **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) 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/*.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). 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), 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 github:YOUR-USER/nomarchy ``` You own two files day-to-day: `system.nix` and `home.nix` (plus `theme-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 ``` Override anything with plain NixOS/HM options (the distro uses `mkDefault` throughout) or the `nomarchy.*` surface: | Option | Default | Purpose | |---|---|---| | `nomarchy.stateFile` | — (required) | Path to your theme-state.json | | `nomarchy.terminal` | `"ghostty"` | Terminal for keybinds and `$TERMINAL` | | `nomarchy.hyprland.enable` | `true` | Nomarchy's Hyprland config | | `nomarchy.waybar.enable` | `true` | Nomarchy's Waybar | | `nomarchy.ghostty.enable` | `true` | Nomarchy's Ghostty | | `nomarchy.btop.enable` | `true` | btop with per-theme colors | | `nomarchy.stylix.enable` | `true` | GTK/Qt/cursor theming | | `nomarchy.themesDir` | Nomarchy's `themes/` | Where per-theme app overrides are probed | | `nomarchy.system.greeter.enable` | `true` | greetd/tuigreet | | `nomarchy.system.audio.enable` | `true` | Pipewire stack | | `nomarchy.system.bluetooth.enable` | `true` | Bluetooth + blueman | ## 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 = ./theme-state.json;`. Reading it is pure — it's flake source. It must be git-tracked (`nomarchy-theme-sync` runs `git add --intent-to-add` after every write as a safety net). ### One change = one generation `nomarchy-theme-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, Ghostty, 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. The **wallpaper** is the one runtime piece (awww — nixpkgs' swww — is imperative; nothing in Nix consumes the path): applied at session start and after every switch via a tiny activation hook, cycled instantly with `bg next`. ### 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) | Six ported themes ship a `waybar.css` identity (catppuccin, lumon, nord, retro-82, summer-day, summer-night). Custom user themes can live in `$NOMARCHY_PATH/themes/` (preset lookup) and `nomarchy.themesDir` (eval-time asset probe). ## 5. Day-to-day ```sh nomarchy-theme-sync list # 21 presets (nord, gruvbox, rose-pine, …) nomarchy-theme-sync apply kanagawa # whole desktop, one generation (~a switch) nomarchy-theme-sync set ui.gapsOut 16 # tweak one knob (also a switch) nomarchy-theme-sync bg next # cycle wallpapers — instant, no rebuild nomarchy-theme-sync bg auto # back to the theme's default wallpaper nomarchy-theme-sync get colors.accent ``` Keybinds: `SUPER+Return` terminal · `SUPER+D` launcher · `SUPER+T` theme picker · `SUPER+SHIFT+T` next wallpaper · `SUPER+Q` close · `SUPER+1..9` workspaces · `Print` region screenshot. ## 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 `theme-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/`. ## Roadmap - **Faster switches:** move `backgrounds/` out of the flake source (the 86 MB re-copy on every state write is the main eval tax), then pre-built theme variants if still needed - Plymouth + SDDM/greeter theming from the same JSON - Installer round 2: multi-disk BTRFS RAID, impermanence, BIOS/legacy boot (v1 `nomarchy-install` is single-disk UEFI — see `pkgs/nomarchy-install`) - `hyprlock`/`hypridle`, swayosd, launch-or-focus UX scripts