Compare commits

..

37 Commits

Author SHA1 Message Date
Bernardo Magri
72f7e7b93d docs(roadmap): log Hyprland keymap + waybar-toggle inconsistencies
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).
2026-05-19 20:24:27 +01:00
Bernardo Magri
20de3d4f97 chore(hyprland): delete orphan config files + share-picker dir
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.
2026-05-19 20:24:16 +01:00
Bernardo Magri
2a301a049f fix(mako): deploy themed config to ~/.config/mako/config
`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.
2026-05-19 20:24:06 +01:00
bd7e5a5706 Merge pull request 'wave/qa-core-system' (#3) from wave/qa-core-system into main
Reviewed-on: #3
2026-05-19 19:17:22 +01:00
Bernardo Magri
af8fa321ff docs(roadmap): log uwsm-in-virtualization module placement (Later)
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.
2026-05-19 19:13:47 +01:00
Bernardo Magri
6238f41e43 fix(hibernate): mkDefault on HandlePowerKey / IdleAction / IdleActionSec
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.
2026-05-19 19:13:23 +01:00
Bernardo Magri
fb4d5d7acc chore(schema): drop orphan features.makima
`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.
2026-05-19 19:13:23 +01:00
Bernardo Magri
99a6c7d547 fix(impermanence): user must match created account, not hardcoded "nomarchy"
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.
2026-05-19 19:13:23 +01:00
Bernardo Magri
85ef8745d7 chore: delete orphan config assets + log chromium/templates concerns
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.
2026-05-19 19:04:56 +01:00
Bernardo Magri
b82954a7b5 fix(options): drop dead skipVsCodeTheme + 4 themeLoader.apps toggles
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.
2026-05-19 19:04:25 +01:00
Bernardo Magri
66c98949ab chore(features): drop orphan userPackages reader
`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.
2026-05-19 19:04:25 +01:00
07e2d5c51c Merge pull request 'fix(home): remove dead behavior options, reserve overrides API' (#4) from wave/qa-core-home into main
Reviewed-on: #4
2026-05-19 18:49:22 +01:00
2529ca114f Merge branch 'main' into wave/qa-core-home 2026-05-19 18:49:15 +01:00
94927952db Merge pull request 'chore(lib): drop dead helpers, document schema boundary' (#2) from wave/qa-lib-schema into main
Reviewed-on: #2
2026-05-19 18:48:30 +01:00
0930458418 Merge pull request 'wave/qa-first-boot' (#1) from wave/qa-first-boot into main
Reviewed-on: #1
2026-05-19 18:48:02 +01:00
Bernardo Magri
95101fda3f fix(sddm): default autoLogin off, not on with hardcoded "nomarchy"
`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.
2026-05-19 18:46:41 +01:00
Bernardo Magri
6e0d17b859 fix(welcome): drop Step 4's dead starter home.nix generation
`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.
2026-05-19 18:46:30 +01:00
Bernardo Magri
27d1506b54 chore(lib): drop dead helpers, document schema boundary
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.
2026-05-19 18:28:54 +01:00
Bernardo Magri
90f07ae75c fix(home): remove dead behavior options, reserve overrides API
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.
2026-05-19 18:08:58 +01:00
Bernardo Magri
bfd95cb40b docs(roadmap): mark Pillar 5 preset rows as Shipped
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.
2026-05-19 17:28:16 +01:00
Bernardo Magri
9283403d8f docs(roadmap): add Pillar 8 QA audit + clean stale Pillar 4 rows
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.
2026-05-19 17:26:22 +01:00
Bernardo Magri
7bf4c3c637 fix(theme): set default to summer-night and fix scripts on live ISO
- 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.
2026-05-18 21:22:39 +01:00
Bernardo Magri
ec6046793e fix(installer): hardware-db references real modules + add ROG Ally + CI lint
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>
2026-05-18 18:31:48 +01:00
Bernardo Magri
2b6d452509 fix: nomarchy-manual opens local README, not an Omarchy URL
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>
2026-05-18 18:24:19 +01:00
Bernardo Magri
ac846f4b03 docs: STRUCTURE.md root listing + Pillar 6 reality-check
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>
2026-05-18 18:21:36 +01:00
Bernardo Magri
03968e5d0d fix(installer): generate state.json from lib/state-schema.nix
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>
2026-05-18 18:18:25 +01:00
Bernardo Magri
d264371b46 fix: complete hybridGPU wiring + make state-derived options overridable
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>
2026-05-18 18:12:09 +01:00
Bernardo Magri
9c672953bc fix(installer): pre-flight resume polish (4 gaps)
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>
2026-05-18 18:00:02 +01:00
Bernardo Magri
7fa909ddf4 fix: centralize state defaults via lib/state-schema.nix
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>
2026-05-18 17:52:47 +01:00
Bernardo Magri
5ddb15ffef ci: add Forgejo Actions workflow (eval + lint)
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>
2026-05-18 17:34:05 +01:00
Bernardo Magri
46738c3663 chore(audit): final Phase B batch — close out unused? cluster
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>
2026-05-18 17:19:57 +01:00
Bernardo Magri
9be4363f4b chore(audit): triage webapp/tui/voxtype install-remove pairs
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>
2026-05-18 17:13:39 +01:00
Bernardo Magri
f93eb7435f chore(audit): delete NixOS-irrelevant Omarchy port scripts
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>
2026-05-18 17:08:09 +01:00
Bernardo Magri
6b2c678669 chore: switch default theme from summer-night to nord
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>
2026-05-18 17:01:29 +01:00
Bernardo Magri
ac4d66e54d fix: nomarchy-sys-update targets actual hostname, not '#default'
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>
2026-05-18 17:01:22 +01:00
Bernardo Magri
098cd42ac8 fix(installer): harden multi-disk LUKS, password handling, revision pinning
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>
2026-05-18 17:01:15 +01:00
Bernardo Magri
158ae308cc fix(audit): close all missing-references rows in script audit
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>
2026-05-18 16:58:54 +01:00
70 changed files with 867 additions and 1044 deletions

View File

@@ -0,0 +1,102 @@
# Nomarchy CI — eval + lint.
#
# Catches the regressions that hurt today:
# 1. Flake stops evaluating (broken option ref, missing import, etc.).
# 2. A `nomarchy-*` shell script has a syntax error or a shellcheck
# error-severity issue.
# 3. `docs/SCRIPTS.md` drifts from the repo state because somebody
# added / removed / renamed a script and didn't run the generator
# (the pre-commit hook handles this, but only when enabled per-clone).
#
# Doesn't build ISOs — that needs a binary cache. Add a separate job
# once Cachix/Attic is in place.
name: Check
on:
push:
branches: [main]
pull_request:
jobs:
eval-and-lint:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
with:
# Match the runner's effective channel. Nomarchy itself tracks
# nixos-25.11 via flake.nix; the installer-action default is fine.
extra-conf: |
experimental-features = nix-command flakes
- name: nix flake check --no-build
run: nix flake check --no-build
- name: Lint nomarchy-* scripts (bash -n + shellcheck)
run: |
# Mirror what .githooks/pre-commit runs locally, but across the
# whole tree instead of just changed files. Pre-commit gates
# individual commits; CI gates branches (including --no-verify
# bypasses).
set -e
fail=0
while IFS= read -r script; do
[[ -f "$script" ]] || continue
# Python helpers ship under the same nomarchy- prefix
# (e.g. nomarchy-haptic-touchpad). Skip non-bash.
head -1 "$script" | grep -qE '^#!.*\bbash\b' || continue
if ! bash -n "$script"; then
echo "::error file=$script::bash syntax error"
fail=1
fi
if ! nix shell nixpkgs#shellcheck --command shellcheck \
--severity=error --shell=bash "$script"; then
echo "::error file=$script::shellcheck error-severity issue"
fail=1
fi
done < <(find features/scripts/utils core/system/scripts \
themes/engine/scripts \
-maxdepth 1 -type f -name 'nomarchy-*')
exit "$fail"
- name: docs/SCRIPTS.md is up to date
run: |
# Regenerate to a temp file and compare. If different, the
# contributor forgot to run the generator (or skipped the
# pre-commit hook). Fail loudly and tell them the fix.
./bin/utils/nomarchy-docs-scripts --out /tmp/SCRIPTS.regen.md
if ! diff -q docs/SCRIPTS.md /tmp/SCRIPTS.regen.md >/dev/null; then
echo "::error::docs/SCRIPTS.md is stale."
echo "Run: ./bin/utils/nomarchy-docs-scripts --out docs/SCRIPTS.md"
echo "Then commit the regenerated file."
echo "--- diff ---"
diff -u docs/SCRIPTS.md /tmp/SCRIPTS.regen.md || true
exit 1
fi
- name: installer/hardware-db.sh references real nixos-hardware modules
run: |
# Every 4th-pipe-field in HARDWARE_DB is a nixos-hardware module
# name. Half the DB used to point at modules that don't exist
# (e.g. microsoft-surface-pro-8 — there's only -pro-intel and
# -pro-9), which made the install fail at eval time with
# cryptic "attribute not found" errors on real laptops. This
# step catches that regression class.
awk -F'|' '/^ "/ { gsub(/"/,"",$4); gsub(/^[[:space:]]+|[[:space:]]+$/,"",$4); if ($4) print $4 }' \
installer/hardware-db.sh | sort -u > /tmp/db-refs.txt
nix eval --impure --json --expr '
let
nh = (builtins.getFlake (toString ./.)).inputs.nixos-hardware.nixosModules;
in builtins.attrNames nh' \
| nix shell nixpkgs#jq --command jq -r '.[]' | sort -u > /tmp/db-real.txt
missing=$(comm -23 /tmp/db-refs.txt /tmp/db-real.txt)
if [[ -n "$missing" ]]; then
echo "::error::hardware-db.sh references nixos-hardware modules that don't exist:"
printf ' - %s\n' $missing
echo "Either fix the name (check the actual attr in nixos-hardware) or drop the row."
exit 1
fi

View File

@@ -40,7 +40,8 @@ This builds a full graphical VM of the installer environment. Once inside, click
### 2. Build the Installer ISO ### 2. Build the Installer ISO
To install on physical hardware, generate your own bootable image: To install on physical hardware, generate your own bootable image:
```bash ```bash
nix build .#nixosConfigurations.nomarchy-installer.config.system.build.isoImage ./features/scripts/utils/nomarchy-build-iso # Minimal TTY installer
./features/scripts/utils/nomarchy-build-live-iso # Graphical try-before-install
``` ```
The ISO will be located at `./result/iso/nixos-*.iso`. Flash it to a USB drive and boot. The ISO will be located at `./result/iso/nixos-*.iso`. Flash it to a USB drive and boot.

View File

@@ -81,13 +81,34 @@ ref_files_per_cmd() {
# Final char must be alphanumeric — dropping trailing-dash matches like # Final char must be alphanumeric — dropping trailing-dash matches like
# `nomarchy-pkg-` that come from glob references (`for c in nomarchy-pkg-*`). # `nomarchy-pkg-` that come from glob references (`for c in nomarchy-pkg-*`).
# Restrict to grep_includes so binaries / tmpfiles don't pollute the set. # Restrict to grep_includes so binaries / tmpfiles don't pollute the set.
# We also filter out common prefix-only tokens that are false positives from # The first `grep -vE` drops lines where `nomarchy-*` is a derivation /
# wildcards/expansions. # tmp file / sudoers basename / systemd unit / flake output / docker
all_refs=$(grep -rohE 'nomarchy-[a-z0-9]([a-z0-9-]*[a-z0-9])?' \ # container identifier rather than a shell invocation.
# The second `grep -vE` is a token-level safety net for prefix-only
# tokens left over from wildcards/expansions (e.g. `nomarchy-pkg-*`).
all_refs=$(grep -rhE 'nomarchy-[a-z0-9]([a-z0-9-]*[a-z0-9])?' \
"${grep_includes[@]}" \ "${grep_includes[@]}" \
"${search_dirs[@]}" 2>/dev/null \ "${search_dirs[@]}" 2>/dev/null \
| grep -vE \
-e '(pname|name)[[:space:]]*=[[:space:]]*"nomarchy-' \
-e '/tmp/nomarchy-' \
-e '/etc/sudoers\.d/[^"[:space:]]*nomarchy-' \
-e 'nixosConfigurations\.nomarchy-' \
-e 'packages\.[^.]+\.nomarchy-' \
-e '\./result/bin/run-nomarchy-' \
-e 'mktemp[[:space:]]+[^|]*-t[[:space:]]+nomarchy-' \
-e '(TIMER_NAME|NOPASSWD_FILE|UNIT_NAME)=.*nomarchy-' \
-e 'docker[[:space:]]+[^|]*nomarchy-' \
| grep -oE 'nomarchy-[a-z0-9]([a-z0-9-]*[a-z0-9])?' \
| grep -vE '^(nomarchy-launch|nomarchy-brightness|nomarchy-cmd|nomarchy-pkg|nomarchy-restart|nomarchy-toggle|nomarchy-theme|nomarchy-webapp-handler|nomarchy-font-selector|nomarchy-theme-selector|nomarchy-wallpaper-selector|nomarchy-setup|nomarchy-refresh|nomarchy-scripts|nomarchy-system-scripts|nomarchy-theme-engine-scripts)$' \ | grep -vE '^(nomarchy-launch|nomarchy-brightness|nomarchy-cmd|nomarchy-pkg|nomarchy-restart|nomarchy-toggle|nomarchy-theme|nomarchy-webapp-handler|nomarchy-font-selector|nomarchy-theme-selector|nomarchy-wallpaper-selector|nomarchy-setup|nomarchy-refresh|nomarchy-scripts|nomarchy-system-scripts|nomarchy-theme-engine-scripts)$' \
| grep -vE '^(nomarchy-plymouth|nomarchy-sddm-theme|nomarchy-live|nomarchy-rev|nomarchy-windows)$' \
| sort -u) | sort -u)
# The second denylist covers identifiers whose ambiguity survives the line
# filter: `nomarchy-plymouth` / `nomarchy-sddm-theme` are Nix derivation
# names referenced as bare idents in `[...]` lists, `nomarchy-live` is an
# ISO label that shows up in comments, `nomarchy-rev` is `/etc/nomarchy-rev`
# (written by the ISO), and `nomarchy-windows` is a docker container name
# in compose heredocs.
# --- Render: header -------------------------------------------------------- # --- Render: header --------------------------------------------------------

View File

@@ -1,94 +0,0 @@
{ config, lib, ... }:
# Behavior Configuration Module
#
# This module deploys non-visual configuration files (keybindings, input settings,
# window rules, etc.) with lib.mkDefault, allowing downstream users to override.
#
# Visual/theme configs are handled separately by theme-loader.nix and stylix.nix.
#
# Behavior configs include:
# - Keybindings (bindings, media keys, clipboard)
# - Input settings (keyboard, mouse, touchpad)
# - Window rules and layouts
# - Autostart applications
# - Environment variables
let
configDir = ./config;
overridesDir = "${config.home.homeDirectory}/.config/nomarchy/overrides";
# Behavior config categories with their source paths
behaviorConfigs = {
# Hyprland behavior (non-visual)
"nomarchy/default/hypr/bindings.conf" = "hypr/bindings.conf";
"nomarchy/default/hypr/bindings/media.conf" = "hypr/bindings/media.conf";
"nomarchy/default/hypr/bindings/clipboard.conf" = "hypr/bindings/clipboard.conf";
"nomarchy/default/hypr/bindings/tiling-v2.conf" = "hypr/bindings/tiling-v2.conf";
"nomarchy/default/hypr/bindings/utilities.conf" = "hypr/bindings/utilities.conf";
"nomarchy/default/hypr/input.conf" = "hypr/input.conf";
"nomarchy/default/hypr/windows.conf" = "hypr/windows.conf";
"nomarchy/default/hypr/autostart.conf" = "hypr/autostart.conf";
"nomarchy/default/hypr/envs.conf" = "hypr/envs.conf";
"nomarchy/default/hypr/looknfeel.conf" = "hypr/looknfeel.conf";
# App-specific window rules (behavior, not visual)
"nomarchy/default/hypr/apps.conf" = "hypr/apps.conf";
"nomarchy/default/hypr/apps/qemu.conf" = "hypr/apps/qemu.conf";
"nomarchy/default/hypr/apps/steam.conf" = "hypr/apps/steam.conf";
"nomarchy/default/hypr/apps/terminals.conf" = "hypr/apps/terminals.conf";
"nomarchy/default/hypr/apps/walker.conf" = "hypr/apps/walker.conf";
"nomarchy/default/hypr/apps/browser.conf" = "hypr/apps/browser.conf";
"nomarchy/default/hypr/apps/1password.conf" = "hypr/apps/1password.conf";
"nomarchy/default/hypr/apps/bitwarden.conf" = "hypr/apps/bitwarden.conf";
"nomarchy/default/hypr/apps/pip.conf" = "hypr/apps/pip.conf";
"nomarchy/default/hypr/apps/system.conf" = "hypr/apps/system.conf";
"nomarchy/default/hypr/apps/localsend.conf" = "hypr/apps/localsend.conf";
"nomarchy/default/hypr/apps/telegram.conf" = "hypr/apps/telegram.conf";
"nomarchy/default/hypr/apps/geforce.conf" = "hypr/apps/geforce.conf";
"nomarchy/default/hypr/apps/moonlight.conf" = "hypr/apps/moonlight.conf";
"nomarchy/default/hypr/apps/retroarch.conf" = "hypr/apps/retroarch.conf";
"nomarchy/default/hypr/apps/webcam-overlay.conf" = "hypr/apps/webcam-overlay.conf";
"nomarchy/default/hypr/apps/davinci-resolve.conf" = "hypr/apps/davinci-resolve.conf";
"nomarchy/default/hypr/apps/hyprshot.conf" = "hypr/apps/hyprshot.conf";
};
in
{
options.nomarchy.behavior = {
hyprland = {
bindings = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to deploy default Hyprland keybindings.";
};
input = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to deploy default input settings.";
};
windowRules = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to deploy default window rules.";
};
autostart = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to deploy default autostart configuration.";
};
};
};
config = {
# Note: The actual config deployment is handled by configs.nix
# This module provides the options and documentation for behavior configs
# The separation allows users to selectively disable behavior categories
# Ensure behavior config directories exist in overrides
home.activation.createBehaviorOverrideDirs = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
mkdir -p "${overridesDir}/hypr/bindings"
mkdir -p "${overridesDir}/hypr/apps"
'';
};
}

View File

@@ -8,7 +8,7 @@ description: >
monitors, gaps, borders, blur, opacity, waybar, walker, terminal config, themes, monitors, gaps, borders, blur, opacity, waybar, walker, terminal config, themes,
wallpaper, night light, idle, lock screen, screenshots, layer rules, workspace wallpaper, night light, idle, lock screen, screenshots, layer rules, workspace
settings, display config, and user-facing nomarchy commands. Excludes Nomarchy settings, display config, and user-facing nomarchy commands. Excludes Nomarchy
source development in ~/.local/share/nomarchy/ and nomarchy-dev-* workflows. source development in ~/.local/share/nomarchy/ and repo-internal workflows.
--- ---
# Nomarchy Skill # Nomarchy Skill
@@ -34,7 +34,7 @@ It is not for contributing to Nomarchy source code.
**If you're about to edit a config file in ~/.config/ on this system, STOP and use this skill first.** **If you're about to edit a config file in ~/.config/ on this system, STOP and use this skill first.**
**Do NOT use this skill for Nomarchy development tasks** (editing files in `~/.local/share/nomarchy/`, creating migrations, or running `nomarchy-dev-*` workflows). **Do NOT use this skill for Nomarchy development tasks** editing files in `~/.local/share/nomarchy/` or modifying repo internals.
## Critical Safety Rules ## Critical Safety Rules
@@ -244,6 +244,9 @@ nomarchy-theme-set <name> # Apply theme (use "Tokyo Night" not "tokyo-nig
nomarchy-theme-next # Cycle to next theme nomarchy-theme-next # Cycle to next theme
nomarchy-theme-bg-next # Cycle wallpaper nomarchy-theme-bg-next # Cycle wallpaper
nomarchy-theme-install <url> # Install from git repo nomarchy-theme-install <url> # Install from git repo
nomarchy-theme-remove <name> # Remove an installed extra theme
nomarchy-theme-refresh # Re-apply current theme from templates
nomarchy-theme-bg-install # Open backgrounds dir to drop in custom images
``` ```
### Keybindings ### Keybindings
@@ -311,10 +314,48 @@ nomarchy-debug --no-sudo --print # Debug info (ALWAYS use these flags)
nomarchy-lock-screen # Lock screen nomarchy-lock-screen # Lock screen
nomarchy-system-shutdown # Shutdown nomarchy-system-shutdown # Shutdown
nomarchy-system-reboot # Reboot nomarchy-system-reboot # Reboot
nomarchy-sudo-passwordless-toggle # Toggle 15-min passwordless sudo
nomarchy-sudo-reset # Clear sudo lockout / faillock
nomarchy-restart-trackpad # Reload intel_quicki2c (fixes dead THC trackpad)
``` ```
**IMPORTANT:** Always run `nomarchy-debug` with `--no-sudo --print` flags to avoid interactive sudo prompts that will hang the terminal. **IMPORTANT:** Always run `nomarchy-debug` with `--no-sudo --print` flags to avoid interactive sudo prompts that will hang the terminal.
### Custom App Launchers
```bash
nomarchy-webapp-install # Add a web app launcher (interactive)
nomarchy-webapp-remove [name...] # Remove web apps (interactive if no name)
nomarchy-webapp-remove-all # Bulk-remove every web app
nomarchy-tui-install # Add a TUI launcher for a terminal program
nomarchy-tui-remove [name...] # Remove TUI launchers
nomarchy-tui-remove-all # Bulk-remove every TUI launcher
```
Both families write `.desktop` files into `~/.local/share/applications/` so they appear in the app launcher (walker / rofi).
### Voice dictation (Voxtype)
```bash
nomarchy-voxtype-install # Install Voxtype + AI model (~150MB)
nomarchy-voxtype-remove # Uninstall Voxtype
nomarchy-voxtype-status # Running state (also shown in waybar)
```
Toggle dictation with `SUPER+CTRL+X` after install.
### Virtualization
```bash
nomarchy-windows-vm install # Provision a Windows VM via docker-compose
nomarchy-windows-vm launch # Connect to the VM (auto-stop on disconnect)
nomarchy-windows-vm launch -k # Connect, keep VM running after disconnect
nomarchy-windows-vm stop # Shut the VM down
nomarchy-windows-vm status # Show current state
```
Requires KVM (`/dev/kvm`) and Docker (enable via `nomarchy.system.virtualization.docker`).
## Troubleshooting ## Troubleshooting
```bash ```bash
@@ -325,6 +366,7 @@ nomarchy-debug --no-sudo --print
nomarchy-upload-log nomarchy-upload-log
# Reset specific config to defaults # Reset specific config to defaults
# Examples: nomarchy-refresh-fastfetch, nomarchy-refresh-hyprland, nomarchy-refresh-waybar
nomarchy-refresh-<app> nomarchy-refresh-<app>
# Refresh specific config file # Refresh specific config file
@@ -352,7 +394,7 @@ When user requests system changes:
This skill intentionally does not cover Nomarchy source development. Do not use this skill for: This skill intentionally does not cover Nomarchy source development. Do not use this skill for:
- Editing files in `~/.local/share/nomarchy/` (`bin/`, `config/`, `default/`, `themes/`, `migrations/`, etc.) - Editing files in `~/.local/share/nomarchy/` (`bin/`, `config/`, `default/`, `themes/`, `migrations/`, etc.)
- Creating or editing migrations - Creating or editing migrations
- Running `nomarchy-dev-*` commands - Modifying Nomarchy's own source tree
## Example Requests ## Example Requests

View File

@@ -1,11 +0,0 @@
# If not running interactively, don't do anything (leave this at the top of this file)
[[ $- != *i* ]] && return
# All the default Nomarchy aliases and functions
# (don't mess with these directly, just overwrite them here!)
source ~/.config/nomarchy/default/bash/rc
# Add your own exports, aliases, and functions here.
#
# Make an alias for invoking commands you use constantly
# alias p='python'

View File

@@ -32,8 +32,7 @@ end
function GetEntries() function GetEntries()
local entries = {} local entries = {}
local user_theme_dir = os.getenv("HOME") .. "/.config/nomarchy/themes" local user_theme_dir = os.getenv("HOME") .. "/.config/nomarchy/themes"
local nomarchy_path = os.getenv("NOMARCHY_PATH") or "/etc/nixos" local default_theme_dir = os.getenv("HOME") .. "/.local/share/nomarchy/themes"
local default_theme_dir = nomarchy_path .. "/themes/palettes"
local seen_themes = {} local seen_themes = {}

View File

@@ -51,5 +51,17 @@ let
in in
{ {
xdg.configFile = configMappings; xdg.configFile = configMappings // {
# mako reads ~/.config/mako/config by default. The themed Nomarchy
# config (urgency rules, app filters, button handlers) lives under
# nomarchy/default/mako/core.ini for organizational reasons, so wire
# it explicitly here. Without this, mako silently falls back to its
# built-in defaults and every Nomarchy notification customization is
# inert.
"mako/config".source = lib.mkDefault ./config/nomarchy/default/mako/core.ini;
};
home.file.".XCompose" = lib.mkDefault {
source = ./config/nomarchy/default/xcompose;
};
} }

View File

@@ -5,7 +5,6 @@
./options.nix ./options.nix
./state.nix ./state.nix
./overrides.nix ./overrides.nix
./behavior.nix
./fonts.nix ./fonts.nix
./configs.nix ./configs.nix
./security.nix ./security.nix

View File

@@ -1,47 +1,47 @@
{ lib, pkgs, ... }: { lib, pkgs, ... }:
let
# Defaults live in lib/state-schema.nix so they can't drift between this
# file, core/system/options.nix, and core/home/state.nix's `or` fallbacks.
schema = import ../../lib/state-schema.nix { inherit lib; };
in
{ {
options.nomarchy = { options.nomarchy = {
toggles = { toggles = {
suspend = lib.mkOption { suspend = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = true; default = schema.home.suspend;
description = "Whether to show suspend in system menu."; description = "Whether to show suspend in system menu.";
}; };
screensaver = lib.mkOption { screensaver = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = true; default = schema.home.screensaver;
description = "Whether the screensaver is enabled."; description = "Whether the screensaver is enabled.";
}; };
idle = lib.mkOption { idle = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = true; default = schema.home.idle;
description = "Whether the idle lock is enabled."; description = "Whether the idle lock is enabled.";
}; };
nightlight = lib.mkOption { nightlight = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = false; default = schema.home.nightlight;
description = "Whether the nightlight is enabled."; description = "Whether the nightlight is enabled.";
}; };
waybar = lib.mkOption { waybar = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = true; default = schema.home.waybar;
description = "Whether the top bar is enabled."; description = "Whether the top bar is enabled.";
}; };
skipVsCodeTheme = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to skip theme changes in VSCode.";
};
}; };
nightlightTemperature = lib.mkOption { nightlightTemperature = lib.mkOption {
type = lib.types.int; type = lib.types.int;
default = 4000; default = schema.home.nightlightTemperature;
description = "Temperature for the nightlight."; description = "Temperature for the nightlight.";
}; };
theme = lib.mkOption { theme = lib.mkOption {
type = lib.types.str; type = lib.types.str;
default = "summer-night"; default = schema.home.theme;
description = "System theme name."; description = "System theme name.";
}; };
formFactor = lib.mkOption { formFactor = lib.mkOption {
@@ -58,35 +58,35 @@
}; };
wallpaper = lib.mkOption { wallpaper = lib.mkOption {
type = lib.types.str; type = lib.types.str;
default = ""; default = schema.home.wallpaper;
description = "System wallpaper path."; description = "System wallpaper path.";
}; };
panelPosition = lib.mkOption { panelPosition = lib.mkOption {
type = lib.types.enum [ "top" "bottom" ]; type = lib.types.enum [ "top" "bottom" ];
default = "top"; default = schema.home.panelPosition;
description = "Waybar panel position."; description = "Waybar panel position.";
}; };
hyprland = { hyprland = {
gaps_in = lib.mkOption { gaps_in = lib.mkOption {
type = lib.types.int; type = lib.types.int;
default = 5; default = schema.home.hyprland.gaps_in;
description = "Inner gaps for Hyprland."; description = "Inner gaps for Hyprland.";
}; };
gaps_out = lib.mkOption { gaps_out = lib.mkOption {
type = lib.types.int; type = lib.types.int;
default = 10; default = schema.home.hyprland.gaps_out;
description = "Outer gaps for Hyprland."; description = "Outer gaps for Hyprland.";
}; };
border_size = lib.mkOption { border_size = lib.mkOption {
type = lib.types.int; type = lib.types.int;
default = 2; default = schema.home.hyprland.border_size;
description = "Border size for Hyprland."; description = "Border size for Hyprland.";
}; };
}; };
fonts = { fonts = {
monospace = lib.mkOption { monospace = lib.mkOption {
type = lib.types.str; type = lib.types.str;
default = "JetBrainsMono Nerd Font"; default = schema.home.font;
description = "System monospace font."; description = "System monospace font.";
}; };
}; };

View File

@@ -1,97 +1,34 @@
{ config, lib, ... }: { config, lib, ... }:
# File-based override system for Nomarchy # File-based override system for Nomarchy.
# #
# Users can place config files in ~/.config/nomarchy/overrides/ to completely # STATUS: option surface only — the actual override mechanism is NOT yet
# replace upstream defaults. Override priority (highest to lowest): # implemented. The options are kept so configs that already set
# 1. User Nix options # `nomarchy.overrides.enable = …;` continue to evaluate; setting them has
# 2. User file overrides (~/.config/nomarchy/overrides/) # no effect today. Tracked in docs/ROADMAP.md.
# 3. Upstream defaults
# #
# Supported override paths: # When implemented, this module should substitute sources in
# - hypr/ - Hyprland configs (bindings.conf, input.conf, etc.) # `xdg.configFile.<path>.source` based on the presence of matching files
# - waybar/ - Waybar config and style # under ~/.config/nomarchy/overrides/.
# - alacritty/ - Alacritty terminal config
# - walker/ - Walker launcher config
# - kitty/ - Kitty terminal config
# - btop/ - Btop resource monitor config
# - apps/ - Other application configs
let
overridesDir = "${config.home.homeDirectory}/.config/nomarchy/overrides";
# Helper to get override from options
getOverrideOrDefault = { path, default }:
config.nomarchy.overrides.paths.${path} or default;
in
{ {
options.nomarchy.overrides = { options.nomarchy.overrides = {
enable = lib.mkOption { enable = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = true; default = true;
description = "Whether to enable file-based override loading from ~/.config/nomarchy/overrides/"; description = ''
Reserved for the future file-based override loader. Currently a
no-op setting this has no effect. See docs/ROADMAP.md.
'';
}; };
paths = lib.mkOption { paths = lib.mkOption {
type = lib.types.attrsOf lib.types.path; type = lib.types.attrsOf lib.types.path;
default = {}; default = {};
description = "Override paths discovered at build time. Populated by the override system."; description = ''
Reserved for the future file-based override loader. Currently
unused.
'';
}; };
}; };
config = lib.mkIf config.nomarchy.overrides.enable {
# Create the overrides directory structure if it doesn't exist
home.activation.createOverridesDir = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
mkdir -p "${overridesDir}"
mkdir -p "${overridesDir}/hypr"
mkdir -p "${overridesDir}/waybar"
mkdir -p "${overridesDir}/apps"
'';
# Document the override system
xdg.configFile."nomarchy/overrides/README.md".text = lib.mkDefault ''
# Nomarchy Configuration Overrides
Place files in this directory to override upstream Nomarchy defaults.
## Directory Structure
```
overrides/
hypr/
bindings.conf # Keybindings
input.conf # Input settings
monitors.conf # Monitor layout
rules.conf # Window rules
autostart.conf # Startup apps
waybar/
config.jsonc # Waybar layout
style.css # Waybar styling
apps/
alacritty.toml # Terminal behavior
...
README.md # This file
```
## Override Priority
1. **Nix Options** (highest) - Set in your flake/config
2. **File Overrides** - Files in this directory
3. **Upstream Defaults** (lowest) - Nomarchy defaults
## Usage
1. Copy the file you want to customize from the upstream config
2. Place it in the appropriate directory here
3. Edit to your preferences
4. Run `nixos-rebuild switch` or `home-manager switch`
## Tips
- For keybindings, copy `~/.config/hypr/bindings.conf` to `overrides/hypr/`
- For Waybar styling, copy `~/.config/waybar/style.css` to `overrides/waybar/`
- Changes here persist across Nomarchy updates
'';
};
} }

View File

@@ -2,42 +2,51 @@
let let
nomarchyLib = import ../../lib { inherit lib; }; nomarchyLib = import ../../lib { inherit lib; };
# Single source of truth for default values when state.json is missing
# a key. Both core/system/options.nix and core/home/options.nix read
# from this same file — changing a default in one place updates
# everywhere. (Was: each consumer hardcoded its own `or X` literal,
# which is how the summer-night/nord split lived for so long.)
schema = import ../../lib/state-schema.nix { inherit lib; };
assetsPath = ../../themes/palettes; assetsPath = ../../themes/palettes;
# Read unified state from ~/.config/nomarchy/state.json # Read unified state from ~/.config/nomarchy/state.json
togglesState = nomarchyLib.readHomeState config.home.homeDirectory; togglesState = nomarchyLib.readHomeState config.home.homeDirectory;
in in
{ {
# Every assignment uses lib.mkDefault so a downstream /etc/nixos/home.nix
# can override the state.json-derived value. Without mkDefault, every
# option here would resolve at default priority and conflict on
# assignment from the user's config.
config = { config = {
nomarchy = { nomarchy = {
toggles = { toggles = {
suspend = togglesState.suspend or true; suspend = lib.mkDefault (togglesState.suspend or schema.home.suspend);
screensaver = togglesState.screensaver or true; screensaver = lib.mkDefault (togglesState.screensaver or schema.home.screensaver);
idle = togglesState.idle or true; idle = lib.mkDefault (togglesState.idle or schema.home.idle);
nightlight = togglesState.nightlight or false; nightlight = lib.mkDefault (togglesState.nightlight or schema.home.nightlight);
waybar = togglesState.waybar or true; waybar = lib.mkDefault (togglesState.waybar or schema.home.waybar);
skipVsCodeTheme = togglesState.skipVsCodeTheme or false;
}; };
nightlightTemperature = togglesState.nightlightTemperature or 4000; nightlightTemperature = lib.mkDefault (togglesState.nightlightTemperature or schema.home.nightlightTemperature);
theme = togglesState.theme or "summer-night"; theme = lib.mkDefault (togglesState.theme or schema.home.theme);
wallpaper = togglesState.wallpaper or ""; wallpaper = lib.mkDefault (togglesState.wallpaper or schema.home.wallpaper);
panelPosition = togglesState.panelPosition or "top"; panelPosition = lib.mkDefault (togglesState.panelPosition or schema.home.panelPosition);
hyprland = { hyprland = {
gaps_in = togglesState.hyprland.gaps_in or 5; gaps_in = lib.mkDefault (togglesState.hyprland.gaps_in or schema.home.hyprland.gaps_in);
gaps_out = togglesState.hyprland.gaps_out or 10; gaps_out = lib.mkDefault (togglesState.hyprland.gaps_out or schema.home.hyprland.gaps_out);
border_size = togglesState.hyprland.border_size or 2; border_size = lib.mkDefault (togglesState.hyprland.border_size or schema.home.hyprland.border_size);
}; };
fonts.monospace = togglesState.font or "JetBrainsMono Nerd Font"; fonts.monospace = lib.mkDefault (togglesState.font or schema.home.font);
# Derived properties from the theme directory # Derived properties from the theme directory
isLightMode = nomarchyLib.isThemeLightMode { isLightMode = lib.mkDefault (nomarchyLib.isThemeLightMode {
themeName = togglesState.theme or "summer-night"; themeName = togglesState.theme or schema.home.theme;
inherit assetsPath; inherit assetsPath;
}; });
iconsTheme = nomarchyLib.getIconsTheme { iconsTheme = lib.mkDefault (nomarchyLib.getIconsTheme {
themeName = togglesState.theme or "summer-night"; themeName = togglesState.theme or schema.home.theme;
inherit assetsPath; inherit assetsPath;
}; });
}; };
}; };
} }

View File

@@ -5,7 +5,7 @@ let
pname = "nomarchy-font"; pname = "nomarchy-font";
version = "1.0"; version = "1.0";
# Point directly to the font file # Point directly to the font file
src = ./../home/config/Nomarchy.ttf; src = ./../branding/Nomarchy.ttf;
# No archive to unpack # No archive to unpack
unpackPhase = "true"; unpackPhase = "true";
installPhase = '' installPhase = ''

View File

@@ -70,8 +70,47 @@ in
}) })
(mkIf config.nomarchy.system.features.hybridGPU { (mkIf config.nomarchy.system.features.hybridGPU {
# supergfxd manages mode switching (Integrated / Hybrid / Vfio /
# AsusEgpu). It blacklists/unblacklists the nvidia kernel module via
# /etc/modprobe.d/ depending on the active mode. ExecStartPre sleep
# gives udev time to settle so the daemon doesn't see a half-attached
# GPU on cold boot.
services.supergfxd.enable = true; services.supergfxd.enable = true;
systemd.services.supergfxd.serviceConfig.ExecStartPre = "-${pkgs.coreutils}/bin/sleep 5"; systemd.services.supergfxd.serviceConfig.ExecStartPre = "-${pkgs.coreutils}/bin/sleep 5";
# Load the NVIDIA driver so the dGPU has something to drive. Without
# these, supergfxd switches modes successfully but the X/Wayland
# stack has no NVIDIA driver loaded — render-offload silently no-ops
# and Hyprland renders everything on the iGPU regardless of mode.
# mkDefault throughout so downstream system.nix can override
# (pin to a beta driver, flip to the open kernel module, etc.).
services.xserver.videoDrivers = lib.mkDefault [ "nvidia" ];
hardware.graphics.enable = lib.mkDefault true;
hardware.graphics.enable32Bit = lib.mkDefault true;
hardware.nvidia = {
modesetting.enable = lib.mkDefault true;
powerManagement.enable = lib.mkDefault true;
open = lib.mkDefault false;
package = lib.mkDefault config.boot.kernelPackages.nvidiaPackages.stable;
};
# Required for Wayland compositors (Hyprland) to render via NVIDIA.
boot.kernelParams = [ "nvidia-drm.modeset=1" ];
# PRIME render-offload (the part that lets `nvidia-offload <cmd>`
# actually use the dGPU) needs bus IDs, which are per-machine.
# We deliberately don't enable `hardware.nvidia.prime.offload.enable`
# here — without the correct intelBusId / nvidiaBusId the nvidia
# kernel module panics on load. The user adds this to their own
# /etc/nixos/system.nix after running `lspci -D`:
#
# hardware.nvidia.prime = {
# offload.enable = true;
# offload.enableOffloadCmd = true;
# intelBusId = "PCI:0:2:0"; # or amdgpuBusId for AMD iGPU
# nvidiaBusId = "PCI:1:0:0";
# };
#
# See docs/OPTIONS.md for the full recipe.
}) })
]; ];
} }

View File

@@ -15,9 +15,9 @@ in
settings.Login = { settings.Login = {
HandleLidSwitch = lib.mkDefault "suspend-then-hibernate"; HandleLidSwitch = lib.mkDefault "suspend-then-hibernate";
HandleLidSwitchExternalPower = lib.mkDefault "suspend"; HandleLidSwitchExternalPower = lib.mkDefault "suspend";
HandlePowerKey = "hibernate"; HandlePowerKey = lib.mkDefault "hibernate";
IdleAction = "suspend-then-hibernate"; IdleAction = lib.mkDefault "suspend-then-hibernate";
IdleActionSec = toString (cfg.idleMinutes * 60); IdleActionSec = lib.mkDefault (toString (cfg.idleMinutes * 60));
}; };
}; };
}; };

View File

@@ -10,13 +10,39 @@ in
options.nomarchy.system.impermanence = { options.nomarchy.system.impermanence = {
enable = lib.mkEnableOption "Erase Your Darlings (Impermanence) root wipe on boot"; enable = lib.mkEnableOption "Erase Your Darlings (Impermanence) root wipe on boot";
# The disko layout names the main LUKS mapping `crypted` on single-disk
# installs and `crypted_main` on multi-disk installs (see
# installer/disko-config.nix: `mainLuksName`). The rollback hook must
# mount the right device, otherwise initrd fails on every boot and the
# @ → root-blank snapshot is never restored.
mainLuksName = lib.mkOption {
type = lib.types.str;
default = "crypted";
description = ''
Name of the /dev/mapper entry holding the BTRFS root. Set to
"crypted_main" on multi-disk installs to match the disko layout.
'';
};
user = lib.mkOption {
type = lib.types.str;
default = "nomarchy";
description = ''
Primary user whose home subset (.ssh, .gnupg, keyrings, common
directories) survives the rootfs wipe. Must match the user
created via `users.users.<name>` otherwise the persistence
block is silently inert and the user's home directory is wiped
on every boot. The installer writes this for you.
'';
};
}; };
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
# 1. The Rollback Script: Runs in initrd before filesystems are mounted # 1. The Rollback Script: Runs in initrd before filesystems are mounted
boot.initrd.postDeviceCommands = lib.mkAfter '' boot.initrd.postDeviceCommands = lib.mkAfter ''
mkdir -p /btrfs_tmp mkdir -p /btrfs_tmp
mount -o subvol=/ /dev/mapper/crypted /btrfs_tmp mount -o subvol=/ /dev/mapper/${cfg.mainLuksName} /btrfs_tmp
if [[ -e /btrfs_tmp/@ ]]; then if [[ -e /btrfs_tmp/@ ]]; then
mkdir -p /btrfs_tmp/old_roots mkdir -p /btrfs_tmp/old_roots
@@ -58,7 +84,7 @@ in
"/etc/machine-id" "/etc/machine-id"
"/etc/supergfxd.conf" "/etc/supergfxd.conf"
]; ];
users.nomarchy = { users.${cfg.user} = {
directories = [ directories = [
".ssh" ".ssh"
".gnupg" ".gnupg"

View File

@@ -1,27 +1,32 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
let
# Defaults live in lib/state-schema.nix so they can't drift between this
# file, core/home/options.nix, and core/home/state.nix's `or` fallbacks.
schema = import ../../lib/state-schema.nix { inherit lib; };
in
{ {
options.nomarchy.system = { options.nomarchy.system = {
dns = lib.mkOption { dns = lib.mkOption {
type = lib.types.enum [ "Cloudflare" "Google" "DHCP" "Custom" ]; type = lib.types.enum [ "Cloudflare" "Google" "DHCP" "Custom" ];
default = "DHCP"; default = schema.system.dns;
description = "Selected DNS provider."; description = "Selected DNS provider.";
}; };
customDns = lib.mkOption { customDns = lib.mkOption {
type = lib.types.listOf lib.types.str; type = lib.types.listOf lib.types.str;
default = []; default = schema.system.customDns;
description = "List of custom DNS servers."; description = "List of custom DNS servers.";
}; };
wifi = { wifi = {
powersave = lib.mkOption { powersave = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = true; default = schema.system.wifi.powersave;
description = "Whether to enable wifi power saving."; description = "Whether to enable wifi power saving.";
}; };
}; };
timezone = lib.mkOption { timezone = lib.mkOption {
type = lib.types.str; type = lib.types.str;
default = "UTC"; default = schema.system.timezone;
description = "System timezone."; description = "System timezone.";
}; };
formFactor = lib.mkOption { formFactor = lib.mkOption {
@@ -39,23 +44,23 @@
features = { features = {
fingerprint = lib.mkOption { fingerprint = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = false; default = schema.system.features.fingerprint;
description = "Whether to enable fingerprint support."; description = "Whether to enable fingerprint support.";
}; };
fido2 = lib.mkOption { fido2 = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = false; default = schema.system.features.fido2;
description = "Whether to enable FIDO2 support."; description = "Whether to enable FIDO2 support.";
}; };
hybridGPU = lib.mkOption { hybridGPU = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = false; default = schema.system.features.hybridGPU;
description = "Whether to enable hybrid GPU support (supergfxd)."; description = "Whether to enable hybrid GPU support (supergfxd).";
}; };
}; };
theme = lib.mkOption { theme = lib.mkOption {
type = lib.types.str; type = lib.types.str;
default = "summer-night"; default = schema.system.theme;
description = "Selected system theme."; description = "Selected system theme.";
}; };

View File

@@ -55,8 +55,7 @@ pkgs.stdenv.mkDerivation {
for file in $out/bin/*; do for file in $out/bin/*; do
if [ -f "$file" ]; then if [ -f "$file" ]; then
wrapProgram "$file" \ wrapProgram "$file" \
--prefix PATH : "$deps" \ --prefix PATH : "$deps"
--set NOMARCHY_PATH "/etc/nixos"
fi fi
done done
''; '';

View File

@@ -1,14 +0,0 @@
#!/bin/bash
set -e
# Returns true if a battery is present on the system.
# Used by the battery monitor and other battery-related checks.
for bat in /sys/class/power_supply/BAT*; do
[[ -r $bat/present ]] &&
[[ $(cat $bat/present) == "1" ]] &&
[[ $(cat $bat/type) == "Battery" ]] &&
exit 0
done
exit 1

View File

@@ -1,11 +0,0 @@
#!/bin/bash
set -e
# Prompt for sudo once and keep the credential alive in the background.
# Source this script so the trap applies to the calling shell:
# source nomarchy-sudo-keepalive
sudo -v
while true; do sudo -n true; sleep 60; done 2>/dev/null &
SUDO_KEEPALIVE_PID=$!
trap "kill $SUDO_KEEPALIVE_PID 2>/dev/null" EXIT

View File

@@ -2,19 +2,28 @@
let let
nomarchyLib = import ../../lib { inherit lib; }; nomarchyLib = import ../../lib { inherit lib; };
# Same canonical schema as core/home/state.nix and the options.nix
# files — keeps every state default in one place.
schema = import ../../lib/state-schema.nix { inherit lib; };
systemState = nomarchyLib.readSystemState; systemState = nomarchyLib.readSystemState;
in in
{ {
# Every assignment is lib.mkDefault so a downstream /etc/nixos/system.nix
# can still set e.g. `nomarchy.system.features.hybridGPU = true;`
# without colliding with the state.json-derived value. Without
# mkDefault, every state.json-driven option was unoverridable from
# Nix — flipping hybridGPU required jq'ing the state file rather
# than declaring it in your config.
config.nomarchy.system = { config.nomarchy.system = {
dns = systemState.dns or "DHCP"; dns = lib.mkDefault (systemState.dns or schema.system.dns);
customDns = systemState.customDns or []; customDns = lib.mkDefault (systemState.customDns or schema.system.customDns);
wifi.powersave = systemState.wifi.powersave or true; wifi.powersave = lib.mkDefault (systemState.wifi.powersave or schema.system.wifi.powersave);
timezone = systemState.timezone or "UTC"; timezone = lib.mkDefault (systemState.timezone or schema.system.timezone);
features = { features = {
fingerprint = systemState.features.fingerprint or false; fingerprint = lib.mkDefault (systemState.features.fingerprint or schema.system.features.fingerprint);
fido2 = systemState.features.fido2 or false; fido2 = lib.mkDefault (systemState.features.fido2 or schema.system.features.fido2);
hybridGPU = systemState.features.hybridGPU or false; hybridGPU = lib.mkDefault (systemState.features.hybridGPU or schema.system.features.hybridGPU);
}; };
theme = systemState.theme or "nord"; theme = lib.mkDefault (systemState.theme or schema.system.theme);
}; };
} }

View File

@@ -41,8 +41,7 @@ core/ Foundational OS + user defaults. Don't put apps here.
scripts/ Low-level system scripts (battery, brightness, hardware). scripts/ Low-level system scripts (battery, brightness, hardware).
home/ Home Manager modules. home/ Home Manager modules.
options.nix Most home-side nomarchy.* options. options.nix Most home-side nomarchy.* options.
behavior.nix nomarchy.behavior.* (deploy-default-config toggles). overrides.nix nomarchy.overrides.* (reserved; currently no-op — see ROADMAP).
overrides.nix File-based overrides from ~/.config/nomarchy/overrides/.
config/ Plain dotfiles symlinked into ~/.config. config/ Plain dotfiles symlinked into ~/.config.
features/ Apps and desktop components. Add new apps here. features/ Apps and desktop components. Add new apps here.

View File

@@ -210,7 +210,7 @@ and it's whichever has higher Nix priority. Fix these explicitly:
| Graphics | `hardware.graphics.enable = true` (was `hardware.opengl`) | Probably already enabled — fine | | Graphics | `hardware.graphics.enable = true` (was `hardware.opengl`) | Probably already enabled — fine |
| User groups | needs `video render networkmanager` | Add to your `users.users.<user>.extraGroups` | | User groups | needs `video render networkmanager` | Add to your `users.users.<user>.extraGroups` |
| `/etc/os-release` | `ID=nomarchy`, `NAME=Nomarchy` | A few third-party scripts grep `ID=nixos` — adjust them or rely on `ID_LIKE` (TBD) | | `/etc/os-release` | `ID=nomarchy`, `NAME=Nomarchy` | A few third-party scripts grep `ID=nixos` — adjust them or rely on `ID_LIKE` (TBD) |
| autoLogin | `enable = true; user = "nomarchy";` (mkDefault) | Override with `services.displayManager.autoLogin.user = "<your user>"` or disable | | autoLogin | `enable = false; user = "nomarchy";` (mkDefault) | Off by default — opt in with `services.displayManager.autoLogin = { enable = true; user = "<your user>"; };` if you want it |
Impermanence is **off** unless you set `nomarchy.system.impermanence.enable = true`, Impermanence is **off** unless you set `nomarchy.system.impermanence.enable = true`,
and it requires a BTRFS layout with a `root-blank` snapshot. Don't enable it and it requires a BTRFS layout with a `root-blank` snapshot. Don't enable it

View File

@@ -53,7 +53,25 @@ Wired in `features/desktop/waybar/default.nix` (filters the battery widget out o
### `nomarchy.system.features.hybridGPU` ### `nomarchy.system.features.hybridGPU`
`bool`, default `false`. Enables `services.supergfxd.enable` for laptops with switchable GPUs. `bool`, default `false`. NVIDIA-hybrid laptop support. Wires:
- `services.supergfxd.enable` for runtime mode switching (`Integrated` / `Hybrid` / `Vfio` / `AsusEgpu`), driven by `nomarchy-toggle-hybrid-gpu`.
- The NVIDIA driver stack (`services.xserver.videoDrivers = ["nvidia"]`, `hardware.graphics.{enable,enable32Bit}`, `hardware.nvidia.{modesetting,powerManagement}.enable`, `boot.kernelParams = ["nvidia-drm.modeset=1"]`).
All driver knobs use `lib.mkDefault`, so a downstream `system.nix` can pin a beta driver or flip to the open kernel module without forking the module.
**You still have to add bus IDs** — they're per-machine and can't be derived from any flag. Find them with `lspci -D | grep -E 'VGA|3D'`, then in your `/etc/nixos/system.nix`:
```nix
hardware.nvidia.prime = {
offload.enable = true;
offload.enableOffloadCmd = true;
intelBusId = "PCI:0:2:0"; # or `amdgpuBusId` for AMD iGPU
nvidiaBusId = "PCI:1:0:0";
};
```
Without prime config, supergfxd still switches modes but render-offload via `nvidia-offload <cmd>` is unavailable.
### `nomarchy.system.snapper.enable` ### `nomarchy.system.snapper.enable`
@@ -135,10 +153,18 @@ Wired in `features/desktop/waybar/default.nix` (filters the battery widget out o
`bool`, default `false`. Intel IPU7 camera support (kernel modules + firmware). `bool`, default `false`. Intel IPU7 camera support (kernel modules + firmware).
### `impermanence.enable` ### `nomarchy.system.impermanence.enable`
`bool`, default `false`. Erase Your Darlings root wipe on boot. Defined in `core/system/impermanence.nix`. The installer writes the flag based on the impermanence prompt. `bool`, default `false`. Erase Your Darlings root wipe on boot. Defined in `core/system/impermanence.nix`. The installer writes the flag based on the impermanence prompt.
### `nomarchy.system.impermanence.mainLuksName`
`str`, default `"crypted"`. Name of the `/dev/mapper` entry holding the BTRFS root. The disko layout uses `"crypted"` on single-disk installs and `"crypted_main"` once multiple drives are selected — the installer writes the matching value automatically.
### `nomarchy.system.impermanence.user`
`str`, default `"nomarchy"`. Primary user whose home subset (`.ssh`, `.gnupg`, `.local/share/keyrings`, `Documents`, `Downloads`, `Pictures`, `Videos`, `Projects`) survives the rootfs wipe. Must match the user created via `users.users.<name>` — otherwise the persistence block is silently inert and the user's home directory is wiped on every boot. The installer writes this for you.
--- ---
## Home Manager options (`home.nix`) ## Home Manager options (`home.nix`)
@@ -179,10 +205,6 @@ Wired in `features/desktop/waybar/default.nix` (filters the battery widget out o
`bool`, default `true`. Whether the top bar is deployed at all. `bool`, default `true`. Whether the top bar is deployed at all.
### `nomarchy.toggles.skipVsCodeTheme`
`bool`, default `false`. Skip theme overrides in VSCode — useful if you manage VSCode themes yourself.
### `nomarchy.nightlightTemperature` ### `nomarchy.nightlightTemperature`
`int`, default `4000`. Nightlight color temperature (Kelvin). `int`, default `4000`. Nightlight color temperature (Kelvin).
@@ -205,11 +227,11 @@ Wired in `features/desktop/waybar/default.nix` (filters the battery widget out o
### `nomarchy.iconsTheme` ### `nomarchy.iconsTheme`
`str`, default `"Yaru-blue"`. GTK/Qt icon theme name. `str`, default derived from the active theme (falls back to `"Yaru-blue"`). GTK/Qt icon theme name. `core/home/state.nix` computes this from the theme's palette metadata; override to pin a specific icon theme regardless of palette.
### `nomarchy.isLightMode` ### `nomarchy.isLightMode`
`bool`, default `false`. Whether the active theme is a light theme. Affects nightlight defaults and a few app theme decisions. `bool`, default derived from the active theme. Whether the active theme is a light theme. `core/home/state.nix` computes this from the theme directory; affects nightlight defaults and a few app theme decisions. Override only if you need to force a specific value.
### `nomarchy.cursor.name` ### `nomarchy.cursor.name`
@@ -221,23 +243,7 @@ Wired in `features/desktop/waybar/default.nix` (filters the battery widget out o
### `nomarchy.configOverrides` ### `nomarchy.configOverrides`
`nullOr path`, default `null`. Path to a directory containing config overrides. See "Overrides" below. `nullOr path`, default `null`. Path to a replacement config directory. When set, the items listed in `core/home/configs.nix` (`fastfetch`, `fcitx5`, `fontconfig`, `git`, `imv`, `nautilus-python`, `nomarchy`, `nomarchy-skill`, `uwsm`, `wiremix`, plus the loose files) are read from `<this-path>/<name>` instead of the bundled defaults. Distinct from `nomarchy.overrides.*` below — `configOverrides` is a working bulk redirect; `overrides.*` is a reserved option surface (currently a no-op).
### `nomarchy.behavior.hyprland.bindings`
`bool`, default `true`. Deploy the default Hyprland keybindings. Set to `false` if you want to write `bindings.conf` from scratch.
### `nomarchy.behavior.hyprland.input`
`bool`, default `true`. Deploy default input settings (kb_layout, mouse accel, etc).
### `nomarchy.behavior.hyprland.windowRules`
`bool`, default `true`. Deploy default window rules.
### `nomarchy.behavior.hyprland.autostart`
`bool`, default `true`. Deploy the default `autostart.conf` (hypridle, mako, swayosd, nm-applet, etc).
### `nomarchy.apps.opencode.enable` ### `nomarchy.apps.opencode.enable`
@@ -253,19 +259,19 @@ Wired in `features/desktop/waybar/default.nix` (filters the battery widget out o
### `nomarchy.themeLoader.enable` ### `nomarchy.themeLoader.enable`
`bool`, default `true`. Auto-load theme-specific app configs (btop, waybar, mako, kitty, alacritty) from the active theme. Disable if you want to provide your own. `bool`, default `true`. Auto-load theme-specific app configs from the active theme's `apps/` directory. Disable if you want to provide your own.
### `nomarchy.themeLoader.apps.{btop,waybar,mako,kitty,alacritty}` ### `nomarchy.themeLoader.apps.btop`
`bool`, default `true` each. Per-app toggles for the theme loader — pick which apps follow the active theme. `bool`, default `true`. Deploy the active theme's `apps/btop.theme` to `~/.config/btop/themes/nomarchy.theme`. The only per-app toggle in this group — waybar themes inline from `colorScheme` in `features/desktop/waybar`; kitty and alacritty are themed by stylix targets (`themes/engine/stylix.nix`); mako has no theme integration yet.
### `nomarchy.overrides.enable` ### `nomarchy.overrides.enable`
`bool`, default `true`. Enable file-based overrides loaded from `~/.config/nomarchy/overrides/`. With this on, Nomarchy looks for matching files in that directory and substitutes them for the bundled defaults. `bool`, default `true`. **Reserved — currently a no-op.** Intended to gate a future file-based override loader (drop a file under `~/.config/nomarchy/overrides/`, have it substitute the bundled default for that path). The option exists so configs that set it don't fail to evaluate; setting it has no effect today. Use `nomarchy.configOverrides` for bulk redirection, or set `xdg.configFile.<path>.source` directly in your `home.nix` for per-file overrides — Nomarchy's defaults use `lib.mkDefault` and yield to higher-priority assignments. Tracked in `docs/ROADMAP.md`.
### `nomarchy.overrides.paths` ### `nomarchy.overrides.paths`
`attrsOf path`, default `{}`. Override paths discovered at build time. Populated by the override system — you don't normally set this directly. `attrsOf path`, default `{}`. **Reserved — currently unused.** Will be populated by the future override loader.
--- ---
@@ -297,15 +303,17 @@ Wired in `features/desktop/waybar/default.nix` (filters the battery widget out o
} }
``` ```
### Disable Nomarchy's default Hyprland keybindings to ship your own ### Ship your own Hyprland keybindings instead of Nomarchy's defaults
Nomarchy deploys its `bindings.conf` with `lib.mkDefault`, so a higher-priority assignment from your own `home.nix` wins:
```nix ```nix
{ {
nomarchy.behavior.hyprland.bindings = false; xdg.configFile."hypr/bindings.conf".source = ./my-bindings.conf;
} }
``` ```
Then put your own `bindings.conf` at `~/.config/nomarchy/overrides/hypr/bindings.conf` (with `nomarchy.overrides.enable = true;`, which is the default). The same pattern works for any file Nomarchy deploys via `xdg.configFile.<path>.source = lib.mkDefault …` — point at your own file and skip the default.
--- ---
@@ -315,7 +323,6 @@ Then put your own `bindings.conf` at `~/.config/nomarchy/overrides/hypr/bindings
- `core/system/hardware.nix``nomarchy.hardware.*` - `core/system/hardware.nix``nomarchy.hardware.*`
- `core/system/impermanence.nix``impermanence.enable` - `core/system/impermanence.nix``impermanence.enable`
- `core/home/options.nix` — most home-side `nomarchy.*` options - `core/home/options.nix` — most home-side `nomarchy.*` options
- `core/home/behavior.nix``nomarchy.behavior.*` - `core/home/overrides.nix``nomarchy.overrides.*` (reserved; currently no-op)
- `core/home/overrides.nix``nomarchy.overrides.*`
- `themes/engine/loader.nix``nomarchy.themeLoader.*` - `themes/engine/loader.nix``nomarchy.themeLoader.*`
- `features/apps/vscode.nix``nomarchy.vscode.*` - `features/apps/vscode.nix``nomarchy.vscode.*`

View File

@@ -19,13 +19,16 @@ Guardrails (apply when adding anything):
### Now (ready to pick up) ### Now (ready to pick up)
- (Empty for now) - **Full QA audit of shipped features.** Walk every feature/component end-to-end on a real install, fix what's small, log what's not. Runs as per-component PR sweeps — methodology in [Pillar 8](#8-pillar-qa-audit--features--components).
- **Installer: "What's installed?" summary on first boot.** Surface what the installer actually wrote (theme, font, profiles, drives, form factor) from `state.json` + `nomarchy-system-scripts` introspection so the user can verify before they start customising. Detail in [Pillar 4](#4-pillar-installer).
- **Installer: optional non-LUKS branch.** Let users explicitly opt out of FDE during install. Detail in [Pillar 4](#4-pillar-installer).
### Next (bigger lifts that build on Now) ### Next (bigger lifts that build on Now)
- **Accessibility — home-side companion.** Hyprland-side bits the system preset can't reach: slower `input.repeat_rate` / `repeat_delay` defaults, `SUPER+ALT+S` keybinding to launch Orca, and a high-contrast palette under `themes/palettes/`. Gated on a new `nomarchy.accessibility.enable` mirror of the system option. - **Accessibility — home-side companion.** Hyprland-side bits the system preset can't reach: slower `input.repeat_rate` / `repeat_delay` defaults, `SUPER+ALT+S` keybinding to launch Orca, and a high-contrast palette under `themes/palettes/`. Gated on a new `nomarchy.accessibility.enable` mirror of the system option.
- **Gaming — declarative flathub remote.** `services.flatpak.enable` doesn't ship a declarative remote API in nixpkgs. Either add the `flatpak-managed-install` overlay, write a one-shot systemd unit that runs `flatpak remote-add --if-not-exists flathub …`, or surface the manual step in `nomarchy-welcome`. - **Gaming — declarative flathub remote.** `services.flatpak.enable` doesn't ship a declarative remote API in nixpkgs. Either add the `flatpak-managed-install` overlay, write a one-shot systemd unit that runs `flatpak remote-add --if-not-exists flathub …`, or surface the manual step in `nomarchy-welcome`.
- **Plymouth theme variants per palette.** Currently one Plymouth theme; could template per-palette so the boot splash matches the active theme. - **Plymouth theme variants per palette.** Currently one Plymouth theme; could template per-palette so the boot splash matches the active theme.
- **Implement `nomarchy.overrides.*` file-based override loader.** The option surface is declared in `core/home/overrides.nix` but the loader doesn't exist — files dropped under `~/.config/nomarchy/overrides/` are ignored. Spec: when `overrides.enable = true`, for each `xdg.configFile.<path>` Nomarchy deploys with `lib.mkDefault`, check whether `~/.config/nomarchy/overrides/<path>` exists at build time; if so, use it as the source. Requires deciding whether discovery happens at activation time (cheap, but rebuild-required to pick up new files) or via a populated `paths` attrset (Nix-side, evaluated once per rebuild). Until shipped, document the per-file workaround (`xdg.configFile.<path>.source = ./mine`) in OPTIONS.md.
### Later (speculative or research-shaped) ### Later (speculative or research-shaped)
@@ -37,6 +40,12 @@ Guardrails (apply when adding anything):
- **Forgejo release pipeline.** `vYY.MM.x` tags matching the upstream NixOS channel; the pipeline pushes the three ISOs and an updated `flake.lock` snapshot. - **Forgejo release pipeline.** `vYY.MM.x` tags matching the upstream NixOS channel; the pipeline pushes the three ISOs and an updated `flake.lock` snapshot.
- **Optional `nomarchy-installer-vm`** rebuilt as a real flake app (not a one-off shell script) so users can install Nomarchy into a libvirt VM declaratively. - **Optional `nomarchy-installer-vm`** rebuilt as a real flake app (not a one-off shell script) so users can install Nomarchy into a libvirt VM declaratively.
- **Surface support module** via the relevant `nixos-hardware` profile + Surface kernel patches behind a `nomarchy.hardware.isSurface` toggle. - **Surface support module** via the relevant `nixos-hardware` profile + Surface kernel patches behind a `nomarchy.hardware.isSurface` toggle.
- **Consolidate palette imports in `flake.nix` via `nomarchyLib`.** `flake.nix:79-80` re-imports `./themes/palettes` and recomputes `themeNames` even though `lib/default.nix` already exports both. Two computations, same result today — drift risk tomorrow. Import `nomarchyLib = import ./lib { inherit lib; }` once and use `nomarchyLib.{palettes,themeNames}` to make `lib/default.nix` the single source of truth for the theme list.
- **Decide what to do about `features/apps/chromium/Default/Preferences`.** The module deploys a static 204-byte `Default/Preferences` (setting `extensions.theme.use_system = false`, `browser.theme.color_scheme = 2`) into chromium's mutable profile directory via Home Manager symlink. 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 real chromium theming work happens via managed policies in `core/system/browser.nix`. Probably the user-profile deployment should be removed; needs someone with chromium-internals knowledge to confirm before deletion.
- **`themes/templates/*.tpl` — decide what these are for.** Eleven mustache-style templates (`alacritty.toml.tpl`, `btop.theme.tpl`, `kitty.conf.tpl`, etc.) are deployed to `~/.local/share/nomarchy/templates/` via `themes/engine/files.nix`, but no script in the tree consumes them. Likely vestigial from a pre-stylix templating system. Either delete the directory + the `xdg.dataFile` deployment, or document them as user-reference assets and explain how to use them.
- **Move `programs.uwsm` Hyprland session out of `core/system/virtualization.nix`.** Session-manager config is wired in the virtualization module by historical accident — it's loaded unconditionally on every install and has nothing to do with libvirt/docker. Move to a dedicated `core/system/session.nix` (or fold into the Hyprland feature module) so the location matches the responsibility.
- **Route installer keymap into Hyprland's Wayland session.** `core/home/config/nomarchy/default/hypr/input.conf:3` hardcodes `kb_layout = us`. The installer writes `services.xserver.xkb.layout = "$KEYMAP_LAYOUT"` (and `console.keyMap`), but Hyprland reads its own input config on native Wayland, so a non-US user's chosen layout works in XWayland apps and the TTY console but not in native Wayland apps — surprising and inconsistent. Fix paths: (a) template `input.conf` from a new `nomarchy.keymap.{layout,variant}` home option that the installer writes alongside `formFactor`, or (b) propagate `XKB_DEFAULT_LAYOUT` via session env so Hyprland's fallback kicks in. Option (a) is the durable fix; needs the installer's heredoc to add the option write.
- **Make `nomarchy.toggles.waybar` a Nix-level gate, or document it as runtime-only.** Today the toggle is exported as `NOMARCHY_TOGGLE_WAYBAR` env, consumed only by `nomarchy-toggle-waybar` (which pkill/exec's at runtime). The Nix module always sets `programs.waybar.enable = lib.mkDefault true`, so waybar comes back on every rebuild/reboot regardless of the toggle. Inconsistent with `toggles.idle`, which correctly gates `services.hypridle.enable`. Either gate `programs.waybar.enable` on the toggle (persistent) or rename the option to make its session-only nature obvious.
## 3. Pillar: Script & menu audit ## 3. Pillar: Script & menu audit
@@ -73,31 +82,31 @@ Each PR description should reference the row(s) in `docs/SCRIPTS.md` it closes,
## 4. Pillar: Installer ## 4. Pillar: Installer
- Software-profile multi-select (Now). - "What's installed?" summary screen on boot of a freshly-installed system, sourced from `state.json` + `nomarchy-system-scripts` introspection (Now).
- Optional non-LUKS branch in the installer for users who explicitly opt out of FDE (Now).
- Richer disk metadata (Shipped). - Richer disk metadata (Shipped).
- Form-factor → laptop preset (Now, depends on Pillar 5).
- `disko-golden.nix` variants for software-RAID and BTRFS-pool-as-root (Shipped). - `disko-golden.nix` variants for software-RAID and BTRFS-pool-as-root (Shipped).
- Pre-flight resume polish (Next). - Pre-flight resume polish (Shipped).
- "What's installed?" summary screen on boot of a freshly-installed system, sourced from `state.json` + `nomarchy-system-scripts` introspection. - Software-profile multi-select (Shipped).
- Optional non-LUKS branch in the installer for users who explicitly opt out of FDE. - Form-factor → laptop preset (Shipped).
## 5. Pillar: Power, hardware, presets ## 5. Pillar: Power, hardware, presets
- Laptop preset (Next): TLP, upower, brightness, lid, hypridle tuning. - Auto-detect dGPU presence in `installer/hardware-db.sh` and pre-fill `hardware.nvidia.prime.{intel,nvidia}BusId` in the generated `system.nix` (driver stack itself is Shipped — see entry below).
- Desktop preset (Next): performance governor, no laptop UI (already filtered), ZFS hooks.
- Accessibility preset (Next).
- Gaming preset (Next).
- Vendor matchers in `installer/hardware-db.sh`: Steam Deck, Surface, ROG Ally, Snapdragon X laptops.
- Surface support behind `nomarchy.hardware.isSurface` (Later). - Surface support behind `nomarchy.hardware.isSurface` (Later).
- Auto-detect dGPU presence and offer `programs.envycontrol`-style switching for the hybrid case (already gated behind `nomarchy.system.features.hybridGPU`, but the wiring is minimal). - Laptop preset: TLP, upower, brightness, lid, hypridle tuning (Shipped).
- Desktop preset: performance governor, no laptop UI (already filtered), ZFS hooks (Shipped).
- Accessibility preset (Shipped).
- Gaming preset (Shipped).
- Vendor matchers in `installer/hardware-db.sh` (Shipped — ROG Ally added; Surface/Framework/Lenovo entries corrected; Steam Deck + Snapdragon X documented as nixos-hardware-unsupported. CI now lints DB references).
## 6. Pillar: Onboarding & docs ## 6. Pillar: Onboarding & docs
- `nomarchy-welcome` first-run wizard (Next). - `nomarchy-welcome` first-run wizard (Shipped).
- `docs/KEYBINDINGS.md` auto-generator (Shipped). - `docs/KEYBINDINGS.md` auto-generator (Shipped).
- `docs/TROUBLESHOOTING.md` (Next). - `docs/TROUBLESHOOTING.md` (Shipped).
- `docs/index.md` (or just enrich `README.md`) so `OPTIONS.md`, `STRUCTURE.md`, `MIGRATION.md`, `ROADMAP.md`, `SCRIPTS.md`, and `creating-themes.md` are all one click from the front page. - `docs/index.md` / README docs index (Shipped — `README.md` links every doc in `docs/`).
- `nomarchy-manual` — orphaned reference today; either implement as a curated `xdg-open` to the docs index, or delete. - `nomarchy-manual` — opens the local `~/.local/share/nomarchy/README.md` via `xdg-open` (Shipped).
## 7. Pillar: Test, CI, release ## 7. Pillar: Test, CI, release
@@ -109,6 +118,33 @@ Each PR description should reference the row(s) in `docs/SCRIPTS.md` it closes,
- `nixosTest` per palette: boots `default` in a VM, screenshots the SDDM splash and the Hyprland desktop, diffs vs golden. Failure surfaces as CI red. - `nixosTest` per palette: boots `default` in a VM, screenshots the SDDM splash and the Hyprland desktop, diffs vs golden. Failure surfaces as CI red.
- A small `bin/utils/nomarchy-bench-iso-build` that records ISO build time + size into a per-commit JSON so we notice regressions. - A small `bin/utils/nomarchy-bench-iso-build` that records ISO build time + size into a per-commit JSON so we notice regressions.
## 8. Pillar: QA audit — features & components
Nomarchy now spans an installer, ~159 `nomarchy-*` scripts, a Hyprland desktop stack (Hyprland + waybar + walker + nightlight + idle), curated apps, a 22-palette theme engine, and two ISO hosts. Pillar 3 audited script *existence*; this pillar audits feature *behavior*. The goal: walk every shipped feature end-to-end on a real install, fix every bug or surprise inline when small, and capture the rest as new roadmap rows.
Runs as **per-component sweeps**. One PR per component, branch `wave/qa-<component>`. Don't grow scope mid-PR — bugs that need a new option, refactor, or missing module become a new **Now**/**Next** row.
Components (each is one sweep):
1. **Installer**`installer/install.sh`, `installer/hardware-db.sh`, disko configs. Fresh install + `--resume` + `--dry-run`, on laptop and desktop, with FDE (non-LUKS branch is Later). Verify every generated file (`flake.nix`, `system.nix`, `home.nix`, `hardware-selection.nix`, `state.json`) is correct and idempotent.
2. **First-boot UX**`nomarchy-welcome`, generated `home.nix`, SDDM and Plymouth metadata, default theme/font/panel position. Re-run on a clean VM; note every prompt that confuses and every default that's wrong.
3. **Core system modules**`core/system/*` (laptop, desktop, accessibility, gaming, hybridGPU, impermanence, network, hardware, branding). For each: enable → rebuild → observe the claimed effect → disable → rebuild → observe it's gone. Cross-check against `docs/OPTIONS.md`.
4. **Core home modules**`core/home/*` (options, state, behavior, overrides, deployed config). Verify every home-side `nomarchy.*` option does what its description claims; confirm `~/.config/nomarchy/overrides/` actually overrides.
5. **Desktop stack** — Hyprland (keybindings, window rules, monitors, input), waybar (every module × both panel positions × both form factors), walker (every launcher mode), idle, nightlight, notifications (mako). Reconcile `docs/KEYBINDINGS.md` against runtime.
6. **Apps**`features/apps/*`. Each app: launches, themed via Stylix, configured as expected. Catches the "we package it but nobody configured it" class.
7. **Theme engine + palettes**`nomarchy-theme-set` across all 22 palettes, font and wallpaper switchers, light-mode toggle. Verify per-palette Stylix targets render correctly across SDDM, Plymouth, GTK, Qt, terminals, browsers, waybar, walker.
8. **Scripts (runtime behavior)** — Pillar 3 confirmed existence; this sweep runs every user-visible script (especially every `nomarchy-menu` entry) on current NixOS and confirms it actually does the thing.
9. **ISOs** — boot `nomarchy-installer` and `nomarchy-live`; verify the `nomarchy-test-live-iso` flow; check the installer ISO ships every tool `install.sh` calls (regression class: `hardware-db.sh` missing, already shipped).
10. **Lib + state schema**`lib/state-schema.nix`, color resolution, path helpers. Cross every codepath that produces `state.json` (installer, welcome wizard, hand-edit) against the schema; confirm bad inputs are rejected with a useful message.
Per-PR deliverable:
- PR body lists what was tested, what was broken, what was fixed inline, what was deferred (with the new roadmap row linked).
- Doc updates ride with the change per `docs/AGENT.md` §5.4.
- Don't bundle fixes across components — keep one component per branch so reviewers can spot-check end-to-end without context-switching.
Pillar is **done** when every component has a closed `wave/qa-<component>` PR and the roadmap captures every deferred finding.
## 9. Process notes ## 9. Process notes
- **Branch naming:** `wave/<pillar>-<short-slug>`. Examples: `wave/audit-pkg-scripts`, `wave/installer-disk-metadata`, `wave/laptop-preset`. - **Branch naming:** `wave/<pillar>-<short-slug>`. Examples: `wave/audit-pkg-scripts`, `wave/installer-disk-metadata`, `wave/laptop-preset`.
@@ -121,6 +157,18 @@ Each PR description should reference the row(s) in `docs/SCRIPTS.md` it closes,
(Move items here when they land — keep them brief, link the commit/PR.) (Move items here when they land — keep them brief, link the commit/PR.)
- _2026-05-18_ — Hardware DB correctness pass + ROG Ally support + CI lint. Audited every `nomarchy-hardware-db` entry against `inputs.nixos-hardware.nixosModules` and found **21 of 43 entries (49%) referenced modules that don't exist**`microsoft-surface-pro-8`, `lenovo-thinkpad-x1-carbon-gen11`, `framework-13-11th-gen-intel`, etc. were all eval-time failures waiting for a real user. Rewrote the DB to use only valid module names: Framework gens dropped the "13-" prefix in nixos-hardware (`framework-11th-gen-intel`, not `framework-13-11th-gen-intel`); ThinkPad X1 modules are `x1-Nth-gen`, not `x1-carbon-genN`; Surface Pro 6/7/8/10 all share `microsoft-surface-pro-intel`; Surface Book / Intel Surface Laptop have no module (rows dropped, generic detection still emits sensible `common-pc-laptop` + cpu/gpu). Added matchers for **ROG Ally** (RC71L / RC72LA / "ROG Ally" via `asus-ally-rc71l`). Documented Steam Deck and Snapdragon X as nixos-hardware-unsupported in a footer comment (Steam Deck → Jovian-NixOS; Snapdragon X → installer is x86_64 only). Added a CI step (`.forgejo/workflows/check.yml`) that fails on any DB entry whose module name isn't in `nixos-hardware.nixosModules` — closes this regression class.
- _2026-05-18_ — `nomarchy-manual` re-targeted at local docs. The script's `xdg-open` previously pointed at `https://learn.omacom.io/2/the-nomarchy-manual` — an upstream Omarchy URL that opened an unrelated page when users hit the menu's Help entry. Now opens `~/.local/share/nomarchy/README.md` (the local docs index per `SKILL.md`'s "Out of Scope" note), with a `notify-send` fallback if the source tree isn't synced.
- _2026-05-18_ — Docs hygiene: STRUCTURE.md "Root Directory" + Pillar 6 reality-check. `docs/STRUCTURE.md` listed three top-level files that don't exist (`GEMINI.md`, root-level `STRUCTURE.md`, `TODO.md`) — replaced with an accurate root listing plus a `docs/` sub-tree that names every doc. Pillar 6 in this file had `nomarchy-welcome`, `docs/TROUBLESHOOTING.md`, and the "docs index" bullet still marked Next despite all three shipping on 2026-04-26 — moved to `(Shipped)`. `nomarchy-manual` bullet's "orphaned reference today" claim was stale (the script is called from `nomarchy-menu` and `nomarchy-theme-install`); rewritten to reflect the real remaining issue — its hardcoded `xdg-open https://learn.omacom.io/2/the-nomarchy-manual` is an Omarchy URL.
- _2026-05-18_ — Installer state.json is now schema-driven. Replaced the heredoc in `installer/install.sh` that hardcoded the JSON literal (theme/dns/wifi/features/etc.) with a `nix eval` of `lib/state-schema.nix`'s `system` block, overlaid with the installer-chosen timezone. Closes the last source-of-truth split after the centralization batch — adding a new default in the schema now reaches the installer with no further plumbing. Output is identical modulo alphabetical key ordering (Nix's `builtins.toJSON` sorts keys; toggle scripts read/write via `jq` so it's invisible to them). Dry-run path unchanged (still bind-mounts a fake `/mnt` so the generator's absolute paths resolve correctly). `bash -n` + `shellcheck --severity=error` clean.
- _2026-05-18_ — Complete the hybrid-GPU wiring + fix unoverridable state-derived options. Two related fixes shipped together. **(1)** `nomarchy.system.features.hybridGPU = true` now wires the full NVIDIA driver stack (`services.xserver.videoDrivers = ["nvidia"]`, `hardware.graphics.{enable,enable32Bit}`, `hardware.nvidia.{modesetting,powerManagement}.enable`, `package = nvidiaPackages.stable`, `boot.kernelParams += "nvidia-drm.modeset=1"`) — was previously enabling only `supergfxd` mode-switching while leaving the system with no NVIDIA driver loaded, so mode switches silently no-op'd. All knobs use `lib.mkDefault` so a downstream `system.nix` can pin a beta driver, flip to the open kernel module, etc. Bus-ID prime config (per-machine) stays user-supplied — `docs/OPTIONS.md` has the full recipe. **(2)** Both `core/system/state.nix` and `core/home/state.nix` now use `lib.mkDefault` on every state.json-derived assignment, fixing a class of "I set X in my system.nix but it doesn't take effect" bugs (the state-derived value was at default priority and conflicted with the user's same-priority override). Side-effect cleanup: `core/system/state.nix` now also reads from `lib/state-schema.nix` like `core/home/state.nix` does, completing the schema-centralization started two batches ago. Verified `nix flake check` + an override test that flips hybridGPU via an overlay and confirms the entire driver stack engages.
- _2026-05-18_ — Pillar 4: pre-flight resume polish. Fixed four resume-flow gaps in `installer/install.sh`: (1) `--resume` with a missing state file now errors loudly with a tmpfs explanation instead of silently falling through to a fresh prompt cycle (the most common operator confusion was "rebooted, forgot tmpfs eats /tmp/, watched the installer start over without realising"); (2) on resume, the saved target drive is validated as a block device before any disk-phase step runs — catches the live-ISO USB-unplugged / non-deterministic /dev/sdX class of mid-install failures; (3) `save_state` now stamps an ISO-8601 timestamp and `load_state` shows a `(saved Xm ago)` banner plus a `Target: /dev/X → user @ host` summary line, so the user can `Ctrl-C` if they're resuming onto the wrong host before any destructive prompt fires; (4) `--help` now documents the tmpfs limitation. `shellcheck --severity=error` passes.
- _2026-05-18_ — Declarative-state defaults centralization. Made `lib/state-schema.nix` the single source of truth for every state-default that previously lived in three places (the schema itself, `core/system/options.nix` / `core/home/options.nix` `default = …` clauses, and `core/home/state.nix` `or …` fallbacks). Replaced ~25 hardcoded literals with `schema.<scope>.<key>` reads. Side-effect: fixed a lingering bug where `core/home/options.nix:theme` still defaulted to `"summer-night"` after the system-side was moved to `"nord"` — half the codebase's home option resolved to the wrong theme when state.json was missing/blank. `nix flake check --no-build` confirms zero semantic change for every other field. Doesn't touch the installer-written `state.json` (separate batch — needs schema → JSON generation).
- _2026-05-18_ — Pillar 7 first step: Forgejo Actions CI (eval + lint). New `.forgejo/workflows/check.yml` runs on every push to `main` and every PR: (1) `nix flake check --no-build` to catch eval regressions, (2) `bash -n` + `shellcheck --severity=error` over every `nomarchy-*` bash script (whole-tree, not just changed files — gates branches that bypass the pre-commit hook), (3) `docs/SCRIPTS.md` drift check (fails loudly if a script change didn't regenerate the audit doc). All three checks pass locally on the current tree. Activation requires enabling Actions on the Forgejo repo and registering a `forgejo-runner`; the workflow itself is dormant until then. ISO build job is intentionally deferred — needs a binary cache (Cachix/Attic) to be tractable.
- _2026-05-18_ — **Pillar 3 Phase B: complete.** Final batch (restart/sudo/theme/misc clusters) cleared the last 13 `unused?` rows. Deleted five truly dead scripts: `nomarchy-restart-{hyprctl,mako}` (theme switching calls `hyprctl reload`/`makoctl reload` directly now), `nomarchy-restart-tmux` (one-liner of marginal value), `nomarchy-battery-present` (battery monitor checks `/sys/class/power_supply/BAT*` inline), `nomarchy-sudo-keepalive` (intended-to-be-sourced building block with no users). Surfaced eight useful tools in `SKILL.md` so the audit catches them as `kept` and AI assistants can discover them: `nomarchy-restart-trackpad` (intel_quicki2c reload), `nomarchy-sudo-{passwordless-toggle,reset}`, `nomarchy-theme-{bg-install,refresh,remove}`, `nomarchy-refresh-fastfetch`, `nomarchy-windows-vm` (new Virtualization section). Final state: 159 scripts, all `kept`, `unused?` = 0, missing references = 0.
- _2026-05-18_ — Pillar 3 Phase B: webapp/tui/voxtype install-remove pair triage. Deleted two dead webapp URI handlers (`nomarchy-webapp-handler-hey`, `nomarchy-webapp-handler-zoom`) — no `.desktop` MimeType registration anywhere routed `mailto:`/`zoom:` URIs to them, so the handlers could never fire. Surfaced six useful CLI tools in `SKILL.md` "Common Tasks" so they're discoverable by AI assistants and tagged `kept` by the audit: `nomarchy-webapp-{remove,remove-all}`, `nomarchy-tui-{remove,remove-all}`, `nomarchy-voxtype-{install,remove}`. Script count 166 → 164; `unused?` 21 → 13.
- _2026-05-18_ — Pillar 3 Phase B: dead-code sweep (NixOS-irrelevant Omarchy ports). Deleted five scripts that duplicated NixOS-native facilities or referenced infrastructure Nomarchy doesn't ship: `nomarchy-rollback` (boot-menu generations + `nixos-rebuild rollback` already cover this), `nomarchy-snapshot` (used `snapper`; impermanence and BTRFS subvolumes are the Nomarchy answer), `nomarchy-migrate-state` (one-shot pre-unification migration, no current callers), `nomarchy-config-direct-boot` (added an EFI entry for a UKI we never build), and `nomarchy-npx-install` (Arch idiom — `nix-shell -p nodejs` is the NixOS path). Kept `nomarchy-build-iso` and `nomarchy-build-live-iso` and surfaced them in README §2 so the audit tags them `kept`. Script count 171 → 166.
- _2026-05-18_ — Pillar 3 Phase B: missing-references triage. (1) Wrote `themes/engine/scripts/nomarchy-theme-next` so `SKILL.md`'s documented "cycle to next theme" command resolves; (2) scrubbed three stale `nomarchy-dev-*` references from `core/home/config/nomarchy-skill/SKILL.md`; (3) added a line-context filter to both `nomarchy-docs-scripts` generators that drops `nomarchy-*` tokens appearing in Nix `pname`/derivation idents, `/tmp/` & `/etc/sudoers.d/` paths, `nixosConfigurations.*` / `packages.*` flake outputs, `mktemp -t` prefixes, systemd unit vars, `./result/bin/run-` binaries, and `docker` container references; (4) 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. `docs/SCRIPTS.md` "Missing references" section is now empty (was 15).
- _2026-05-04_ — Pillar 8: Distro Branding. (1) Scrubbed remaining "Omarchy" and "Spirit of Omarchy" references from README, scripts, and welcome wizard; (2) Updated `nomarchy-welcome` banner and `nomarchy-version` codename ("Sovereign"); (3) Verified existing `core/system/branding.nix` handles OS-release and bootloader labels; (4) Confirmed SDDM and Plymouth metadata are already Nomarchy-branded. - _2026-05-04_ — Pillar 8: Distro Branding. (1) Scrubbed remaining "Omarchy" and "Spirit of Omarchy" references from README, scripts, and welcome wizard; (2) Updated `nomarchy-welcome` banner and `nomarchy-version` codename ("Sovereign"); (3) Verified existing `core/system/branding.nix` handles OS-release and bootloader labels; (4) Confirmed SDDM and Plymouth metadata are already Nomarchy-branded.
- _2026-05-04_ — Thorough Out-of-the-Box QA Audit. (1) Restored automatic wallpaper switching by removing image filters from deployed themes; (2) Fixed broken "Style" menu entries by creating missing `about.txt` and `screensaver.txt` branding files; (3) Cleaned up conflicting keybindings by removing deprecated `tiling.conf` and updating the doc generator; (4) Removed legacy Nord theme hack from `nomarchy-theme-set`; (5) Fixed JSON parse error in `summer-day` waybar theme. - _2026-05-04_ — Thorough Out-of-the-Box QA Audit. (1) Restored automatic wallpaper switching by removing image filters from deployed themes; (2) Fixed broken "Style" menu entries by creating missing `about.txt` and `screensaver.txt` branding files; (3) Cleaned up conflicting keybindings by removing deprecated `tiling.conf` and updating the doc generator; (4) Removed legacy Nord theme hack from `nomarchy-theme-set`; (5) Fixed JSON parse error in `summer-day` waybar theme.
- _2026-05-03_ — Fixed multi-disk LUKS/BTRFS boot hang. (1) Moved temporary LUKS keyfile to `/tmp/` so Disko correctly omits it from the runtime configuration; (2) Injected `x-systemd.requires` and `x-systemd.device-timeout=0` into BTRFS mount options to ensure all LUKS drives are decrypted before mounting. - _2026-05-03_ — Fixed multi-disk LUKS/BTRFS boot hang. (1) Moved temporary LUKS keyfile to `/tmp/` so Disko correctly omits it from the runtime configuration; (2) Injected `x-systemd.requires` and `x-systemd.device-timeout=0` into BTRFS mount options to ensure all LUKS drives are decrypted before mounting.

View File

@@ -24,22 +24,21 @@ Phase B (per-batch PRs) refines those into `port-from-omarchy`,
- `delete-dead` — Phase B verdict: remove and update callers. - `delete-dead` — Phase B verdict: remove and update callers.
- `stub-with-notify` — Phase B verdict: temporary `notify-send` stub. - `stub-with-notify` — Phase B verdict: temporary `notify-send` stub.
## Scripts (170) ## Scripts (159)
| Script | Location | Callers | Status | Notes | | Script | Location | Callers | Status | Notes |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| `nomarchy-backup` | `features/scripts/utils` | features/scripts/utils/nomarchy-sync | `kept` | | | `nomarchy-backup` | `features/scripts/utils` | features/scripts/utils/nomarchy-sync | `kept` | |
| `nomarchy-battery-capacity` | `core/system/scripts` | core/system/scripts/nomarchy-battery-status | `kept` | | | `nomarchy-battery-capacity` | `core/system/scripts` | core/system/scripts/nomarchy-battery-status | `kept` | |
| `nomarchy-battery-monitor` | `core/system/scripts` | features/scripts/battery-monitor.nix | `kept` | | | `nomarchy-battery-monitor` | `core/system/scripts` | features/scripts/battery-monitor.nix | `kept` | |
| `nomarchy-battery-present` | `core/system/scripts` | — | `unused?` | |
| `nomarchy-battery-remaining` | `core/system/scripts` | core/system/scripts/nomarchy-battery-monitor,core/system/scripts/nomarchy-battery-status | `kept` | | | `nomarchy-battery-remaining` | `core/system/scripts` | core/system/scripts/nomarchy-battery-monitor,core/system/scripts/nomarchy-battery-status | `kept` | |
| `nomarchy-battery-remaining-time` | `core/system/scripts` | core/system/scripts/nomarchy-battery-status | `kept` | | | `nomarchy-battery-remaining-time` | `core/system/scripts` | core/system/scripts/nomarchy-battery-status | `kept` | |
| `nomarchy-battery-status` | `core/system/scripts` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,core/system/scripts/nomarchy-battery-capacity, +1 more | `kept` | | | `nomarchy-battery-status` | `core/system/scripts` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,core/system/scripts/nomarchy-battery-capacity, +1 more | `kept` | |
| `nomarchy-brightness-display` | `core/system/scripts` | core/home/config/nomarchy/default/hypr/bindings/media.conf,core/home/config/nomarchy/default/hypr/bindings/utilities.conf | `kept` | | | `nomarchy-brightness-display` | `core/system/scripts` | core/home/config/nomarchy/default/hypr/bindings/media.conf,core/home/config/nomarchy/default/hypr/bindings/utilities.conf | `kept` | |
| `nomarchy-brightness-display-apple` | `core/system/scripts` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf | `kept` | | | `nomarchy-brightness-display-apple` | `core/system/scripts` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf | `kept` | |
| `nomarchy-brightness-keyboard` | `core/system/scripts` | core/home/config/nomarchy/default/hypr/bindings/media.conf | `kept` | | | `nomarchy-brightness-keyboard` | `core/system/scripts` | core/home/config/nomarchy/default/hypr/bindings/media.conf | `kept` | |
| `nomarchy-build-iso` | `features/scripts/utils` | — | `unused?` | | | `nomarchy-build-iso` | `features/scripts/utils` | README.md | `kept` | |
| `nomarchy-build-live-iso` | `features/scripts/utils` | — | `unused?` | | | `nomarchy-build-live-iso` | `features/scripts/utils` | README.md | `kept` | |
| `nomarchy-cmd-audio-switch` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/media.conf | `kept` | | | `nomarchy-cmd-audio-switch` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/media.conf | `kept` | |
| `nomarchy-cmd-present` | `features/scripts/utils` | core/home/config/nomarchy/hooks/battery-low.sample,features/scripts/utils/nomarchy-launch-editor, +4 more | `kept` | | | `nomarchy-cmd-present` | `features/scripts/utils` | core/home/config/nomarchy/hooks/battery-low.sample,features/scripts/utils/nomarchy-launch-editor, +4 more | `kept` | |
| `nomarchy-cmd-screenrecord` | `features/scripts/utils` | features/desktop/waybar/config/config.jsonc,features/desktop/waybar/themes/summer-night/config.jsonc, +1 more | `kept` | | | `nomarchy-cmd-screenrecord` | `features/scripts/utils` | features/desktop/waybar/config/config.jsonc,features/desktop/waybar/themes/summer-night/config.jsonc, +1 more | `kept` | |
@@ -47,7 +46,6 @@ Phase B (per-batch PRs) refines those into `port-from-omarchy`,
| `nomarchy-cmd-screenshot` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,core/home/config/nomarchy-skill/SKILL.md, +1 more | `kept` | | | `nomarchy-cmd-screenshot` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,core/home/config/nomarchy-skill/SKILL.md, +1 more | `kept` | |
| `nomarchy-cmd-share` | `features/scripts/utils` | features/scripts/utils/nomarchy-menu | `kept` | | | `nomarchy-cmd-share` | `features/scripts/utils` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-cmd-terminal-cwd` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/plain-bindings.conf,features/desktop/hyprland/config/bindings.conf | `kept` | | | `nomarchy-cmd-terminal-cwd` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/plain-bindings.conf,features/desktop/hyprland/config/bindings.conf | `kept` | |
| `nomarchy-config-direct-boot` | `features/scripts/utils` | — | `unused?` | |
| `nomarchy-debug` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | | | `nomarchy-debug` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-docs-keybindings` | `features/scripts/utils` | bin/utils/nomarchy-docs-keybindings | `kept` | | | `nomarchy-docs-keybindings` | `features/scripts/utils` | bin/utils/nomarchy-docs-keybindings | `kept` | |
| `nomarchy-docs-scripts` | `features/scripts/utils` | bin/utils/nomarchy-docs-scripts | `kept` | | | `nomarchy-docs-scripts` | `features/scripts/utils` | bin/utils/nomarchy-docs-scripts | `kept` | |
@@ -69,7 +67,7 @@ Phase B (per-batch PRs) refines those into `port-from-omarchy`,
| `nomarchy-hw-vulkan` | `core/system/scripts` | features/scripts/utils/nomarchy-voxtype-install | `kept` | | | `nomarchy-hw-vulkan` | `core/system/scripts` | features/scripts/utils/nomarchy-voxtype-install | `kept` | |
| `nomarchy-hyprland-active-window-transparency-toggle` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf | `kept` | | | `nomarchy-hyprland-active-window-transparency-toggle` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf | `kept` | |
| `nomarchy-hyprland-monitor-scaling-cycle` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/tiling-v2.conf,features/scripts/utils/nomarchy-menu, +1 more | `kept` | | | `nomarchy-hyprland-monitor-scaling-cycle` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/tiling-v2.conf,features/scripts/utils/nomarchy-menu, +1 more | `kept` | |
| `nomarchy-hyprland-window-close-all` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/tiling.conf,core/home/config/nomarchy/default/hypr/bindings/tiling-v2.conf, +3 more | `kept` | | | `nomarchy-hyprland-window-close-all` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/tiling-v2.conf,core/system/scripts/nomarchy-system-logout, +2 more | `kept` | |
| `nomarchy-hyprland-window-gaps-toggle` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,features/scripts/utils/nomarchy-menu | `kept` | | | `nomarchy-hyprland-window-gaps-toggle` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-hyprland-window-pop` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/tiling-v2.conf | `kept` | | | `nomarchy-hyprland-window-pop` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/tiling-v2.conf | `kept` | |
| `nomarchy-hyprland-window-single-square-aspect-toggle` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,features/scripts/utils/nomarchy-menu | `kept` | | | `nomarchy-hyprland-window-single-square-aspect-toggle` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,features/scripts/utils/nomarchy-menu | `kept` | |
@@ -88,15 +86,13 @@ Phase B (per-batch PRs) refines those into `port-from-omarchy`,
| `nomarchy-launch-screensaver` | `features/scripts/utils` | features/desktop/idle.nix,features/scripts/utils/nomarchy-menu | `kept` | | | `nomarchy-launch-screensaver` | `features/scripts/utils` | features/desktop/idle.nix,features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-launch-tui` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,features/desktop/hyprland/config/bindings.conf, +2 more | `kept` | | | `nomarchy-launch-tui` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,features/desktop/hyprland/config/bindings.conf, +2 more | `kept` | |
| `nomarchy-launch-walker` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/clipboard.conf,core/home/config/nomarchy/default/hypr/bindings/utilities.conf, +4 more | `kept` | | | `nomarchy-launch-walker` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/clipboard.conf,core/home/config/nomarchy/default/hypr/bindings/utilities.conf, +4 more | `kept` | |
| `nomarchy-launch-webapp` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/plain-bindings.conf,features/desktop/hyprland/config/bindings.conf, +7 more | `kept` | | | `nomarchy-launch-webapp` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/plain-bindings.conf,features/desktop/hyprland/config/bindings.conf, +5 more | `kept` | |
| `nomarchy-launch-wifi` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,core/home/config/nomarchy/default/mako/core.ini, +4 more | `kept` | | | `nomarchy-launch-wifi` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,core/home/config/nomarchy/default/mako/core.ini, +4 more | `kept` | |
| `nomarchy-lock-screen` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,core/home/config/nomarchy/extensions/menu.sh, +3 more | `kept` | | | `nomarchy-lock-screen` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,core/home/config/nomarchy/extensions/menu.sh, +3 more | `kept` | |
| `nomarchy-manual` | `features/scripts/utils` | features/scripts/utils/nomarchy-menu,themes/engine/scripts/nomarchy-theme-install | `kept` | | | `nomarchy-manual` | `features/scripts/utils` | core/branding/about.txt,features/scripts/utils/nomarchy-menu, +1 more | `kept` | |
| `nomarchy-menu` | `features/scripts/utils` | bin/utils/nomarchy-docs-scripts,core/home/config/nomarchy/default/hypr/bindings/utilities.conf, +10 more | `kept` | | | `nomarchy-menu` | `features/scripts/utils` | bin/utils/nomarchy-docs-scripts,core/home/config/nomarchy/default/hypr/bindings/utilities.conf, +10 more | `kept` | |
| `nomarchy-menu-keybindings` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,core/home/config/nomarchy/default/mako/core.ini, +2 more | `kept` | | | `nomarchy-menu-keybindings` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,core/home/config/nomarchy/default/mako/core.ini, +2 more | `kept` | |
| `nomarchy-migrate-state` | `features/scripts/utils` | — | `unused?` | |
| `nomarchy-notification-dismiss` | `features/scripts/utils` | core/home/config/nomarchy/default/mako/core.ini | `kept` | | | `nomarchy-notification-dismiss` | `features/scripts/utils` | core/home/config/nomarchy/default/mako/core.ini | `kept` | |
| `nomarchy-npx-install` | `features/scripts/utils` | — | `unused?` | |
| `nomarchy-on-boot` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/autostart.conf | `kept` | | | `nomarchy-on-boot` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/autostart.conf | `kept` | |
| `nomarchy-pkg-add` | `core/system/scripts` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-pkg-install, +2 more | `kept` | | | `nomarchy-pkg-add` | `core/system/scripts` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-pkg-install, +2 more | `kept` | |
| `nomarchy-pkg-aur-add` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | | | `nomarchy-pkg-aur-add` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
@@ -104,42 +100,36 @@ Phase B (per-batch PRs) refines those into `port-from-omarchy`,
| `nomarchy-pkg-install` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | | | `nomarchy-pkg-install` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-pkg-remove` | `core/system/scripts` | features/scripts/utils/nomarchy-pkg-drop | `kept` | | | `nomarchy-pkg-remove` | `core/system/scripts` | features/scripts/utils/nomarchy-pkg-drop | `kept` | |
| `nomarchy-powerprofiles-list` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | | | `nomarchy-powerprofiles-list` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-preflight-migration` | `core/system/scripts` | core/system/scripts/nomarchy-env-update,features/scripts/utils/nomarchy-migrate-state | `kept` | | | `nomarchy-preflight-migration` | `core/system/scripts` | core/system/scripts/nomarchy-env-update | `kept` | |
| `nomarchy-refresh-config` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-refresh-fastfetch | `kept` | | | `nomarchy-refresh-config` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-refresh-fastfetch | `kept` | |
| `nomarchy-refresh-fastfetch` | `features/scripts/utils` | — | `unused?` | | | `nomarchy-refresh-fastfetch` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-refresh-hyprland` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | | | `nomarchy-refresh-hyprland` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-refresh-waybar` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | | | `nomarchy-refresh-waybar` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-reinstall` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | | | `nomarchy-reinstall` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-restart-app` | `features/scripts/utils` | core/system/scripts/nomarchy-restart-xcompose,features/scripts/utils/nomarchy-restart-hypridle, +3 more | `kept` | | | `nomarchy-restart-app` | `features/scripts/utils` | core/system/scripts/nomarchy-restart-xcompose,features/scripts/utils/nomarchy-restart-hypridle, +3 more | `kept` | |
| `nomarchy-restart-bluetooth` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | | | `nomarchy-restart-bluetooth` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-restart-btop` | `features/scripts/utils` | themes/engine/scripts/nomarchy-theme-set | `kept` | | | `nomarchy-restart-btop` | `features/scripts/utils` | themes/engine/scripts/nomarchy-theme-set | `kept` | |
| `nomarchy-restart-hyprctl` | `features/scripts/utils` | — | `unused?` | |
| `nomarchy-restart-hypridle` | `features/scripts/utils` | features/scripts/utils/nomarchy-menu | `kept` | | | `nomarchy-restart-hypridle` | `features/scripts/utils` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-restart-hyprsunset` | `features/scripts/utils` | features/scripts/utils/nomarchy-menu | `kept` | | | `nomarchy-restart-hyprsunset` | `features/scripts/utils` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-restart-mako` | `features/scripts/utils` | — | `unused?` | |
| `nomarchy-restart-opencode` | `features/scripts/utils` | themes/engine/scripts/nomarchy-theme-set | `kept` | | | `nomarchy-restart-opencode` | `features/scripts/utils` | themes/engine/scripts/nomarchy-theme-set | `kept` | |
| `nomarchy-restart-pipewire` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | | | `nomarchy-restart-pipewire` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-restart-swayosd` | `features/scripts/utils` | features/scripts/utils/nomarchy-menu | `kept` | | | `nomarchy-restart-swayosd` | `features/scripts/utils` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-restart-terminal` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | | | `nomarchy-restart-terminal` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-restart-tmux` | `features/scripts/utils` | — | `unused?` | | | `nomarchy-restart-trackpad` | `core/system/scripts` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-restart-trackpad` | `core/system/scripts` | — | `unused?` | |
| `nomarchy-restart-walker` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-menu, +1 more | `kept` | | | `nomarchy-restart-walker` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-menu, +1 more | `kept` | |
| `nomarchy-restart-waybar` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-menu, +4 more | `kept` | | | `nomarchy-restart-waybar` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-menu, +4 more | `kept` | |
| `nomarchy-restart-wifi` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | | | `nomarchy-restart-wifi` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-restart-xcompose` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | | | `nomarchy-restart-xcompose` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-rollback` | `features/scripts/utils` | — | `unused?` | |
| `nomarchy-setup-dns` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | | | `nomarchy-setup-dns` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-setup-fido2` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | | | `nomarchy-setup-fido2` | `core/system/scripts` | features/scripts/utils/nomarchy-menu,installer/install.sh | `kept` | |
| `nomarchy-setup-fingerprint` | `core/system/scripts` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-menu | `kept` | | | `nomarchy-setup-fingerprint` | `core/system/scripts` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-show-done` | `themes/engine/scripts` | features/scripts/utils/nomarchy-launch-floating-terminal-with-presentation | `kept` | | | `nomarchy-show-done` | `themes/engine/scripts` | features/scripts/utils/nomarchy-launch-floating-terminal-with-presentation | `kept` | |
| `nomarchy-show-logo` | `themes/engine/scripts` | features/scripts/utils/nomarchy-launch-floating-terminal-with-presentation | `kept` | | | `nomarchy-show-logo` | `themes/engine/scripts` | features/scripts/utils/nomarchy-launch-floating-terminal-with-presentation | `kept` | |
| `nomarchy-skill` | `features/scripts/utils` | core/home/configs.nix | `kept` | | | `nomarchy-skill` | `features/scripts/utils` | core/home/configs.nix | `kept` | |
| `nomarchy-snapshot` | `features/scripts/utils` | — | `unused?` | |
| `nomarchy-state` | `features/scripts/utils` | core/system/scripts/nomarchy-system-reboot,core/system/scripts/nomarchy-system-shutdown, +2 more | `kept` | | | `nomarchy-state` | `features/scripts/utils` | core/system/scripts/nomarchy-system-reboot,core/system/scripts/nomarchy-system-shutdown, +2 more | `kept` | |
| `nomarchy-state-write` | `features/scripts/utils` | features/scripts/utils/nomarchy-welcome | `kept` | | | `nomarchy-state-write` | `features/scripts/utils` | features/scripts/utils/nomarchy-welcome | `kept` | |
| `nomarchy-sudo-keepalive` | `core/system/scripts` | — | `unused?` | | | `nomarchy-sudo-passwordless-toggle` | `core/system/scripts` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-sudo-passwordless-toggle` | `core/system/scripts` | — | `unused?` | | | `nomarchy-sudo-reset` | `core/system/scripts` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-sudo-reset` | `core/system/scripts` | — | `unused?` | |
| `nomarchy-swayosd-brightness` | `core/system/scripts` | core/system/scripts/nomarchy-brightness-display,core/system/scripts/nomarchy-brightness-display-apple | `kept` | | | `nomarchy-swayosd-brightness` | `core/system/scripts` | core/system/scripts/nomarchy-brightness-display,core/system/scripts/nomarchy-brightness-display-apple | `kept` | |
| `nomarchy-swayosd-kbd-brightness` | `core/system/scripts` | core/system/scripts/nomarchy-brightness-keyboard | `kept` | | | `nomarchy-swayosd-kbd-brightness` | `core/system/scripts` | core/system/scripts/nomarchy-brightness-keyboard | `kept` | |
| `nomarchy-sync` | `features/scripts/utils` | features/scripts/utils/nomarchy-backup,README.md | `kept` | | | `nomarchy-sync` | `features/scripts/utils` | features/scripts/utils/nomarchy-backup,README.md | `kept` | |
@@ -150,16 +140,17 @@ Phase B (per-batch PRs) refines those into `port-from-omarchy`,
| `nomarchy-test-installer` | `features/scripts/utils` | features/scripts/utils/nomarchy-test-vm,README.md | `kept` | | | `nomarchy-test-installer` | `features/scripts/utils` | features/scripts/utils/nomarchy-test-vm,README.md | `kept` | |
| `nomarchy-test-live-iso` | `features/scripts/utils` | hosts/nomarchy-live.nix | `kept` | | | `nomarchy-test-live-iso` | `features/scripts/utils` | hosts/nomarchy-live.nix | `kept` | |
| `nomarchy-test-vm` | `features/scripts/utils` | features/scripts/utils/nomarchy-test-live-iso | `kept` | | | `nomarchy-test-vm` | `features/scripts/utils` | features/scripts/utils/nomarchy-test-live-iso | `kept` | |
| `nomarchy-theme` | `features/scripts/utils` | bin/utils/nomarchy-docs-scripts,core/home/config/nomarchy/default/elephant/nomarchy_background_selector.lua, +16 more | `kept` | | | `nomarchy-theme` | `features/scripts/utils` | bin/utils/nomarchy-docs-scripts,core/home/config/nomarchy/default/elephant/nomarchy_background_selector.lua, +17 more | `kept` | |
| `nomarchy-theme-bg-install` | `themes/engine/scripts` | — | `unused?` | | | `nomarchy-theme-bg-install` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-theme-bg-next` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-wallpaper, +1 more | `kept` | | | `nomarchy-theme-bg-next` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-wallpaper, +1 more | `kept` | |
| `nomarchy-theme-bg-set` | `themes/engine/scripts` | core/home/config/nomarchy/default/elephant/nomarchy_background_selector.lua,features/scripts/utils/nomarchy-wallpaper | `kept` | | | `nomarchy-theme-bg-set` | `themes/engine/scripts` | core/home/config/nomarchy/default/elephant/nomarchy_background_selector.lua,features/scripts/utils/nomarchy-wallpaper | `kept` | |
| `nomarchy-theme-current` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md | `kept` | | | `nomarchy-theme-current` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md,themes/engine/scripts/nomarchy-theme-next | `kept` | |
| `nomarchy-theme-install` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md | `kept` | | | `nomarchy-theme-install` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-theme-list` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-theme, +1 more | `kept` | | | `nomarchy-theme-list` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-theme, +2 more | `kept` | |
| `nomarchy-theme-refresh` | `themes/engine/scripts` | — | `unused?` | | | `nomarchy-theme-next` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-theme-remove` | `themes/engine/scripts` | — | `unused?` | | | `nomarchy-theme-refresh` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-theme-set` | `themes/engine/scripts` | core/home/config/nomarchy/default/elephant/nomarchy_themes.lua,core/home/config/nomarchy-skill/SKILL.md, +8 more | `kept` | | | `nomarchy-theme-remove` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-theme-set` | `themes/engine/scripts` | core/home/config/nomarchy/default/elephant/nomarchy_themes.lua,core/home/config/nomarchy-skill/SKILL.md, +9 more | `kept` | |
| `nomarchy-theme-set-keyboard` | `themes/engine/scripts` | features/scripts/utils/nomarchy-on-boot | `kept` | | | `nomarchy-theme-set-keyboard` | `themes/engine/scripts` | features/scripts/utils/nomarchy-on-boot | `kept` | |
| `nomarchy-theme-set-keyboard-asus-rog` | `themes/engine/scripts` | features/scripts/utils/nomarchy-on-boot,themes/engine/scripts/nomarchy-theme-set-keyboard | `kept` | | | `nomarchy-theme-set-keyboard-asus-rog` | `themes/engine/scripts` | features/scripts/utils/nomarchy-on-boot,themes/engine/scripts/nomarchy-theme-set-keyboard | `kept` | |
| `nomarchy-theme-set-keyboard-f16` | `themes/engine/scripts` | features/scripts/utils/nomarchy-on-boot,themes/engine/scripts/nomarchy-theme-set-keyboard | `kept` | | | `nomarchy-theme-set-keyboard-f16` | `themes/engine/scripts` | features/scripts/utils/nomarchy-on-boot,themes/engine/scripts/nomarchy-theme-set-keyboard | `kept` | |
@@ -167,37 +158,35 @@ Phase B (per-batch PRs) refines those into `port-from-omarchy`,
| `nomarchy-theme-set-templates` | `themes/engine/scripts` | themes/engine/scripts/nomarchy-theme-set | `kept` | | | `nomarchy-theme-set-templates` | `themes/engine/scripts` | themes/engine/scripts/nomarchy-theme-set | `kept` | |
| `nomarchy-themes-prebuild` | `themes/engine/scripts` | installer/install.sh | `kept` | | | `nomarchy-themes-prebuild` | `themes/engine/scripts` | installer/install.sh | `kept` | |
| `nomarchy-theme-update` | `themes/engine/scripts` | features/scripts/utils/nomarchy-menu | `kept` | | | `nomarchy-theme-update` | `themes/engine/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-toggle-hybrid-gpu` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | | | `nomarchy-toggle-hybrid-gpu` | `core/system/scripts` | features/scripts/utils/nomarchy-menu,features/scripts/utils/nomarchy-sys-update, +1 more | `kept` | |
| `nomarchy-toggle-idle` | `core/system/scripts` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,features/desktop/waybar/config/config.jsonc, +2 more | `kept` | | | `nomarchy-toggle-idle` | `core/system/scripts` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,features/desktop/waybar/config/config.jsonc, +2 more | `kept` | |
| `nomarchy-toggle-nightlight` | `themes/engine/scripts` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,core/home/config/nomarchy-skill/SKILL.md, +1 more | `kept` | | | `nomarchy-toggle-nightlight` | `themes/engine/scripts` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,core/home/config/nomarchy-skill/SKILL.md, +1 more | `kept` | |
| `nomarchy-toggle-notification-silencing` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,features/desktop/waybar/config/config.jsonc, +1 more | `kept` | | | `nomarchy-toggle-notification-silencing` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,features/desktop/waybar/config/config.jsonc, +1 more | `kept` | |
| `nomarchy-toggle-screensaver` | `features/scripts/utils` | features/scripts/utils/nomarchy-menu | `kept` | | | `nomarchy-toggle-screensaver` | `features/scripts/utils` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-toggle-suspend` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | | | `nomarchy-toggle-suspend` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-toggle-waybar` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,core/home/config/nomarchy-skill/SKILL.md, +1 more | `kept` | | | `nomarchy-toggle-waybar` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,core/home/config/nomarchy-skill/SKILL.md, +1 more | `kept` | |
| `nomarchy-tui-install` | `features/scripts/utils` | features/scripts/utils/nomarchy-tui-remove-all | `kept` | | | `nomarchy-tui-install` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-tui-remove-all | `kept` | |
| `nomarchy-tui-remove` | `features/scripts/utils` | — | `unused?` | | | `nomarchy-tui-remove` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-tui-remove-all` | `features/scripts/utils` | — | `unused?` | | | `nomarchy-tui-remove-all` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-tz-select` | `core/system/scripts` | features/desktop/waybar/config/config.jsonc,features/scripts/utils/nomarchy-menu | `kept` | | | `nomarchy-tz-select` | `core/system/scripts` | features/desktop/waybar/config/config.jsonc,features/scripts/utils/nomarchy-menu, +2 more | `kept` | |
| `nomarchy-update` | `core/system/scripts` | core/home/config/nomarchy/default/mako/core.ini,core/home/config/nomarchy-skill/SKILL.md, +4 more | `kept` | | | `nomarchy-update` | `core/system/scripts` | core/home/config/nomarchy/default/mako/core.ini,core/home/config/nomarchy-skill/SKILL.md, +4 more | `kept` | |
| `nomarchy-update-available` | `features/scripts/utils` | features/desktop/waybar/config/config.jsonc,features/desktop/waybar/themes/summer-night/config.jsonc | `kept` | | | `nomarchy-update-available` | `features/scripts/utils` | features/desktop/waybar/config/config.jsonc,features/desktop/waybar/themes/summer-night/config.jsonc | `kept` | |
| `nomarchy-update-firmware` | `features/scripts/utils` | features/scripts/utils/nomarchy-menu | `kept` | | | `nomarchy-update-firmware` | `features/scripts/utils` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-update-time` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | | | `nomarchy-update-time` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-upload-log` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-debug | `kept` | | | `nomarchy-upload-log` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-debug | `kept` | |
| `nomarchy-version` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-debug, +1 more | `kept` | | | `nomarchy-version` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-debug | `kept` | |
| `nomarchy-voxtype-config` | `features/scripts/utils` | features/desktop/waybar/config/config.jsonc,features/desktop/waybar/themes/summer-night/config.jsonc | `kept` | | | `nomarchy-voxtype-config` | `features/scripts/utils` | features/desktop/waybar/config/config.jsonc,features/desktop/waybar/themes/summer-night/config.jsonc | `kept` | |
| `nomarchy-voxtype-install` | `features/scripts/utils` | — | `unused?` | | | `nomarchy-voxtype-install` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-voxtype-model` | `features/scripts/utils` | features/desktop/waybar/config/config.jsonc,features/desktop/waybar/themes/summer-night/config.jsonc | `kept` | | | `nomarchy-voxtype-model` | `features/scripts/utils` | features/desktop/waybar/config/config.jsonc,features/desktop/waybar/themes/summer-night/config.jsonc | `kept` | |
| `nomarchy-voxtype-remove` | `features/scripts/utils` | — | `unused?` | | | `nomarchy-voxtype-remove` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-voxtype-status` | `features/scripts/utils` | features/desktop/waybar/config/config.jsonc,features/desktop/waybar/themes/summer-night/config.jsonc | `kept` | | | `nomarchy-voxtype-status` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/desktop/waybar/config/config.jsonc, +1 more | `kept` | |
| `nomarchy-wallpaper` | `features/scripts/utils` | bin/utils/nomarchy-docs-scripts,core/home/config/nomarchy/default/hypr/autostart.conf, +2 more | `kept` | | | `nomarchy-wallpaper` | `features/scripts/utils` | bin/utils/nomarchy-docs-scripts,core/home/config/nomarchy/default/hypr/autostart.conf, +2 more | `kept` | |
| `nomarchy-webapp-handler-hey` | `features/scripts/utils` | — | `unused?` | | | `nomarchy-webapp-install` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-webapp-remove-all | `kept` | |
| `nomarchy-webapp-handler-zoom` | `features/scripts/utils` | — | `unused?` | | | `nomarchy-webapp-remove` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-webapp-install` | `features/scripts/utils` | features/scripts/utils/nomarchy-webapp-remove-all | `kept` | | | `nomarchy-webapp-remove-all` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-webapp-remove` | `features/scripts/utils` | — | `unused?` | |
| `nomarchy-webapp-remove-all` | `features/scripts/utils` | — | `unused?` | |
| `nomarchy-welcome` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/autostart.conf | `kept` | | | `nomarchy-welcome` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/autostart.conf | `kept` | |
| `nomarchy-wifi-powersave` | `core/system/scripts` | — | `unused?` | | | `nomarchy-wifi-powersave` | `core/system/scripts` | features/scripts/utils/nomarchy-sys-update,installer/install.sh | `kept` | |
| `nomarchy-windows-vm` | `features/scripts/utils` | — | `unused?` | | | `nomarchy-windows-vm` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
## Missing references ## Missing references
@@ -205,21 +194,6 @@ Tokens grepped from `core/`, `features/`, `themes/`, `installer/`, `hosts/`, `bi
| Token | Referenced in | Status | | Token | Referenced in | Status |
| --- | --- | --- | | --- | --- | --- |
| `nomarchy-dev` | core/home/config/nomarchy-skill/SKILL.md | `missing` |
| `nomarchy-dryrun` | installer/install.sh | `missing` |
| `nomarchy-installer` | features/scripts/utils/nomarchy-build-iso,README.md | `missing` |
| `nomarchy-live` | features/scripts/utils/nomarchy-build-live-iso,features/scripts/utils/nomarchy-test-live-iso | `missing` |
| `nomarchy-luks` | installer/disko-config.nix,installer/install.sh | `missing` |
| `nomarchy-menu-rows` | bin/utils/nomarchy-docs-scripts,features/scripts/utils/nomarchy-docs-scripts | `missing` |
| `nomarchy-nopasswd` | core/system/scripts/nomarchy-sudo-passwordless-toggle | `missing` |
| `nomarchy-nopasswd-expire` | core/system/scripts/nomarchy-sudo-passwordless-toggle | `missing` |
| `nomarchy-plymouth` | themes/engine/plymouth.nix | `missing` |
| `nomarchy-screenrecord-filename` | features/scripts/utils/nomarchy-cmd-screenrecord | `missing` |
| `nomarchy-sddm-theme` | themes/engine/sddm.nix | `missing` |
| `nomarchy-theme-next` | core/home/config/nomarchy-skill/SKILL.md | `missing` |
| `nomarchy-themes-no-images` | themes/engine/files.nix | `missing` |
| `nomarchy-vm` | features/scripts/utils/nomarchy-test-vm | `missing` |
| `nomarchy-windows` | features/scripts/utils/nomarchy-windows-vm | `missing` |
## Menu items ## Menu items

View File

@@ -33,10 +33,19 @@ While the system is defined declaratively, Nomarchy uses a small, local state fi
- `nixosModules.home`: Exports the application and desktop logic (`./features`). - `nixosModules.home`: Exports the application and desktop logic (`./features`).
- `nixosConfigurations`: Defines pre-configured targets like `nomarchy-installer`, `nomarchy-live`, and a testing `vm`. - `nixosConfigurations`: Defines pre-configured targets like `nomarchy-installer`, `nomarchy-live`, and a testing `vm`.
- **`flake.lock`**: Locks dependency versions for reproducible builds. - **`flake.lock`**: Locks dependency versions for reproducible builds.
- **`GEMINI.md`**: Foundational mandates and architectural rules for the Nomarchy Agent.
- **`STRUCTURE.md`**: (This file) Detailed architectural documentation.
- **`README.md`**: Project overview, installation instructions, and basic usage. - **`README.md`**: Project overview, installation instructions, and basic usage.
- **`TODO.md`**: Roadmap and pending tasks. - **`docs/`**: All long-form documentation. Key entry points:
- **`AGENT.md`**: Onboarding for AI coding agents picking up Nomarchy.
- **`STRUCTURE.md`**: (This file) Detailed architectural documentation.
- **`OPTIONS.md`**: Reference for every `nomarchy.*` option.
- **`ROADMAP.md`**: Now / Next / Later board and the Shipped log.
- **`MIGRATION.md`**: Layering Nomarchy onto an existing NixOS install.
- **`KEYBINDINGS.md`**: Auto-generated keybinding reference.
- **`SCRIPTS.md`**: Auto-generated `nomarchy-*` script audit.
- **`TROUBLESHOOTING.md`**: Common rebuild errors and fixes.
- **`creating-themes.md`**: Theme palette authoring guide.
- **`.forgejo/workflows/`**: Forgejo Actions CI. Runs `nix flake check --no-build`, lints every `nomarchy-*` bash script with `bash -n` + `shellcheck --severity=error`, and verifies `docs/SCRIPTS.md` is up to date on every push to `main` and every PR. To activate: enable Actions on the repo in Forgejo and register a `forgejo-runner` (any Docker-capable Linux host works; the workflow uses `ubuntu-latest` and installs Nix itself).
- **`.githooks/`**: Optional per-clone git hooks (`pre-commit` lints changed scripts and regenerates `docs/SCRIPTS.md`). Enable with `git config core.hooksPath .githooks`. CI enforces the same invariants tree-wide.
--- ---
@@ -61,8 +70,8 @@ The `core/` directory contains the foundational modules required for a functiona
- **`default.nix`**: The entry point for the base Home Manager module. - **`default.nix`**: The entry point for the base Home Manager module.
- **`options.nix`**: Defines the `nomarchy` user options (Toggles, Theme, Fonts, etc.). - **`options.nix`**: Defines the `nomarchy` user options (Toggles, Theme, Fonts, etc.).
- **`state.nix`**: Loads and applies user-level state (from `~/.config/nomarchy/state.json`). - **`state.nix`**: Loads and applies user-level state (from `~/.config/nomarchy/state.json`).
- **`behavior.nix`**: Deploys non-visual configs (Keybindings, Input settings, Window rules) with `lib.mkDefault`. - **`overrides.nix`**: Declares `nomarchy.overrides.*` (reserved for a future file-based override loader; currently no-op).
- **`configs.nix`**: Manages static configuration files and directories in `~/.config/`. - **`configs.nix`**: Manages static configuration files and directories in `~/.config/`. Honors `nomarchy.configOverrides` as a bulk redirect to a replacement config dir.
- **`bash.nix`**: Shell environment, aliases, and specialized `env-update` hooks. - **`bash.nix`**: Shell environment, aliases, and specialized `env-update` hooks.
- **`security.nix`**: Polkit, keyring management, and GPG settings. - **`security.nix`**: Polkit, keyring management, and GPG settings.
- **`config/`**: Contains the physical source files for the base user configuration (e.g., `starship.toml`, `hypr/` behavior configs). - **`config/`**: Contains the physical source files for the base user configuration (e.g., `starship.toml`, `hypr/` behavior configs).
@@ -125,7 +134,6 @@ The `lib/` directory provides centralized logic and data structures to maintain
### `installer/` (Bootstrap) ### `installer/` (Bootstrap)
- **`install.sh`**: The interactive TTY-based installer. It handles disk partitioning, NixOS installation, and generating a clean "Downstream" flake for the user. - **`install.sh`**: The interactive TTY-based installer. It handles disk partitioning, NixOS installation, and generating a clean "Downstream" flake for the user.
- **`disko-config.nix`**: The disko partition layout (BTRFS on top of LUKS2). A Nix function of `{ mainDrive, extraDrives ? [] }` — single-disk path is `extraDrives = []`; multi-disk adds BTRFS `-d single -m raid1` across the extras. Invoked by `install.sh` via `disko --argstr mainDrive … --arg extraDrives '[…]'`. - **`disko-config.nix`**: The disko partition layout (BTRFS on top of LUKS2). A Nix function of `{ mainDrive, extraDrives ? [] }` — single-disk path is `extraDrives = []`; multi-disk adds BTRFS `-d single -m raid1` across the extras. Invoked by `install.sh` via `disko --argstr mainDrive … --arg extraDrives '[…]'`.
- **`disko-btrfs-luks.nix`**: A simpler reference layout for disk management (not used by the installer).
### `hosts/` (Targets) ### `hosts/` (Targets)
- **`nomarchy-installer.nix`**: Configuration for the minimal, TTY-based installation ISO. - **`nomarchy-installer.nix`**: Configuration for the minimal, TTY-based installation ISO.

View File

@@ -1,25 +0,0 @@
general.import = [ "~/.config/nomarchy/current/theme/alacritty.toml" ]
[env]
TERM = "xterm-256color"
[terminal]
osc52 = "CopyPaste"
[font]
normal = { family = "JetBrainsMono Nerd Font", style = "Regular" }
bold = { family = "JetBrainsMono Nerd Font", style = "Bold" }
italic = { family = "JetBrainsMono Nerd Font", style = "Italic" }
size = 9
[window]
padding.x = 14
padding.y = 14
decorations = "None"
[keyboard]
bindings = [
{ key = "Insert", mods = "Shift", action = "Paste" },
{ key = "Insert", mods = "Control", action = "Copy" },
{ key = "Return", mods = "Shift", chars = "\u001B\r" }
]

View File

@@ -2,14 +2,6 @@
let let
nomarchyLib = import ../lib { inherit lib; }; nomarchyLib = import ../lib { inherit lib; };
userPackagesFile = "${config.home.homeDirectory}/.config/home-manager/user-packages.json";
userPackages = if builtins.pathExists userPackagesFile then
let
pkgNames = builtins.fromJSON (builtins.readFile userPackagesFile);
# Filter to only packages that exist in pkgs to prevent build failures
validPkgs = builtins.filter (name: builtins.hasAttr name pkgs) pkgNames;
in builtins.map (name: pkgs.${name}) validPkgs
else [];
in in
{ {
imports = [ imports = [

View File

@@ -1,71 +0,0 @@
# paths to stylesheets on the filesystem which should be applied to the application
#
# relative paths are resolved relative to the location of the config file
stylesheets: ["../nomarchy/current/theme/hyprland-preview-share-picker.css"]
# default page selected when the picker is opened
default_page: outputs
window:
# height of the application window
height: 500
# width of the application window
width: 1000
image:
# size to which the images should be internally resized to reduce the memory footprint
resize_size: 500
# target size of the longer side of the image widget
widget_size: 150
classes:
# css classname of the window
window: window
# css classname of the card containing an image and a label
image_card: card
# css classname of the card containing an image and a label when the image is still being loaded
image_card_loading: card-loading
# css classname of the image inside the card
image: image
# css classname of the label inside the card
image_label: image-label
# css classname of the notebook containing all pages
notebook: notebook
# css classname of a label of the notebook
tab_label: tab-label
# css classname of a notebook page (e.g. windows container)
notebook_page: page
# css classname of the region selection button
region_button: region-button
# css classname of the button containing the session restore checkbox and label
restore_button: restore-button
windows:
# minimum amount of image cards per row on the windows page
min_per_row: 3
# maximum amount of image cards per row on the windows page
max_per_row: 999
# number of clicks needed to select a window
clicks: 1
# spacing in pixels between the window cards
spacing: 12
outputs:
# number of clicks needed to select an output
clicks: 1
# spacing in pixels between the outputs in the layout
# note: the spacing is applied from both sides (the gap is `spacing * 2`)
spacing: 6
# show the label with the output name
show_label: false
# size the output cards respectively to their scaling
respect_output_scaling: true
region:
# command to run for region selection
# the output needs to be in the <output>@<x>,<y>,<w>,<h> (e.g. DP-3@2789,436,756,576) format
command: slurp -f '%o@%x,%y,%w,%h'
# hide the token restore checkbox and use the default value instead
hide_token_restore: true
# enable debug logs by default
debug: false

View File

@@ -1,2 +0,0 @@
# Extra autostart processes
# exec-once = uwsm-app -- my-service

View File

@@ -1,43 +0,0 @@
source = ~/.config/nomarchy/current/theme/hyprlock.conf
general {
ignore_empty_input = true
}
background {
monitor =
color = $color
path = ~/.config/nomarchy/current/background
blur_passes = 3
}
animations {
enabled = false
}
input-field {
monitor =
size = 650, 100
position = 0, 0
halign = center
valign = center
inner_color = $inner_color
outer_color = $outer_color
outline_thickness = 4
font_family = JetBrainsMono Nerd Font
font_color = $font_color
placeholder_text = Enter Password
check_color = $check_color
fail_text = <i>$FAIL ($ATTEMPTS)</i>
rounding = 0
shadow_passes = 0
fade_on_empty = false
}
auth {
fingerprint:enabled = false
}

View File

@@ -1,14 +0,0 @@
# Makes hyprsunset do nothing to the screen by default
# Without this, the default applies some tint to the monitor
profile {
time = 07:00
identity = true
}
# To enable auto switch to nightlight, set in your .config/hypr/autostart:
# exec-once = uwsm app -- hyprsunset
# and use the following:
# profile {
# time = 20:00
# temperature = 4000
# }

View File

@@ -1,34 +0,0 @@
# Change the default Nomarchy look'n'feel
# https://wiki.hyprland.org/Configuring/Variables/#general
general {
# No gaps between windows or borders
# gaps_in = 0
# gaps_out = 0
# border_size = 0
# Change to niri-like side-scrolling layout
# layout = scrolling
}
# https://wiki.hyprland.org/Configuring/Variables/#decoration
decoration {
# Use round window corners
# rounding = 8
# Dim unfocused windows (0.0 = no dim, 1.0 = fully dimmed)
# dim_inactive = true
# dim_strength = 0.15
}
# https://wiki.hyprland.org/Configuring/Variables/#animations
animations {
# Disable all animations
# enabled = no
}
# https://wiki.hypr.land/Configuring/Variables/#layout
layout {
# Avoid overly wide single-window layouts on wide screens
# single_window_aspect_ratio = 1 1
}

View File

@@ -1,4 +0,0 @@
screencopy {
allow_token_by_default = true
custom_picker_binary = hyprland-preview-share-picker
}

View File

@@ -48,13 +48,14 @@ in
''; '';
}; };
# Deploy Hyprland configuration files # Deploy Hyprland configuration files. Only the files that nomarchy.conf
# actually sources are deployed here — looknfeel.conf and autostart.conf
# live under ~/.config/nomarchy/default/hypr/ and are deployed by the
# core/home bulk-nomarchy dir, so duplicating them here was dead surface.
xdg.configFile."hypr/nomarchy.conf".source = ./config/nomarchy.conf; xdg.configFile."hypr/nomarchy.conf".source = ./config/nomarchy.conf;
xdg.configFile."hypr/monitors.conf".source = lib.mkDefault ./config/monitors.conf; xdg.configFile."hypr/monitors.conf".source = lib.mkDefault ./config/monitors.conf;
xdg.configFile."hypr/input.conf".source = lib.mkDefault ./config/input.conf; xdg.configFile."hypr/input.conf".source = lib.mkDefault ./config/input.conf;
xdg.configFile."hypr/bindings.conf".source = lib.mkDefault ./config/bindings.conf; xdg.configFile."hypr/bindings.conf".source = lib.mkDefault ./config/bindings.conf;
xdg.configFile."hypr/looknfeel.conf".source = lib.mkDefault ./config/looknfeel.conf;
xdg.configFile."hypr/autostart.conf".source = lib.mkDefault ./config/autostart.conf;
# Run swaybg as a proper systemd user service rather than a Hyprland exec-once. # Run swaybg as a proper systemd user service rather than a Hyprland exec-once.
# exec-once fails silently (black screen with no visible error) when timing # exec-once fails silently (black screen with no visible error) when timing

View File

@@ -71,7 +71,6 @@ let
NOMARCHY_TOGGLE_IDLE = if config.nomarchy.toggles.idle then "true" else "false"; NOMARCHY_TOGGLE_IDLE = if config.nomarchy.toggles.idle then "true" else "false";
NOMARCHY_TOGGLE_NIGHTLIGHT = if config.nomarchy.toggles.nightlight then "true" else "false"; NOMARCHY_TOGGLE_NIGHTLIGHT = if config.nomarchy.toggles.nightlight then "true" else "false";
NOMARCHY_TOGGLE_WAYBAR = if config.nomarchy.toggles.waybar then "true" else "false"; NOMARCHY_TOGGLE_WAYBAR = if config.nomarchy.toggles.waybar then "true" else "false";
NOMARCHY_TOGGLE_SKIP_VSCODE_THEME = if config.nomarchy.toggles.skipVsCodeTheme then "true" else "false";
NOMARCHY_MONOSPACE_FONT = config.nomarchy.fonts.monospace; NOMARCHY_MONOSPACE_FONT = config.nomarchy.fonts.monospace;
}; };

View File

@@ -1,46 +0,0 @@
#!/bin/bash
set -e
# Add an EFI boot entry for the Nomarchy UKI, allowing the system to boot directly
# without a bootloader like Limine. Requires UEFI firmware and a built UKI.
if [[ ! -d /sys/firmware/efi ]]; then
echo "Error: System is not booted in UEFI mode" >&2
exit 1
fi
if ! efibootmgr &>/dev/null; then
echo "Error: efibootmgr is not available or not functional" >&2
exit 1
fi
if cat /sys/class/dmi/id/bios_vendor 2>/dev/null | grep -qi "American Megatrends"; then
echo "Error: American Megatrends firmware may not safely support custom EFI entries" >&2
exit 1
fi
if cat /sys/class/dmi/id/bios_vendor 2>/dev/null | grep -qi "Apple"; then
echo "Error: Apple firmware uses its own boot manager" >&2
exit 1
fi
uki_file=$(find /boot/EFI/Linux/ -name "nomarchy*.efi" -printf "%f\n" 2>/dev/null | head -1)
if [[ -z $uki_file ]]; then
echo "Error: No Nomarchy UKI found in /boot/EFI/Linux/" >&2
exit 1
fi
boot_source=$(findmnt -n -o SOURCE /boot)
disk=$(echo "$boot_source" | sed 's/p\?[0-9]*$//')
part=$(echo "$boot_source" | grep -o 'p\?[0-9]*$' | sed 's/^p//')
if gum confirm "Setup direct boot (so snapshot booting must be done via bios)?"; then
echo "Creating EFI boot entry for $uki_file"
sudo efibootmgr --create \
--disk "$disk" \
--part "$part" \
--label "Nomarchy" \
--loader "\\EFI\\Linux\\$uki_file"
fi

View File

@@ -82,10 +82,32 @@ ref_files_per_cmd() {
# Final char must be alphanumeric — dropping trailing-dash matches like # Final char must be alphanumeric — dropping trailing-dash matches like
# `nomarchy-pkg-` that come from glob references (`for c in nomarchy-pkg-*`). # `nomarchy-pkg-` that come from glob references (`for c in nomarchy-pkg-*`).
# Restrict to grep_includes so binaries / tmpfiles don't pollute the set. # Restrict to grep_includes so binaries / tmpfiles don't pollute the set.
all_refs=$(grep -rohE 'nomarchy-[a-z0-9]([a-z0-9-]*[a-z0-9])?' \ # The middle `grep -vE` drops lines where `nomarchy-*` is a derivation /
# tmp file / sudoers basename / systemd unit / flake output / docker
# container identifier — not a shell invocation — so they don't show up
# as fake "missing" references.
all_refs=$(grep -rhE 'nomarchy-[a-z0-9]([a-z0-9-]*[a-z0-9])?' \
"${grep_includes[@]}" \ "${grep_includes[@]}" \
"${search_dirs[@]}" 2>/dev/null \ "${search_dirs[@]}" 2>/dev/null \
| grep -vE \
-e '(pname|name)[[:space:]]*=[[:space:]]*"nomarchy-' \
-e '/tmp/nomarchy-' \
-e '/etc/sudoers\.d/[^"[:space:]]*nomarchy-' \
-e 'nixosConfigurations\.nomarchy-' \
-e 'packages\.[^.]+\.nomarchy-' \
-e '\./result/bin/run-nomarchy-' \
-e 'mktemp[[:space:]]+[^|]*-t[[:space:]]+nomarchy-' \
-e '(TIMER_NAME|NOPASSWD_FILE|UNIT_NAME)=.*nomarchy-' \
-e 'docker[[:space:]]+[^|]*nomarchy-' \
| grep -oE 'nomarchy-[a-z0-9]([a-z0-9-]*[a-z0-9])?' \
| grep -vE '^(nomarchy-plymouth|nomarchy-sddm-theme|nomarchy-live|nomarchy-rev|nomarchy-windows)$' \
| sort -u) | sort -u)
# The token-level denylist above covers identifiers whose ambiguity survives
# the line filter: `nomarchy-plymouth` / `nomarchy-sddm-theme` are Nix
# derivation names referenced as bare idents in `[...]` lists,
# `nomarchy-live` is an ISO label that shows up in comments, `nomarchy-rev`
# is `/etc/nomarchy-rev` (a file written by the ISO), and
# `nomarchy-windows` is a docker container name in compose heredocs.
# --- Render: header -------------------------------------------------------- # --- Render: header --------------------------------------------------------

View File

@@ -1,10 +1,18 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -e set -e
# Nomarchy Manual Script # Open the Nomarchy docs index in the default handler.
# Opens the Nomarchy manual in the default web browser. # On an installed system the source tree lives at ~/.local/share/nomarchy/
# (see SKILL.md's "Out of Scope" section), so the README — which links every
# doc in docs/ — is the canonical "open the manual" target.
URL="https://learn.omacom.io/2/the-nomarchy-manual" README="$HOME/.local/share/nomarchy/README.md"
echo "Opening Nomarchy manual: $URL" if [[ -f "$README" ]]; then
xdg-open "$URL" echo "Opening Nomarchy manual: $README"
xdg-open "$README"
else
notify-send "Nomarchy Manual" \
"Source tree not found at $README. Try \`nomarchy-update\` to sync it."
exit 1
fi

View File

@@ -1,54 +0,0 @@
#!/usr/bin/env bash
# Nomarchy State Migration Script
# Migrates state from old locations to unified ~/.config/nomarchy/state.json
set -e
NEW_STATE_DIR="$HOME/.config/nomarchy"
NEW_STATE_FILE="$NEW_STATE_DIR/state.json"
OLD_HOME_STATE="$HOME/.config/home-manager/state.json"
OLD_SYSTEM_STATE="/etc/nixos/state.json"
mkdir -p "$NEW_STATE_DIR"
# Initialize new state file if it doesn't exist
if [[ ! -f "$NEW_STATE_FILE" ]]; then
echo "{}" > "$NEW_STATE_FILE"
fi
# Function to safely merge JSON
merge_json() {
local source="$1"
if [[ -f "$source" ]]; then
echo "Migrating from $source..."
TMP_FILE=$(mktemp)
# Merge source into new state (new state values take precedence if conflict)
jq -s '.[0] * .[1]' "$source" "$NEW_STATE_FILE" > "$TMP_FILE" && mv "$TMP_FILE" "$NEW_STATE_FILE"
fi
}
# Migrate old home-manager state
if [[ -f "$OLD_HOME_STATE" ]] && [[ "$OLD_HOME_STATE" != "$NEW_STATE_FILE" ]]; then
merge_json "$OLD_HOME_STATE"
echo "Old home state migrated. You can remove: $OLD_HOME_STATE"
fi
# Check if system state exists and user wants to sync it
if [[ -f "$OLD_SYSTEM_STATE" ]]; then
echo ""
echo "System state found at $OLD_SYSTEM_STATE"
echo "Note: System state will continue to be read from /etc/nixos/state.json"
echo " for system-level NixOS configuration."
fi
# Run the preflight migration for any legacy formats
if command -v nomarchy-preflight-migration &> /dev/null; then
nomarchy-preflight-migration
fi
echo ""
echo "Migration complete!"
echo "New state location: $NEW_STATE_FILE"
echo ""
echo "Current state:"
jq '.' "$NEW_STATE_FILE"

View File

@@ -1,25 +0,0 @@
#!/bin/bash
set -e
# Install an npx wrapper for a given npm package.
# Usage: nomarchy-npx-install <package> [command-name]
#
# If command-name is omitted, it defaults to the package name.
# Example: nomarchy-npx-install opencode-ai opencode
if [[ -z $1 ]]; then
echo "Usage: nomarchy-npx-install <package> [command-name]"
exit 1
fi
package=$1
command=${2:-$1}
mkdir -p "$HOME/.local/bin"
cat > "$HOME/.local/bin/$command" <<EOF
#!/bin/bash
exec npx --yes $package "\$@"
EOF
chmod +x "$HOME/.local/bin/$command"

View File

@@ -1,6 +0,0 @@
#!/bin/bash
set -e
# Reload hyprland configuration (used by the Nomarchy theme switching).
hyprctl reload >/dev/null

View File

@@ -1,6 +0,0 @@
#!/bin/bash
set -e
# Reload mako configuration (used by the Nomarchy theme switching).
makoctl reload

View File

@@ -1,8 +0,0 @@
#!/bin/bash
set -e
# Restart tmux if running with the latest configuration
if pgrep -x tmux; then
tmux source-file ~/.config/tmux/tmux.conf
fi

View File

@@ -1,19 +0,0 @@
#!/usr/bin/env bash
# Nomarchy Rollback Script
# Reverts the system to a previous working generation.
set -e
if command -v snapper &>/dev/null; then
echo "BTRFS Snapper detected. Listing snapshots..."
sudo snapper list
echo ""
echo "To rollback to a specific snapshot, run: sudo snapper rollback <number>"
echo "Or to rollback the current NixOS generation only:"
fi
echo "Rolling back NixOS generation..."
sudo nixos-rebuild rollback
echo "Rollback complete. Please reboot if you performed a BTRFS rollback."

View File

@@ -1,33 +0,0 @@
#!/bin/bash
set -e
COMMAND="$1"
if [[ -z $COMMAND ]]; then
echo "Usage: nomarchy-snapshot <create|restore>" >&2
exit 1
fi
if ! command -v snapper &>/dev/null; then
exit 127 # nomarchy-update can use this to just ignore if snapper is not available
fi
case "$COMMAND" in
create)
DESC="$(nomarchy-version)"
echo -e "\e[32mCreate system snapshot\e[0m"
# Get existing snapper config names from CSV output
mapfile -t CONFIGS < <(sudo snapper --csvout list-configs | awk -F, 'NR>1 {print $1}')
for config in "${CONFIGS[@]}"; do
sudo snapper -c "$config" create -c number -d "$DESC"
done
echo
;;
restore)
sudo limine-snapper-restore
;;
esac

View File

@@ -15,8 +15,20 @@ else
exit 1 exit 1
fi fi
# Apply NixOS changes from the local flake # The installer generates `nixosConfigurations.<hostname>` (see
echo "Applying system-level changes from $REPO_DIR..." # installer/install.sh: `nixosConfigurations.$HOSTNAME`), so the flake target
sudo nixos-rebuild switch --flake "$REPO_DIR#default" --impure # must match the current host. The previous `#default` literal worked only
# for a development host that happened to be named "default" and silently
# broke every toggle script (nomarchy-tz-select, nomarchy-wifi-powersave,
# nomarchy-setup-{dns,fido2,fingerprint}, nomarchy-toggle-hybrid-gpu) on a
# real install.
HOSTNAME_ATTR=$(hostname)
if [ -z "$HOSTNAME_ATTR" ]; then
echo "Error: could not determine hostname for flake attribute." >&2
exit 1
fi
echo "Applying system-level changes from $REPO_DIR#$HOSTNAME_ATTR..."
sudo nixos-rebuild switch --flake "$REPO_DIR#$HOSTNAME_ATTR"
echo "System update complete." echo "System update complete."

View File

@@ -1,12 +0,0 @@
#!/bin/bash
set -e
url="$1"
web_url="https://app.hey.com"
# Handle mailto: URLs
if [[ $url =~ ^mailto: ]]; then
email=$(echo "$url" | sed 's/mailto://')
web_url="https://app.hey.com/messages/new?to=$email"
fi
exec nomarchy-launch-webapp "$web_url"

View File

@@ -1,21 +0,0 @@
#!/bin/bash
set -e
url="$1"
web_url="https://app.zoom.us/wc/home"
if [[ $url =~ ^zoom(mtg|us):// ]]; then
confno=$(echo "$url" | sed -n 's/.*[?&]confno=\([^&]*\).*/\1/p')
if [[ -n $confno ]]; then
pwd=$(echo "$url" | sed -n 's/.*[?&]pwd=\([^&]*\).*/\1/p')
if [[ -n $pwd ]]; then
web_url="https://app.zoom.us/wc/join/$confno?pwd=$pwd"
else
web_url="https://app.zoom.us/wc/join/$confno"
fi
fi
fi
exec nomarchy-launch-webapp "$web_url"

View File

@@ -28,16 +28,25 @@ echo ""
# 1. Select initial theme # 1. Select initial theme
echo "Step 1: Choose your starting theme" echo "Step 1: Choose your starting theme"
nomarchy-theme-set "$(nomarchy-theme-list | gum filter --placeholder 'Select a theme...')" --no-update SELECTED_THEME="$(nomarchy-theme-list | gum filter --placeholder 'Select a theme...')"
if [[ -n "$SELECTED_THEME" ]]; then
THEME_ID=$(echo "$SELECTED_THEME" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')
nomarchy-theme-set "$THEME_ID" --no-update
fi
# 2. Select initial font # 2. Select initial font
echo "Step 2: Choose your preferred font" echo "Step 2: Choose your preferred font"
nomarchy-font-set "$(nomarchy-font-list | gum filter --placeholder 'Select a font...')" --no-update SELECTED_FONT="$(nomarchy-font-list | gum filter --placeholder 'Select a font...')"
if [[ -n "$SELECTED_FONT" ]]; then
nomarchy-font-set "$SELECTED_FONT" --no-update
fi
# 3. Select panel position # 3. Select panel position
echo "Step 3: Choose your preferred panel position" echo "Step 3: Choose your preferred panel position"
POSITION=$(gum choose "top" "bottom") POSITION=$(gum choose "top" "bottom")
nomarchy-state-write panelPosition "$POSITION" if [[ -n "$POSITION" ]]; then
nomarchy-state-write panelPosition "$POSITION"
fi
# Skip system-modifying steps in the Live ISO environment # Skip system-modifying steps in the Live ISO environment
if [[ "$USER" == "nixos" ]]; then if [[ "$USER" == "nixos" ]]; then
@@ -50,39 +59,9 @@ if [[ "$USER" == "nixos" ]]; then
exit 0 exit 0
fi fi
# 4. Starter home.nix # 4. Setup Local Repo (Crucial for nomarchy-env-update to work)
echo "" echo ""
echo "Step 4: Starter home.nix" echo "Step 4: Git Repository Check"
HOME_NIX="$HOME/.config/home-manager/home.nix"
if [ ! -f "$HOME_NIX" ]; then
echo "It looks like you don't have a ~/.config/home-manager/home.nix file yet."
echo "Nomarchy uses this file to manage your user-level packages and settings."
if gum confirm "Would you like to generate a starter home.nix?"; then
mkdir -p "$(dirname "$HOME_NIX")"
cat <<EOF > "$HOME_NIX"
{ pkgs, ... }:
{
# Nomarchy starter home.nix
# Add your user packages here.
home.packages = with pkgs; [
btop
fastfetch
chromium
# Add more packages here
];
# home.stateVersion = "25.11"; # Consult docs/MIGRATION.md if you change this
}
EOF
echo "Starter home.nix generated at $HOME_NIX"
fi
else
echo "Detected existing home.nix at $HOME_NIX. Skipping generation."
fi
# 5. Setup Local Repo (Crucial for nomarchy-env-update to work)
echo ""
echo "Step 5: Git Repository Check"
echo "Nomarchy relies on a local git repository for declarative updates." echo "Nomarchy relies on a local git repository for declarative updates."
if [ ! -d "/etc/nixos/.git" ]; then if [ ! -d "/etc/nixos/.git" ]; then
echo "Warning: /etc/nixos is not a git repository. Declarative updates might fail." echo "Warning: /etc/nixos is not a git repository. Declarative updates might fail."
@@ -93,7 +72,7 @@ if [ ! -d "/etc/nixos/.git" ]; then
fi fi
fi fi
# 6. Success # 5. Success
echo "" echo ""
echo "Applying all changes..." echo "Applying all changes..."
nomarchy-env-update nomarchy-env-update

View File

@@ -22,6 +22,7 @@
parted parted
btrfs-progs btrfs-progs
cryptsetup cryptsetup
mkpasswd
inputs.disko.packages.${pkgs.stdenv.hostPlatform.system}.disko inputs.disko.packages.${pkgs.stdenv.hostPlatform.system}.disko
(pkgs.makeDesktopItem { (pkgs.makeDesktopItem {
name = "install-nomarchy"; name = "install-nomarchy";
@@ -64,6 +65,15 @@
environment.etc."nomarchy".source = inputs.self; environment.etc."nomarchy".source = inputs.self;
# Embed the git revision the ISO was built from so install.sh can pin the
# generated flake to the exact same commit. `inputs.self.rev` exists only
# when the flake is built from a clean git tree; from a dirty worktree we
# fall back to dirtyRev (which won't be resolvable by `git+https`, so the
# installer treats it as "unpinned"). Empty file = unpinned.
environment.etc."nomarchy-rev".text =
if inputs.self ? rev then inputs.self.rev
else "";
# Auto-login to the graphical session # Auto-login to the graphical session
services.displayManager.autoLogin.enable = true; services.displayManager.autoLogin.enable = true;
services.displayManager.autoLogin.user = "nixos"; services.displayManager.autoLogin.user = "nixos";

View File

@@ -1,68 +0,0 @@
{
disko.devices = {
disk = {
main = {
type = "disk";
device = "@TARGET_DRIVE@";
content = {
type = "gpt";
partitions = {
ESP = {
priority = 1;
name = "ESP";
start = "1M";
end = "512M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
luks = {
size = "100%";
content = {
type = "luks";
name = "crypted";
settings.allowDiscards = true;
content = {
type = "btrfs";
extraArgs = [ "-f" ];
subvolumes = {
"@" = {
mountpoint = "/";
mountOptions = [ "compress=zstd" "noatime" ];
};
"@persist" = {
mountpoint = "/persist";
mountOptions = [ "compress=zstd" "noatime" ];
};
"@home" = {
mountpoint = "/home";
mountOptions = [ "compress=zstd" "noatime" ];
};
"@nix" = {
mountpoint = "/nix";
mountOptions = [ "compress=zstd" "noatime" ];
};
"@log" = {
mountpoint = "/var/log";
mountOptions = [ "compress=zstd" "noatime" ];
};
};
postCreateHook = ''
MNTPOINT=$(mktemp -d)
mount -t btrfs /dev/mapper/crypted $MNTPOINT
btrfs subvolume snapshot -r $MNTPOINT/@ $MNTPOINT/root-blank
umount $MNTPOINT
'';
};
};
};
};
};
};
};
};
}

View File

@@ -19,13 +19,17 @@
HARDWARE_DB=( HARDWARE_DB=(
# Framework --------------------------------------------------------------- # Framework ---------------------------------------------------------------
# Module names follow nixos-hardware's actual attrs — for Framework 13
# the per-generation modules dropped the "13-" prefix.
"Framework|Laptop 16.*AMD|isFramework=true|framework-16-7040-amd" "Framework|Laptop 16.*AMD|isFramework=true|framework-16-7040-amd"
"Framework|Laptop 13.*AMD Ryzen AI 300|isFramework=true|framework-13-amd-ai-300-series" "Framework|Laptop 16.*Ryzen AI 300|isFramework=true|framework-16-amd-ai-300-series"
"Framework|Laptop 13.*AMD Ryzen 7040|isFramework=true|framework-13-7040-amd" "Framework|Laptop 13.*Ryzen AI 300|isFramework=true|framework-amd-ai-300-series"
"Framework|Laptop 13.*13th Gen Intel|isFramework=true|framework-13-13th-gen-intel" "Framework|Laptop 13.*Ryzen 7040|isFramework=true|framework-13-7040-amd"
"Framework|Laptop 13.*12th Gen Intel|isFramework=true|framework-13-12th-gen-intel" "Framework|Laptop 13.*Core Ultra|isFramework=true|framework-intel-core-ultra-series1"
"Framework|Laptop 13.*11th Gen Intel|isFramework=true|framework-13-11th-gen-intel" "Framework|Laptop 13.*13th Gen Intel|isFramework=true|framework-13th-gen-intel"
"Framework|Laptop \(13.*|isFramework=true|framework-13-11th-gen-intel" "Framework|Laptop 13.*12th Gen Intel|isFramework=true|framework-12th-gen-intel"
"Framework|Laptop 13.*11th Gen Intel|isFramework=true|framework-11th-gen-intel"
"Framework|Laptop \(13.*|isFramework=true|framework"
# Dell XPS / Precision / Latitude ---------------------------------------- # Dell XPS / Precision / Latitude ----------------------------------------
"Dell|XPS 15 9500|isXPS=true|dell-xps-15-9500" "Dell|XPS 15 9500|isXPS=true|dell-xps-15-9500"
@@ -36,40 +40,61 @@ HARDWARE_DB=(
"Dell|XPS 13 9380|isXPS=true|dell-xps-13-9380" "Dell|XPS 13 9380|isXPS=true|dell-xps-13-9380"
"Dell|XPS 13 7390|isXPS=true|dell-xps-13-7390" "Dell|XPS 13 7390|isXPS=true|dell-xps-13-7390"
"Dell|Precision 5530|_|dell-precision-5530" "Dell|Precision 5530|_|dell-precision-5530"
"Dell|Latitude 7480|_|dell-latitude-7480" "Dell|Latitude 7490|_|dell-latitude-7490"
"Dell|Latitude 5400|_|dell-latitude-5400" "Dell|Latitude 7430|_|dell-latitude-7430"
"Dell|Latitude 7420|_|dell-latitude-7420"
# Lenovo ThinkPad -------------------------------------------------------- # Lenovo ThinkPad --------------------------------------------------------
"LENOVO|ThinkPad X1 Carbon Gen 11|_|lenovo-thinkpad-x1-carbon-gen11" # X1 Carbon: the per-gen modules are named "x1-Nth-gen", not
"LENOVO|ThinkPad X1 Carbon Gen 10|_|lenovo-thinkpad-x1-carbon-gen10" # "x1-carbon-genN" — both naming schemes appeared in nixos-hardware and
"LENOVO|ThinkPad X1 Carbon Gen 9|_|lenovo-thinkpad-x1-carbon-gen9" # the old DB references picked the wrong one. Carbon-specific quirks
# match the X1 modules because the X1 series IS the Carbon line.
"LENOVO|ThinkPad X1 Carbon Gen 11|_|lenovo-thinkpad-x1-11th-gen"
"LENOVO|ThinkPad X1 Carbon Gen 10|_|lenovo-thinkpad-x1-10th-gen"
"LENOVO|ThinkPad X1 Carbon Gen 9|_|lenovo-thinkpad-x1-9th-gen"
"LENOVO|ThinkPad X1 Carbon Gen 7|_|lenovo-thinkpad-x1-7th-gen"
"LENOVO|ThinkPad X1 Carbon Gen 6|_|lenovo-thinkpad-x1-6th-gen"
"LENOVO|ThinkPad X1 Extreme|_|lenovo-thinkpad-x1-extreme" "LENOVO|ThinkPad X1 Extreme|_|lenovo-thinkpad-x1-extreme"
"LENOVO|ThinkPad X1 Nano|_|lenovo-thinkpad-x1-nano-gen1"
"LENOVO|ThinkPad T14 Gen 3|_|lenovo-thinkpad-t14-amd-gen3" "LENOVO|ThinkPad T14 Gen 3|_|lenovo-thinkpad-t14-amd-gen3"
"LENOVO|ThinkPad T14 Gen 2|_|lenovo-thinkpad-t14-amd-gen2" "LENOVO|ThinkPad T14 Gen 2|_|lenovo-thinkpad-t14-amd-gen2"
"LENOVO|ThinkPad T14 Gen 1|_|lenovo-thinkpad-t14-amd-gen1" "LENOVO|ThinkPad T14 Gen 1|_|lenovo-thinkpad-t14-amd-gen1"
"LENOVO|ThinkPad T480|_|lenovo-thinkpad-t480" "LENOVO|ThinkPad T480|_|lenovo-thinkpad-t480"
"LENOVO|ThinkPad L13|_|lenovo-thinkpad-l13" "LENOVO|ThinkPad L13|_|lenovo-thinkpad-l13"
"LENOVO|ThinkPad P14s|_|lenovo-thinkpad-p14s" "LENOVO|ThinkPad P14s.*Gen 5|_|lenovo-thinkpad-p14s-amd-gen5"
"LENOVO|ThinkPad P14s.*Gen 4|_|lenovo-thinkpad-p14s-amd-gen4"
"LENOVO|ThinkPad P14s.*Gen 3|_|lenovo-thinkpad-p14s-amd-gen3"
# Microsoft Surface ------------------------------------------------------ # Microsoft Surface ------------------------------------------------------
# nixos-hardware ships per-chip modules, not per-revision: Intel Surface
# Pros (6 through 10) all use `microsoft-surface-pro-intel`; AMD Surface
# Laptops use `microsoft-surface-laptop-amd`. Surface Pro 9 has its own
# variant for the SQ3 / ARM model — kept separate. Surface Book and
# Intel-based Surface Laptops have no dedicated module; we fall back to
# the common detection (chassis + cpu + gpu).
"Microsoft|Surface Pro 10|_|microsoft-surface-pro-intel"
"Microsoft|Surface Pro 9|_|microsoft-surface-pro-9" "Microsoft|Surface Pro 9|_|microsoft-surface-pro-9"
"Microsoft|Surface Pro 8|_|microsoft-surface-pro-8" "Microsoft|Surface Pro 8|_|microsoft-surface-pro-intel"
"Microsoft|Surface Pro 7|_|microsoft-surface-pro-7" "Microsoft|Surface Pro 7|_|microsoft-surface-pro-intel"
"Microsoft|Surface Pro 6|_|microsoft-surface-pro-6" "Microsoft|Surface Pro 6|_|microsoft-surface-pro-intel"
"Microsoft|Surface Laptop 5|_|microsoft-surface-laptop-5" "Microsoft|Surface Pro 3|_|microsoft-surface-pro-3"
"Microsoft|Surface Laptop 4|_|microsoft-surface-laptop-4" "Microsoft|Surface Laptop.*Ryzen|_|microsoft-surface-laptop-amd"
"Microsoft|Surface Laptop 3|_|microsoft-surface-laptop-3"
"Microsoft|Surface Go|_|microsoft-surface-go" "Microsoft|Surface Go|_|microsoft-surface-go"
"Microsoft|Surface Book 3|_|microsoft-surface-book-3"
"Microsoft|Surface Book 2|_|microsoft-surface-book-2"
# ASUS ROG / Zephyrus ---------------------------------------------------- # ASUS ROG Ally / Zephyrus / Strix --------------------------------------
"ASUS.*|ROG Zephyrus G14.*2024|_|asus-zephyrus-ga403" # Ally is the handheld (Z1 Extreme / Z1). RC71L is the original Ally,
"ASUS.*|ROG Zephyrus G14.*2023|_|asus-zephyrus-ga402" # RC72LA is the Ally X — both share the same nixos-hardware module
"ASUS.*|ROG Zephyrus G14.*2021|_|asus-zephyrus-ga401" # currently.
"ASUS.*|RC71L|_|asus-ally-rc71l"
"ASUS.*|RC72LA|_|asus-ally-rc71l"
"ASUS.*|ROG Ally|_|asus-ally-rc71l"
"ASUS.*|ROG Zephyrus G14.*GA402X|_|asus-zephyrus-ga402x"
"ASUS.*|ROG Zephyrus G14.*GA402|_|asus-zephyrus-ga402"
"ASUS.*|ROG Zephyrus G14.*GA401|_|asus-zephyrus-ga401"
"ASUS.*|ROG Zephyrus G15|_|asus-zephyrus-ga503" "ASUS.*|ROG Zephyrus G15|_|asus-zephyrus-ga503"
"ASUS.*|ROG Strix G15|_|asus-rog-strix-g513" "ASUS.*|ROG Zephyrus.*GU603|_|asus-zephyrus-gu603h"
"ASUS.*|Zenbook UX|_|asus-zenbook-ux" "ASUS.*|ROG Strix G513|_|asus-rog-strix-g513im"
"ASUS.*|ROG Strix G533|_|asus-rog-strix-g533zw"
# Apple (T2 Intel; M-series falls back to asahi elsewhere) --------------- # Apple (T2 Intel; M-series falls back to asahi elsewhere) ---------------
"Apple.*|MacBookPro15|isT2Mac=true|apple-t2" "Apple.*|MacBookPro15|isT2Mac=true|apple-t2"
@@ -86,9 +111,14 @@ HARDWARE_DB=(
"System76|Galago Pro.*|_|system76" "System76|Galago Pro.*|_|system76"
"System76|Pangolin.*|_|system76" "System76|Pangolin.*|_|system76"
# Raspberry Pi (ARM) — listed for completeness; installer is x86_64 only - # Devices nixos-hardware doesn't cover (yet) -----------------------------
# "Raspberry Pi|Raspberry Pi 5|_|raspberry-pi-5" # Listed here so future contributors know they're known-unsupported, not
# "Raspberry Pi|Raspberry Pi 4|_|raspberry-pi-4" # accidentally missing:
# - Valve Steam Deck (Galileo / Jupiter): try Jovian-NixOS as a flake
# input instead — separate ecosystem, not in nixos-hardware.
# - Snapdragon X laptops (Surface Pro 11, Lenovo Yoga Slim 7x, …):
# aarch64-only and the Nomarchy installer is x86_64 only.
# - Raspberry Pi (ARM): same — installer is x86_64 only.
) )
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------

View File

@@ -32,6 +32,7 @@ TARGET_DRIVE=""
USERNAME="" USERNAME=""
LUKS_PASSWORD="" LUKS_PASSWORD=""
USER_PASSWORD="" USER_PASSWORD=""
USER_PASSWORD_HASH=""
TIMEZONE="UTC" TIMEZONE="UTC"
KEYMAP_LAYOUT="" KEYMAP_LAYOUT=""
KEYMAP_VARIANT="" KEYMAP_VARIANT=""
@@ -73,6 +74,9 @@ Usage: install.sh [--dry-run] [--resume] [-h|--help]
Doesn't touch the disk, doesn't run nixos-install. Doesn't touch the disk, doesn't run nixos-install.
--resume Reuse answers from a previous interrupted run --resume Reuse answers from a previous interrupted run
(saved at $STATE_FILE — passwords excluded). (saved at $STATE_FILE — passwords excluded).
NOTE: the live ISO uses tmpfs, so the state file is lost
on reboot. --resume only works within the same live-ISO
session as the original interrupted run.
-h, --help Print this message. -h, --help Print this message.
USAGE USAGE
} }
@@ -92,20 +96,83 @@ parse_args() {
# Persist non-secret answers so an interrupted install can pick up where it # Persist non-secret answers so an interrupted install can pick up where it
# left off. Uses `declare -p` so each line is a self-contained `declare --` # left off. Uses `declare -p` so each line is a self-contained `declare --`
# statement that `source` re-establishes verbatim. # statement that `source` re-establishes verbatim.
#
# USER_PASSWORD_HASH is intentionally NOT persisted, even though a SHA-512
# crypt hash isn't reversible. The contract is: after --resume, the password
# prompt re-runs. configure_user's early-return guard at the top of the
# function checks `[[ -n "$USER_PASSWORD_HASH" ]]` for exactly this reason —
# if you ever change that guard to skip on USERNAME+HOSTNAME alone, --resume
# will silently install a system with an empty password hash and lock the
# user out. Keep the guard checking the hash.
save_state() { save_state() {
declare -p \ # The leading timestamp lets --resume surface "how old is this state?"
TARGET_DRIVE USERNAME HOSTNAME TIMEZONE \ # and is parsed back via NOMARCHY_INSTALL_STATE_SAVED_AT.
KEYMAP_LAYOUT KEYMAP_VARIANT LOCALE FORM_FACTOR \ {
ENABLE_IMPERMANENCE HARDWARE_MODULES NOMARCHY_HW_OPTS \ echo "NOMARCHY_INSTALL_STATE_SAVED_AT=\"$(date -Iseconds)\""
SELECTED_PROFILES NOMARCHY_REV \ declare -p \
> "$STATE_FILE" TARGET_DRIVE USERNAME HOSTNAME TIMEZONE \
KEYMAP_LAYOUT KEYMAP_VARIANT LOCALE FORM_FACTOR \
ENABLE_IMPERMANENCE HARDWARE_MODULES NOMARCHY_HW_OPTS \
SELECTED_PROFILES NOMARCHY_REV
} > "$STATE_FILE"
}
# Pretty-print "X minutes/hours/days ago" from an ISO-8601 timestamp.
# Falls back to the raw string if `date -d` can't parse it (defensive —
# the timestamp is always produced by `date -Iseconds` above, but we
# don't want a stale state file to crash --resume).
format_age() {
local saved="$1" saved_epoch now_epoch diff
saved_epoch=$(date -d "$saved" +%s 2>/dev/null) || { echo "$saved"; return; }
now_epoch=$(date +%s)
diff=$(( now_epoch - saved_epoch ))
if (( diff < 60 )); then echo "${diff}s ago"
elif (( diff < 3600 )); then echo "$((diff / 60))m ago"
elif (( diff < 86400 )); then echo "$((diff / 3600))h $((diff % 3600 / 60))m ago"
else echo "$((diff / 86400))d ago"
fi
} }
load_state() { load_state() {
if [[ "$RESUME" == "true" ]] && [[ -f "$STATE_FILE" ]]; then if [[ "$RESUME" != "true" ]]; then
# shellcheck disable=SC1090 return
source "$STATE_FILE" fi
info "Resumed from $STATE_FILE"
# --resume with no state file is almost always operator error — the
# most common cause is "rebooted the live ISO and forgot tmpfs eats
# /tmp/". Fail loudly so the user doesn't sit through a fresh prompt
# cycle thinking it was resumed.
if [[ ! -f "$STATE_FILE" ]]; then
error "--resume was passed but no saved state exists at $STATE_FILE."
info "The live ISO uses tmpfs — saved state doesn't survive a reboot."
info "Re-run install.sh without --resume to start fresh."
exit 1
fi
# shellcheck disable=SC1090
source "$STATE_FILE"
# If the saved target drive isn't visible right now, every later
# disk-phase step will fail with cryptic errors. Catch it here.
# Live ISOs frequently get their non-boot USB sticks unplugged
# between sessions, and dev hosts sometimes have non-deterministic
# /dev/sdX numbering.
if [[ -n "${TARGET_DRIVE:-}" ]] && [[ ! -b "$TARGET_DRIVE" ]]; then
error "Saved target drive $TARGET_DRIVE is no longer a block device."
info "The drive may have been unplugged or renamed since the saved run."
info "Delete $STATE_FILE and re-run without --resume."
exit 1
fi
# Show what we're resuming into so the user can Ctrl-C if they're on
# the wrong host before any password / disk-wipe prompts fire.
local age="unknown age"
if [[ -n "${NOMARCHY_INSTALL_STATE_SAVED_AT:-}" ]]; then
age=$(format_age "$NOMARCHY_INSTALL_STATE_SAVED_AT")
fi
info "Resumed from $STATE_FILE (saved $age)"
if [[ -n "${USERNAME:-}" || -n "${HOSTNAME:-}" || -n "${TARGET_DRIVE:-}" ]]; then
info " Target: ${TARGET_DRIVE:-?}${USERNAME:-?} @ ${HOSTNAME:-?}"
fi fi
} }
@@ -128,7 +195,7 @@ clear_step_state() {
case "$1" in case "$1" in
select_disk) TARGET_DRIVE="" ;; select_disk) TARGET_DRIVE="" ;;
get_luks_passphrase) LUKS_PASSWORD="" ;; get_luks_passphrase) LUKS_PASSWORD="" ;;
configure_user) USERNAME=""; HOSTNAME=""; USER_PASSWORD="" ;; configure_user) USERNAME=""; HOSTNAME=""; USER_PASSWORD=""; USER_PASSWORD_HASH="" ;;
select_keymap_locale) KEYMAP_LAYOUT=""; KEYMAP_VARIANT=""; LOCALE="" ;; select_keymap_locale) KEYMAP_LAYOUT=""; KEYMAP_VARIANT=""; LOCALE="" ;;
select_timezone) TIMEZONE="" ;; select_timezone) TIMEZONE="" ;;
select_hardware) HARDWARE_MODULES=""; NOMARCHY_HW_OPTS="" ;; select_hardware) HARDWARE_MODULES=""; NOMARCHY_HW_OPTS="" ;;
@@ -206,13 +273,32 @@ check_environment() {
# Capture the exact commit we're installing from. The generated flake # Capture the exact commit we're installing from. The generated flake
# pins `nomarchy.url` to this revision so the installed system can't # pins `nomarchy.url` to this revision so the installed system can't
# silently drift onto a newer (possibly breaking) main. # silently drift onto a newer (possibly breaking) main.
if command -v git >/dev/null 2>&1 && [[ -d "$NOMARCHY_REPO/.git" ]]; then #
# Three sources, in priority order:
# 1. /etc/nomarchy-rev — written at ISO build time from `inputs.self.rev`
# (the only source that works on a normal live-ISO install, because
# `inputs.self` strips .git from the Nix store copy at /etc/nomarchy).
# 2. `git rev-parse HEAD` in the repo — works when running the installer
# from a dev checkout instead of the live ISO.
# 3. Empty → unpinned, user gets a loud confirmation prompt below.
if [[ -z "$NOMARCHY_REV" ]] && [[ -f /etc/nomarchy-rev ]]; then
NOMARCHY_REV=$(tr -d '[:space:]' < /etc/nomarchy-rev)
fi
if [[ -z "$NOMARCHY_REV" ]] && command -v git >/dev/null 2>&1 && [[ -d "$NOMARCHY_REPO/.git" ]]; then
NOMARCHY_REV=$(git -C "$NOMARCHY_REPO" rev-parse HEAD 2>/dev/null || echo "") NOMARCHY_REV=$(git -C "$NOMARCHY_REPO" rev-parse HEAD 2>/dev/null || echo "")
fi fi
if [[ -n "$NOMARCHY_REV" ]]; then if [[ -n "$NOMARCHY_REV" ]]; then
success "Pinning Nomarchy to $NOMARCHY_REV" success "Pinning Nomarchy to $NOMARCHY_REV"
else else
info "Could not determine Nomarchy revision; downstream flake will track main." error "Could not determine Nomarchy revision."
info "The installed system would silently track upstream main, which"
info "defeats the point of locking inputs at install time."
if [[ "$DRY_RUN" != "true" ]]; then
if ! nrun gum confirm --default=false \
"Continue anyway with an unpinned (tracking main) configuration?"; then
exit 1
fi
fi
fi fi
# Check internet # Check internet
@@ -452,8 +538,8 @@ configure_user() {
section "User Configuration" section "User Configuration"
if [[ -n "$USERNAME" && -n "$HOSTNAME" ]]; then if [[ -n "$USERNAME" && -n "$HOSTNAME" ]]; then
# Password check skipped in dry run or if already set # Password check skipped in dry run or if already hashed in this session.
if [[ "$DRY_RUN" == "true" ]] || [[ -n "$USER_PASSWORD" ]]; then if [[ "$DRY_RUN" == "true" ]] || [[ -n "$USER_PASSWORD_HASH" ]]; then
success "User $USERNAME @ $HOSTNAME configured" success "User $USERNAME @ $HOSTNAME configured"
return 0 return 0
fi fi
@@ -493,6 +579,9 @@ configure_user() {
if [[ "$DRY_RUN" == "true" ]]; then if [[ "$DRY_RUN" == "true" ]]; then
info "Dry run: skipping user password prompt." info "Dry run: skipping user password prompt."
USER_PASSWORD="dryrun-not-used" USER_PASSWORD="dryrun-not-used"
# Stable placeholder hash so generated system.nix still parses as Nix.
# Never lands on a real install — dry-run skips nixos-install.
USER_PASSWORD_HASH='$6$dryrun$3xxK3aQ.0bGcv0fM2RhV4Q9oN3p1mYxz5kSjQ.bC8tZpZ7QnFv2cN0Yhd5lDqJ8X9mP2K1L0vR6BqWqzNk7Yo/'
save_state save_state
return return
fi fi
@@ -521,6 +610,20 @@ configure_user() {
fi fi
done done
# Hash now so we never have to embed the cleartext in /etc/nixos/system.nix
# (where it would be world-readable and break Nix parsing on quotes/backslash).
# mkpasswd is added to the live ISO via hosts/nomarchy-live.nix.
if ! command -v mkpasswd >/dev/null 2>&1; then
error "mkpasswd not found on the live ISO — cannot hash the user password."
exit 1
fi
USER_PASSWORD_HASH=$(printf '%s' "$USER_PASSWORD" | mkpasswd -m sha-512 -s)
if [[ -z "$USER_PASSWORD_HASH" ]]; then
error "mkpasswd produced an empty hash."
exit 1
fi
unset pass1 pass2 USER_PASSWORD
success "User password set" success "User password set"
save_state save_state
} }
@@ -1247,7 +1350,7 @@ execute_installation() {
mkdir -p /mnt/persist/etc mkdir -p /mnt/persist/etc
mv /mnt/etc/nixos /mnt/persist/etc/ mv /mnt/etc/nixos /mnt/persist/etc/
mkdir -p /mnt/etc mkdir -p /mnt/etc
ln -s /persist/etc/nixos /mnt/etc/nixos ln -s ../persist/etc/nixos /mnt/etc/nixos
success "Impermanence configured" success "Impermanence configured"
fi fi
@@ -1346,9 +1449,17 @@ EOF
# ============================================================================ # ============================================================================
generate_flake_config() { generate_flake_config() {
# Impermanence must mount the same /dev/mapper name that disko created.
# disko-config.nix uses "crypted" for single-disk and "crypted_main" once
# extraDrives is non-empty — keep these in sync.
local impermanence_opt="" local impermanence_opt=""
if [[ "$ENABLE_IMPERMANENCE" == "true" ]]; then if [[ "$ENABLE_IMPERMANENCE" == "true" ]]; then
impermanence_opt="nomarchy.system.impermanence.enable = true;" local _main_luks_name="crypted"
local _drives=($TARGET_DRIVE)
if (( ${#_drives[@]} > 1 )); then
_main_luks_name="crypted_main"
fi
impermanence_opt=$'nomarchy.system.impermanence.enable = true;\n nomarchy.system.impermanence.mainLuksName = "'"$_main_luks_name"$'";\n nomarchy.system.impermanence.user = "'"$USERNAME"$'";'
fi fi
local PROFILE_SYSTEM_OPTS="" local PROFILE_SYSTEM_OPTS=""
@@ -1534,7 +1645,9 @@ $xkb_variant_line
users.users."$USERNAME" = { users.users."$USERNAME" = {
isNormalUser = true; isNormalUser = true;
initialPassword = "$USER_PASSWORD"; # SHA-512 crypt hash generated by mkpasswd during install. Cleartext
# never touches /etc/nixos. Change later with \`passwd\`.
initialHashedPassword = "$USER_PASSWORD_HASH";
extraGroups = [ "networkmanager" "wheel" "video" "audio" "render" ]; extraGroups = [ "networkmanager" "wheel" "video" "audio" "render" ];
}; };
@@ -1685,6 +1798,30 @@ EOF
# Extra Home Manager modules go here (program configs, services, etc.). # Extra Home Manager modules go here (program configs, services, etc.).
} }
EOF EOF
# state.json — consumed by core/system/state.nix at every nixos-rebuild
# and mutated by toggle scripts (nomarchy-tz-select, nomarchy-setup-fido2,
# nomarchy-toggle-hybrid-gpu, nomarchy-wifi-powersave, ...). Those scripts
# `jq` the file in place and fail hard if it doesn't exist or isn't
# valid JSON, so a fresh install MUST ship one.
#
# Source of truth: lib/state-schema.nix. We eval its `system` block and
# overlay the installer-chosen timezone, so this generator no longer
# drifts from the schema or the *.nix consumers — the previous heredoc
# was the last source-of-truth split after the state centralization.
# Adding a new default in the schema now reaches the installer without
# any further plumbing.
local _state_tz="${TIMEZONE:-UTC}"
nix --extra-experimental-features 'nix-command flakes' 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
} }
# ============================================================================ # ============================================================================
@@ -1712,8 +1849,18 @@ finish() {
echo " variant. Theme switches after that are instant (no rebuild)." echo " variant. Theme switches after that are instant (no rebuild)."
echo "" echo ""
# Unmount /mnt before either reboot or returning to the live shell:
# - Reboot: clean unmount avoids dirty BTRFS, which would otherwise
# force a longer first-boot fsck/replay.
# - Decline: leaving /mnt mounted blocks a second `install.sh` run on
# the same live ISO (disko refuses to wipe a busy device).
# `-R` recursively unmounts /mnt/boot, /mnt/home, /mnt/nix, etc.; the
# `|| true` absorbs the case where /mnt was already torn down.
if nrun gum confirm "Reboot now?"; then if nrun gum confirm "Reboot now?"; then
umount -R /mnt 2>/dev/null || true
reboot reboot
else
umount -R /mnt 2>/dev/null || true
fi fi
} }

View File

@@ -98,7 +98,6 @@ let
in { in {
inherit inherit
palettes palettes
readState
readHomeState readHomeState
readSystemState readSystemState
resolveWallpaper resolveWallpaper

View File

@@ -1,12 +1,20 @@
# Nomarchy State Schema # Nomarchy State Schema
# Defines the complete state shape with defaults for both home and system state #
# Defines the default values for every state.json field that's consumed by a
# Nix option. Read by core/{system,home}/options.nix (for `default = …`) and
# by core/{system,home}/state.nix (for `or` fallbacks).
#
# state.json may also hold runtime-only fields that aren't declared here —
# notably `welcome_done`, managed by `nomarchy-welcome`. Those are intentionally
# off-schema because no Nix option reads them; the schema is the "consumed by
# Nix" surface, not the full state.json shape.
{ lib }: { lib }:
{ {
# Home state defaults (user preferences) # Home state defaults (user preferences)
home = { home = {
# Theme and appearance # Theme and appearance
theme = "nord"; theme = "summer-night";
wallpaper = ""; wallpaper = "";
font = "JetBrainsMono Nerd Font"; font = "JetBrainsMono Nerd Font";
panelPosition = "top"; panelPosition = "top";
@@ -18,7 +26,6 @@
idle = true; idle = true;
nightlight = false; nightlight = false;
waybar = true; waybar = true;
skipVsCodeTheme = false;
# Hyprland window manager settings # Hyprland window manager settings
hyprland = { hyprland = {
@@ -31,7 +38,7 @@
# System state defaults (system-level configuration) # System state defaults (system-level configuration)
system = { system = {
# Theme (can differ from home for system-level theming) # Theme (can differ from home for system-level theming)
theme = "nord"; theme = "summer-night";
# Timezone # Timezone
timezone = "UTC"; timezone = "UTC";
@@ -50,18 +57,7 @@
fingerprint = false; fingerprint = false;
fido2 = false; fido2 = false;
hybridGPU = false; hybridGPU = false;
makima = false;
}; };
}; };
# Get a value from state with fallback to default
getWithDefault = state: path: default:
let
pathList = lib.splitString "." path;
getValue = obj: remaining:
if remaining == [] then obj
else if builtins.isAttrs obj && builtins.hasAttr (builtins.head remaining) obj
then getValue obj.${builtins.head remaining} (builtins.tail remaining)
else default;
in getValue state pathList;
} }

View File

@@ -83,6 +83,12 @@ in
path = ../palettes; path = ../palettes;
}; };
# Expose all theme templates to the system via local share
xdg.dataFile."nomarchy/templates".source = builtins.path {
name = "nomarchy-templates";
path = ../templates;
};
# Nautilus python extensions # Nautilus python extensions
xdg.dataFile."nautilus-python/extensions/localsend.py".source = ../../core/home/config/nautilus-python/extensions/localsend.py; xdg.dataFile."nautilus-python/extensions/localsend.py".source = ../../core/home/config/nautilus-python/extensions/localsend.py;
} }

View File

@@ -54,31 +54,16 @@ in
}; };
apps = { apps = {
# waybar, kitty, alacritty, and mako are intentionally absent. Waybar
# themes inline in features/desktop/waybar via colorScheme; kitty and
# alacritty are themed by stylix targets (themes/engine/stylix.nix); mako
# has no theme integration yet. Only btop is loaded from the active
# theme's apps/ directory.
btop = lib.mkOption { btop = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = true; default = true;
description = "Whether to load btop theme from active theme."; description = "Whether to load btop theme from active theme.";
}; };
waybar = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to load waybar CSS from active theme.";
};
mako = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to load mako config from active theme.";
};
kitty = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to load kitty config from active theme.";
};
alacritty = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to load alacritty config from active theme.";
};
}; };
}; };

View File

@@ -40,8 +40,7 @@ let
for file in $out/bin/*; do for file in $out/bin/*; do
if [ -f "$file" ]; then if [ -f "$file" ]; then
wrapProgram "$file" \ wrapProgram "$file" \
--prefix PATH : "$deps" \ --prefix PATH : "$deps"
--set NOMARCHY_PATH "/etc/nixos"
fi fi
done done
''; '';

View File

@@ -2,8 +2,8 @@
set -e set -e
{ {
find ~/.config/nomarchy/themes/ -mindepth 1 -maxdepth 1 \( -type d -o -type l \) -printf '%f\n' find ~/.config/nomarchy/themes/ -mindepth 1 -maxdepth 1 \( -type d -o -type l \) -printf '%f\n' 2>/dev/null || true
find "$NOMARCHY_PATH/themes/palettes/" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' find ~/.local/share/nomarchy/themes/ -mindepth 1 -maxdepth 1 -type d -printf '%f\n' 2>/dev/null || true
} | sort -u | while read -r name; do } | sort -u | while read -r name; do
echo "$name" | sed -E 's/(^|-)([a-z])/\1\u\2/g; s/-/ /g' echo "$name" | sed -E 's/(^|-)([a-z])/\1\u\2/g; s/-/ /g'
done done

View File

@@ -0,0 +1,24 @@
#!/bin/bash
set -e
# Cycles to the next available theme (wraps from last back to first).
mapfile -t THEMES < <(nomarchy-theme-list)
TOTAL=${#THEMES[@]}
if (( TOTAL == 0 )); then
notify-send "No themes available"
exit 1
fi
CURRENT=$(nomarchy-theme-current)
INDEX=-1
for i in "${!THEMES[@]}"; do
if [[ "${THEMES[$i]}" == "$CURRENT" ]]; then
INDEX=$i
break
fi
done
NEXT_INDEX=$(((INDEX + 1) % TOTAL))
nomarchy-theme-set "${THEMES[$NEXT_INDEX]}"

View File

@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
set -e set -e
TEMPLATES_DIR="$NOMARCHY_PATH/themes/templates" TEMPLATES_DIR="$HOME/.local/share/nomarchy/templates"
USER_TEMPLATES_DIR="$HOME/.config/nomarchy/themes/templates" USER_TEMPLATES_DIR="$HOME/.config/nomarchy/themes/templates"
NEXT_THEME_DIR="$HOME/.config/nomarchy/current/theme" NEXT_THEME_DIR="$HOME/.config/nomarchy/current/theme"
COLORS_FILE="$NEXT_THEME_DIR/colors.toml" COLORS_FILE="$NEXT_THEME_DIR/colors.toml"

View File

@@ -27,8 +27,12 @@ in
services.displayManager.defaultSession = lib.mkDefault "hyprland-uwsm"; services.displayManager.defaultSession = lib.mkDefault "hyprland-uwsm";
# autoLogin defaults off so hand-migrated configs (no installer-written
# username) don't try to log in as a nonexistent "nomarchy" user. The
# installer-generated system.nix sets both `enable = true;` and
# `user = "$USERNAME";` at normal priority, overriding these defaults.
services.displayManager.autoLogin = { services.displayManager.autoLogin = {
enable = lib.mkDefault true; enable = lib.mkDefault false;
user = lib.mkDefault "nomarchy"; user = lib.mkDefault "nomarchy";
}; };

View File

@@ -1,5 +0,0 @@
include=~/.config/nomarchy/default/mako/core.ini
text-color={{ foreground }}
border-color={{ accent }}
background-color={{ background }}