Compare commits

...

61 Commits

Author SHA1 Message Date
Bernardo Magri
4de8afbea9 docs(roadmap): close out Pillar 8 / Component 6 + log VSCode marketplace gap
Component 6 (Apps) closeout entry covers the three inline theming
fixes (kitty/ghostty per-palette colors, btop color_theme name match,
VSCode theme extensions split). One new Later row tracks the 15
palettes — including the default summer-night (sainnhe.everforest) —
whose theme extensions aren't in nixpkgs and need
pkgs.vscode-utils.extensionFromVscodeMarketplace per-palette pinning.

Chromium's static Default/Preferences symlink was confirmed redundant
with the managed-policy intent in core/system/browser.nix, but left
under the existing Later entry (the user already hedged on deletion
without chromium-internals confirmation).
2026-05-21 20:32:38 +01:00
Bernardo Magri
577b3aeb91 fix(vscode): always install palette theme extensions
programs.vscode.profiles.default.userSettings.workbench.colorTheme is
set unconditionally to the active palette's theme name (read from
themes/palettes/<theme>/apps/vscode.json), but the matching theme
extensions were bundled with devExtensions — which defaults to false.
So out of the box, VSCode silently fell back to the built-in dark
theme on every palette.

Split themeExtensions out as always-installed and devExtensions as
opt-in via nomarchy.vscode.devExtensions. themeExtensions covers the 6
palettes whose VSCode theme is packaged in nixpkgs (catppuccin,
catppuccin-latte, nord, tokyo-night, rose-pine, gruvbox).

The other 15 palettes (including the default summer-night, which uses
sainnhe.everforest) still break because their theme extensions are on
the VSCode marketplace but not yet in nixpkgs — handling that needs
pkgs.vscode-utils.extensionFromVscodeMarketplace plus per-palette
publisher/name/version/sha256 metadata. Logged separately.
2026-05-21 20:31:50 +01:00
Bernardo Magri
b52aec28ce fix(btop): point color_theme at the deployed nomarchy.theme
themes/engine/loader.nix:72 deploys the active palette's btop theme to
~/.config/btop/themes/nomarchy.theme, but btop.conf had
color_theme = "current" — btop looked for themes/current.theme, didn't
find it, and silently fell back to the built-in Default theme. So every
palette rendered btop in the same default colors regardless of the
selected Nomarchy theme.

Renamed the config reference to match the deployed file name.

lazygit and tmux both inherit terminal ANSI colors (verified: the tmux
status bar config uses blue/brightblack/etc., not hex), so the kitty +
ghostty + alacritty theming changes from 8d3ce2d cover them
transitively — no module fix needed.
2026-05-21 20:27:12 +01:00
Bernardo Magri
8d3ce2d841 fix(theme): generate per-palette kitty.conf + ghostty.conf
features/apps/kitty/config/kitty.conf:1 contains
  include ~/.config/nomarchy/current/theme/kitty.conf
and features/apps/ghostty/config/config:2 contains
  config-file = ?"~/.config/nomarchy/current/theme/ghostty.conf"

Neither file existed for any of the 22 palettes. The kitty include
failed silently and the ghostty include is optional (?-prefix), so both
terminals rendered with built-in default colors regardless of the
active Nomarchy theme.

Stylix has kitty.enable = true in themes/engine/stylix.nix but the
kitty module uses xdg.configFile rather than programs.kitty, so the
Stylix target had nothing to hook into. ghostty has no Stylix target at
all.

Generated both files from the active palette's base16 colors in
themes/engine/files.nix, mirroring the waybar.css pattern already there.
Color mapping reproduces the original colors.toml fields (background,
foreground, cursor, selection_*, color0..15) via base16 indices —
verified against the inverse mapping in themes/palettes/default.nix.

themes/palettes/summer-day/apps/kitty/kitty.conf (a 76KB stray file in
the wrong tree location) is unaffected by this fix — it was already
dead surface since the include path never resolved to it.
2026-05-21 20:26:13 +01:00
Bernardo Magri
0486e037df docs(roadmap): close out Pillar 8 / Component 5 + log hyprsunset finding
Pillar 8 Component 5 (Desktop stack) closeout entry covers the five inline
fixes from this sweep (hyprland apps.conf wiring, NNOMARCHY typo pair,
broken waybar palette overrides, dead bindings files, mako post-fix verify)
plus the doc reconciles (SCRIPTS.md, KEYBINDINGS.md). One new Later row
captures the nightlight/hyprsunset toggle-vs-systemd inconsistency — Nix
always enables the systemd unit with a rebuild-time temperature while the
toggle script pkills/exec's hyprsunset directly and hardcodes 4000K
instead of reading nightlightTemperature.

Runtime verification (boot live ISO; eyeball waybar across panel
positions × form factors × all 22 palettes, walker launcher modes, the
screensaver at idle) remains on the user before declaring Component 5
fully closed.
2026-05-21 20:19:31 +01:00
Bernardo Magri
40b62124e6 fix(scripts): correct \$NNOMARCHY_TOGGLE_* typos breaking 2 toggles
nomarchy-menu:330 and nomarchy-launch-screensaver:16 referenced
\$NNOMARCHY_TOGGLE_SUSPEND and \$NNOMARCHY_TOGGLE_SCREENSAVER with a
double-N. The real env vars injected by features/scripts/default.nix:69-73
are single-N. Both reads always resolved to the empty string, so:

  - nomarchy.toggles.suspend = false; still showed "Suspend" in the
    system menu (the condition is "!= false", so empty != false → true).
  - nomarchy.toggles.screensaver = false; still launched the screensaver
    on hypridle's 150s timeout (the gate "== false" never hit on empty,
    so the early-exit was skipped).

Both toggles documented in docs/OPTIONS.md were vacuous in practice.
2026-05-21 20:13:34 +01:00
Bernardo Magri
72443fd69f fix(waybar): drop 4 broken per-palette style.css overrides
themes/{catppuccin,lumon,nord,retro-82}/style.css fully replaced the
default style.css (no @import) but defined only 2–14 lines — just
@define-color declarations and, for nord, a minimal window#waybar block.
The default style ships ~110 lines covering #workspaces, #tray, #cpu,
#custom-nomarchy's Nomarchy-font override, margins/padding, the indicator
.active states, etc. So picking any of those four palettes produced a
waybar with zero structural styling.

The default style at features/desktop/waybar/config/style.css already
@imports ../nomarchy/current/theme/waybar.css — which themes/engine/
files.nix:30-34 generates with @background/@foreground/@accent from the
active palette. So removing the broken overrides restores per-palette
colors via the default-style path. summer-day and summer-night are kept
because their 100+-line style.css files are intentional, self-contained
visual redesigns (the case the themes/engine/loader.nix:76-79 comment
explicitly carves out for "themes that need significantly different
styling").
2026-05-21 20:07:25 +01:00
Bernardo Magri
641ab0cfb0 chore(hyprland): drop two dead bindings files in default/hypr/
bindings.conf was explicitly labeled "Deprecated bindings file. New
installations include everything directly." and was sourced by nothing.
plain-bindings.conf referenced \$terminal/\$browser/\$fileManager/\$music/
\$messenger/\$passwordManager — Hyprland variables that aren't defined
anywhere in the loaded config tree — and was likewise sourced by nothing.
Both files have been superseded by features/desktop/hyprland/config/
bindings.conf (the user-overrideable, mkDefault-deployed one at
~/.config/hypr/bindings.conf) and the entries already inside
default/hypr/bindings/{utilities,media,clipboard,tiling-v2}.conf.

Regenerated docs/SCRIPTS.md; the diff drops the stale plain-bindings.conf
callers and incidentally corrects four scripts whose Origin column was
out of date after they moved from core/system/scripts/ to
features/scripts/utils/ (nomarchy-env-update, nomarchy-pkg-add,
nomarchy-pkg-remove, nomarchy-preflight-migration).
2026-05-21 20:03:09 +01:00
Bernardo Magri
0297ec268f fix(hyprland): source the 9 unwired window-rule files in apps.conf
9 of the 17 .conf files under core/home/config/nomarchy/default/hypr/apps/
were deployed but never sourced. apps/system.conf carried the load-bearing
"tag +floating-window" rule that the bluetui/impala/btop/satty/screensaver
classes rely on, plus the "fullscreen, class:org.nomarchy.screensaver" rule
hypridle's 150s on-timeout depends on. apps/pip.conf carried the picture-
in-picture pin/float/size rules. Neither set of rules fired today — the
screensaver came up tiled, PiP windows didn't pin, and the floating-window
helpers shipped degraded.

Sourcing all 17 unconditionally; every rule is class- or title-gated so
the conditional ones (steam, qemu, 1password, etc.) no-op cleanly when the
app isn't installed — same pattern as the already-sourced telegram /
retroarch / localsend entries.
2026-05-21 20:00:30 +01:00
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
Bernardo Magri
c1895eefd4 feat: implement Pillar 8: Distro Branding
Scrubbed remaining upstream references and solidified Nomarchy's identity:
- Replaced 'Omarchy' and 'Spirit of Omarchy' with brand-independent terms in README.md and scripts.
- Updated nomarchy-welcome banner to 'The Professional NixOS Desktop'.
- Set nomarchy-version codename to 'Sovereign'.
- Verified core/system/branding.nix for OS-release and bootloader labels.
- Verified SDDM and Plymouth metadata for correct branding.
- Updated ROADMAP.md board.
2026-05-04 22:08:27 +01:00
Bernardo Magri
74e2dc34e3 chore: complete OOTB QA audit
Fixes identified during the thorough distro review:
- Restore automatic wallpaper switching by removing image filters from deployed themes.
- Fix broken 'Style' menu entries by creating missing about.txt and screensaver.txt branding files.
- Clean up conflicting keybindings by removing deprecated tiling.conf and updating doc generator.
- Remove legacy Nord theme hack from nomarchy-theme-set.
- Fix JSON parse error in summer-day waybar theme.
2026-05-04 21:44:31 +01:00
Bernardo Magri
3510a51492 fix(installer): resolve multi-disk LUKS/BTRFS boot hang
- Move temporary LUKS keyfile to /tmp/ so Disko omits it from runtime config
- Explicitly add x-systemd.requires and x-systemd.device-timeout=0 to BTRFS mount options
- Ensures all LUKS devices are decrypted before BTRFS attempts to mount
2026-05-03 09:13:02 +01:00
Bernardo Magri
7064108ce7 fix(distro): fix /etc/nixos ownership, theme discovery, and CLI wrappers
- installer: set recursive ownership of /etc/nixos to main user post-install
- themes: fix NOMARCHY_PATH and discovery logic for Lua theme menu
- scripts: update CLI wrappers (font, theme, wallpaper) to use Walker menus
- core: remove obsolete NOMARCHY_PATH and cleanup dead code
- features: add pkgs.lua for Walker and remove obsolete switcher.nix
- docs: update ROADMAP.md, SCRIPTS.md and STRUCTURE.md
2026-05-03 08:59:13 +01:00
Bernardo Magri
bef7be01b8 fix(installer): wire HM as a NixOS module, move env-update to system layer
The post-install standalone HM activation kept failing in new ways
(daemon access, git ownership, missing PATH on first boot). Wire HM as
a NixOS module in the generated flake instead, so first-boot dotfiles
are activated by `nixos-install` itself with proper system context. The
standalone `homeConfigurations.<user>` is kept alongside for fast
iteration via `nomarchy-env-update`. Also:

- Drop the chroot HM activation block from the installer entirely.
- Move `nomarchy-env-update` from `features/scripts/utils/` to
  `core/system/scripts/` so it ships in `nomarchy-system-scripts` and
  exists on a freshly-installed system regardless of HM state.
- Set system-wide git `safe.directory` for /etc/nixos and the
  impermanence-relocated /persist/etc/nixos so the user-mode HM run
  doesn't trip on the root-owned flake repo.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 12:24:12 +01:00
Bernardo Magri
d4f50afc62 fix(installer): start nix-daemon and trust flake repo for HM activation
HM activation inside `nixos-enter` failed with `big.lock: Permission
denied` because the chroot has no systemd and therefore no nix-daemon —
the user-level `nix run` fell back to single-user mode and couldn't
write /nix/var/nix/db. Launch nix-daemon manually for the activation
window and force NIX_REMOTE=daemon. Also mark /etc/nixos (and the
impermanence path) as a git safe.directory so HM doesn't trip over
git's dubious-ownership check on the root-owned repo. Make
nomarchy-env-update self-bootstrap via `nix run home-manager` when
home-manager isn't on PATH so the recovery hint actually works on a
freshly-installed system.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 11:56:22 +01:00
Bernardo Magri
2f18d4efcf fix(installer): unblock disko, bootloader, HM activation, hyprland res
- Pass --yes-wipe-all-disks to disko so the silent gum-spin path no
  longer hangs forever waiting on a hidden "yes" confirmation prompt
  (added in disko 1.13's destroy,format,mount mode).
- Stop threading an externally-built pkgs into the user flake's
  nixosSystem; configure nixpkgs through the module system instead so
  core/system/default.nix's nixpkgs.config.allowUnfree stops conflicting
  with the assertion "system configures nixpkgs with an externally
  created instance".
- Enable boot.loader.systemd-boot in the generated system.nix so the
  installed system has an actual bootloader (disko already lays out a
  1 GiB ESP at /boot).
- Bump nix.settings.download-buffer-size to 512 MiB to silence the
  "download buffer is full" warning on large NAR fetches.
- Activate home-manager via `runuser -l` instead of `runuser -u … --
  env HOME=…`. The latter only switches uid and leaves \$USER=root, so
  HM's activation script saw root, warned, and wrote dotfiles into
  /root/ — meaning the user's first login had no Hyprland config.
- Revert default Hyprland monitor line back to highres (live ISO and
  user default) — preferred falls back to EDID's 1024x768 in QEMU and
  on several laptop panels, which is the bug highres was put there to
  defeat.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 11:07:02 +01:00
Bernardo Magri
329dc009b6 fix(installer): repair git init, LUKS reprompt, impermanence-no exit, quiet disko
- nrun git git init -q passed 'git' as a subcommand to git itself,
  failing the post-disko repo init. Drop the duplicated arg.
- disko's luks module reads passwordFile at the top level; placing it
  under settings.* meant it was silently ignored and disko fell back
  to askPassword=true, prompting the user again on luksOpen. Move the
  option to the right scope.
- configure_impermanence now uses local rc, nrun gum confirm, and an
  explicit case (0/1/130) with a final return 0 so a No answer no
  longer aborts the installer.
- run_disko_with_retry hides disko's chatty output behind a gum spin
  by default and surfaces the captured log on failure. Set
  NOMARCHY_VERBOSE_DISKO=1 to stream output live.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 09:48:37 +01:00
Bernardo Magri
0af1395df2 fix(themes): complete summer-day with Everforest Light palette
colors.toml and icons.theme were copy-pasted from summer-night (a dark
theme). Repalette to Everforest Light, mark as light via light.mode,
switch icons to Yaru-blue, fix the broken Waybar import, and add the
btop/neovim/vscode/rofi/walker app configs the sibling themes ship.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 09:48:21 +01:00
Bernardo Magri
f2b99e0f75 adding summer-day theme 2026-05-02 09:16:58 +01:00
Bernardo Magri
6411395d9f fix(qa): comprehensive out-of-the-box audit and repair
- Fix critical bash dynamic scoping bug in install.sh (Impermanence/Form Factor).
- Polished Live ISO with auto-login and passwordless sudo.
- Repurposed nomarchy-toggle-suspend to directly execute systemctl suspend.
- Updated nomarchy-launch-wifi to use nmtui in alacritty.
- Optimized nomarchy-welcome to avoid redundant rebuilds via --no-update flag.
- Enabled nomarchy-welcome in Hyprland autostart.
- Wrapped Live ISO-modifying steps in welcome wizard to prevent failures.
- Removed obsolete hardware auto-detection from nomarchy-on-boot.
- Hardened script doc generator against false-positive wildcard tokens.
- Regenerated docs/SCRIPTS.md and updated docs/ROADMAP.md.
2026-05-01 20:03:04 +01:00
Bernardo Magri
39b1a9c1b3 style(hyprland): set default monitor to 'preferred' resolution
Updated the default Hyprland monitor configuration to 'monitor=,preferred,auto,1'. This ensures the best resolution is selected automatically while maintaining a 1x scale by default.
2026-05-01 17:00:57 +01:00
Bernardo Magri
c5544e56c8 feat(system): default to latest stable kernel
Set boot.kernelPackages to pkgs.linuxPackages_latest by default. This ensures Nomarchy users benefit from the latest hardware support and security features in the mainline kernel.
2026-05-01 16:54:01 +01:00
Bernardo Magri
0306dff092 feat(installer): implement single-input flake architecture
- Refactor generated flake.nix to use the Appliance Model.
- Downstream flake now only defines the 'nomarchy' input.
- Dependencies (nixpkgs, home-manager) are inherited from nomarchy.inputs to ensure maximum stability and version alignment with upstream.
2026-05-01 16:51:53 +01:00
Bernardo Magri
3b977f181d fix(installer): resolve disko evaluation crash and infinite loops
- Fix disko-config.nix signature by adding '...' to handle unexpected CLI arguments.
- Update disko mode to 'destroy,format,mount' for the modern API and to avoid deprecation warnings.
- Fix infinite loops in 'configure_impermanence' and 'confirm_form_factor' caused by misinterpreting 'No' (rc=1) as an abort.
2026-05-01 16:43:05 +01:00
119 changed files with 4053 additions and 1553 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

@@ -1,6 +1,6 @@
# 👑 Nomarchy
**Nomarchy** is a professional-grade NixOS distribution that replicates the Omarchy Wayland workflow with a strictly declarative, flake-based architecture. It provides a highly polished, "it just works" experience for power users who want a beautiful Hyprland environment without sacrificing the reliability of NixOS.
**Nomarchy** is a professional-grade NixOS distribution that ships a highly curated Hyprland desktop on a strictly declarative, flake-based foundation. It provides a highly polished, "it just works" experience for power users who want a beautiful Wayland environment without sacrificing the reliability of NixOS.
## ✨ Key Features
@@ -40,7 +40,8 @@ This builds a full graphical VM of the installer environment. Once inside, click
### 2. Build the Installer ISO
To install on physical hardware, generate your own bootable image:
```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.
@@ -114,4 +115,4 @@ Nomarchy includes dozens of productivity scripts available in your PATH. Some hi
- `nomarchy-menu`: The central hub for all utilities and pickers.
---
*Built with ❤️ using NixOS, Hyprland, Stylix, and the spirit of Omarchy.*
*Built with ❤️ using NixOS, Hyprland, Stylix, and the Nomarchy Community.*

View File

@@ -15,8 +15,7 @@ repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
sources=(
"core/home/config/nomarchy/default/hypr/bindings/utilities.conf|Utilities"
"core/home/config/nomarchy/default/hypr/bindings/tiling.conf|Tiling"
"core/home/config/nomarchy/default/hypr/bindings/tiling-v2.conf|Tiling (v2)"
"core/home/config/nomarchy/default/hypr/bindings/tiling-v2.conf|Tiling"
"core/home/config/nomarchy/default/hypr/bindings/clipboard.conf|Clipboard"
"core/home/config/nomarchy/default/hypr/bindings/media.conf|Media keys"
"features/desktop/hyprland/config/bindings.conf|Apps & web shortcuts"

View File

@@ -81,10 +81,34 @@ ref_files_per_cmd() {
# Final char must be alphanumeric — dropping trailing-dash matches like
# `nomarchy-pkg-` that come from glob references (`for c in nomarchy-pkg-*`).
# 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 first `grep -vE` drops lines where `nomarchy-*` is a derivation /
# tmp file / sudoers basename / systemd unit / flake output / docker
# 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[@]}" \
"${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-plymouth|nomarchy-sddm-theme|nomarchy-live|nomarchy-rev|nomarchy-windows)$' \
| 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 --------------------------------------------------------

16
core/branding/about.txt Normal file
View File

@@ -0,0 +1,16 @@
# About Nomarchy
Nomarchy is a highly curated, NixOS-based distribution designed for power users.
It features a customized Hyprland desktop environment with a declarative
theming engine and a suite of integrated utility scripts.
Built on a foundation of:
- NixOS (Linux)
- Hyprland (Window Manager)
- Waybar (Status Bar)
- Walker (Application Launcher & Menu)
- Stylix (Theming Engine)
Version: 2026.05.04
Docs: https://github.com/nomarchy/nomarchy/docs
Manual: nomarchy-manual (Command)

View File

@@ -0,0 +1,11 @@
# Nomarchy Screensaver Configuration
Nomarchy uses hyprlock for locking and hypridle for idle management.
To configure the screensaver/lock screen visuals, edit:
~/.config/hypr/hyprlock.conf
To configure idle timeouts and actions, edit:
~/.config/hypr/hypridle.conf
You can also toggle the screensaver/idle management via the Nomarchy Menu:
Trigger > Toggle > Idle Lock

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,
wallpaper, night light, idle, lock screen, screenshots, layer rules, workspace
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
@@ -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.**
**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
@@ -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-bg-next # Cycle wallpaper
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
@@ -311,10 +314,48 @@ nomarchy-debug --no-sudo --print # Debug info (ALWAYS use these flags)
nomarchy-lock-screen # Lock screen
nomarchy-system-shutdown # Shutdown
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.
### 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
```bash
@@ -325,6 +366,7 @@ nomarchy-debug --no-sudo --print
nomarchy-upload-log
# Reset specific config to defaults
# Examples: nomarchy-refresh-fastfetch, nomarchy-refresh-hyprland, nomarchy-refresh-waybar
nomarchy-refresh-<app>
# 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:
- Editing files in `~/.local/share/nomarchy/` (`bin/`, `config/`, `default/`, `themes/`, `migrations/`, etc.)
- Creating or editing migrations
- Running `nomarchy-dev-*` commands
- Modifying Nomarchy's own source tree
## Example Requests

View File

@@ -3,5 +3,4 @@ export SUDO_EDITOR="$EDITOR"
export BAT_THEME=ansi
# Duplicated from .config/uwsm/env so SSH works too
export NOMARCHY_PATH=$HOME/.local/share/nomarchy
export PATH=$NOMARCHY_PATH/bin:$PATH:$HOME/.local/bin
export PATH=$PATH:$HOME/.local/bin

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()
local entries = {}
local user_theme_dir = os.getenv("HOME") .. "/.config/nomarchy/themes"
local nomarchy_path = os.getenv("NOMARCHY_PATH") or ""
local default_theme_dir = nomarchy_path .. "/themes"
local default_theme_dir = os.getenv("HOME") .. "/.local/share/nomarchy/themes"
local seen_themes = {}

View File

@@ -1,8 +1,18 @@
# App-specific tweaks
# App-specific tweaks. All rules are class- or title-gated, so sourcing
# unconditionally is harmless when the app isn't installed or running.
source = ~/.config/nomarchy/default/hypr/apps/1password.conf
source = ~/.config/nomarchy/default/hypr/apps/bitwarden.conf
source = ~/.config/nomarchy/default/hypr/apps/browser.conf
source = ~/.config/nomarchy/default/hypr/apps/davinci-resolve.conf
source = ~/.config/nomarchy/default/hypr/apps/geforce.conf
source = ~/.config/nomarchy/default/hypr/apps/hyprshot.conf
source = ~/.config/nomarchy/default/hypr/apps/localsend.conf
source = ~/.config/nomarchy/default/hypr/apps/moonlight.conf
source = ~/.config/nomarchy/default/hypr/apps/pip.conf
source = ~/.config/nomarchy/default/hypr/apps/qemu.conf
source = ~/.config/nomarchy/default/hypr/apps/retroarch.conf
source = ~/.config/nomarchy/default/hypr/apps/steam.conf
source = ~/.config/nomarchy/default/hypr/apps/system.conf
source = ~/.config/nomarchy/default/hypr/apps/telegram.conf
source = ~/.config/nomarchy/default/hypr/apps/terminals.conf
source = ~/.config/nomarchy/default/hypr/apps/walker.conf

View File

@@ -1,5 +1,6 @@
exec-once = uwsm-app -- hypridle
exec-once = uwsm-app -- mako
exec-once = nomarchy-welcome
# exec-once = uwsm-app -- waybar
# fcitx5 is autostarted by NixOS's i18n.inputMethod when
# nomarchy.system.inputMethod.enable = true; no manual exec-once needed.

View File

@@ -1,16 +0,0 @@
# Deprecated bindings file. New installations include everything directly.
bindd = SUPER, RETURN, Terminal, exec, $terminal
bindd = SUPER, F, File manager, exec, $fileManager
bindd = SUPER, B, Web browser, exec, $browser
bindd = SUPER, M, Music player, exec, $music
bindd = SUPER, N, Neovim, exec, $terminal -e nvim
bindd = SUPER, T, Top, exec, $terminal -e btop
bindd = SUPER, D, Lazy Docker, exec, $terminal -e lazydocker
bindd = SUPER, G, Messenger, exec, $messenger
bindd = SUPER, O, Obsidian, exec, obsidian -disable-gpu
bindd = SUPER, SLASH, Password manager, exec, $passwordManager
source = ~/.config/nomarchy/default/hypr/bindings/media.conf
source = ~/.config/nomarchy/default/hypr/bindings/tiling.conf
source = ~/.config/nomarchy/default/hypr/bindings/utilities.conf

View File

@@ -1,74 +0,0 @@
# This is now a deprecated file meant for those who did not wish to learn the latest Nomarchy hotkeys.
# Do not make changes here, but bring them to tiling-v2.conf instead.
# Close windows
bindd = SUPER, Q, Close window, killactive,
bindd = CTRL ALT, DELETE, Close all windows, exec, nomarchy-hyprland-window-close-all
# Control tiling
bindd = SUPER, J, Toggle window split, togglesplit, # dwindle
bindd = SUPER, P, Pseudo window, pseudo, # dwindle
bindd = SUPER SHIFT, V, Toggle window floating/tiling, togglefloating,
bindd = SHIFT, F11, Force full screen, fullscreen, 0
bindd = ALT, F11, Full width, fullscreen, 1
# Move focus with SUPER + arrow keys
bindd = SUPER, LEFT, Move focus left, movefocus, l
bindd = SUPER, RIGHT, Move focus right, movefocus, r
bindd = SUPER, UP, Move focus up, movefocus, u
bindd = SUPER, DOWN, Move focus down, movefocus, d
# Switch workspaces with SUPER + [0-9]
bindd = SUPER, code:10, Switch to workspace 1, workspace, 1
bindd = SUPER, code:11, Switch to workspace 2, workspace, 2
bindd = SUPER, code:12, Switch to workspace 3, workspace, 3
bindd = SUPER, code:13, Switch to workspace 4, workspace, 4
bindd = SUPER, code:14, Switch to workspace 5, workspace, 5
bindd = SUPER, code:15, Switch to workspace 6, workspace, 6
bindd = SUPER, code:16, Switch to workspace 7, workspace, 7
bindd = SUPER, code:17, Switch to workspace 8, workspace, 8
bindd = SUPER, code:18, Switch to workspace 9, workspace, 9
bindd = SUPER, code:19, Switch to workspace 10, workspace, 10
# Move active window to a workspace with SUPER + SHIFT + [0-9]
bindd = SUPER SHIFT, code:10, Move window to workspace 1, movetoworkspace, 1
bindd = SUPER SHIFT, code:11, Move window to workspace 2, movetoworkspace, 2
bindd = SUPER SHIFT, code:12, Move window to workspace 3, movetoworkspace, 3
bindd = SUPER SHIFT, code:13, Move window to workspace 4, movetoworkspace, 4
bindd = SUPER SHIFT, code:14, Move window to workspace 5, movetoworkspace, 5
bindd = SUPER SHIFT, code:15, Move window to workspace 6, movetoworkspace, 6
bindd = SUPER SHIFT, code:16, Move window to workspace 7, movetoworkspace, 7
bindd = SUPER SHIFT, code:17, Move window to workspace 8, movetoworkspace, 8
bindd = SUPER SHIFT, code:18, Move window to workspace 9, movetoworkspace, 9
bindd = SUPER SHIFT, code:19, Move window to workspace 10, movetoworkspace, 10
# TAB between workspaces
bindd = SUPER, TAB, Next workspace, workspace, e+1
bindd = SUPER SHIFT, TAB, Previous workspace, workspace, e-1
bindd = SUPER CTRL, TAB, Former workspace, workspace, previous
# Swap active window with the one next to it with SUPER + SHIFT + arrow keys
bindd = SUPER SHIFT, LEFT, Swap window to the left, swapwindow, l
bindd = SUPER SHIFT, RIGHT, Swap window to the right, swapwindow, r
bindd = SUPER SHIFT, UP, Swap window up, swapwindow, u
bindd = SUPER SHIFT, DOWN, Swap window down, swapwindow, d
# Cycle through applications on active workspace
bindd = ALT, TAB, Cycle to next window, cyclenext
bindd = ALT SHIFT, TAB, Cycle to prev window, cyclenext, prev
bindd = ALT, TAB, Reveal active window on top, bringactivetotop
bindd = ALT SHIFT, TAB, Reveal active window on top, bringactivetotop
# Resize active window
bindd = SUPER, code:20, Expand window left, resizeactive, -100 0 # - key
bindd = SUPER, code:21, Shrink window left, resizeactive, 100 0 # = key
bindd = SUPER SHIFT, code:20, Shrink window up, resizeactive, 0 -100
bindd = SUPER SHIFT, code:21, Expand window down, resizeactive, 0 100
# Scroll through existing workspaces with SUPER + scroll
bindd = SUPER, MOUSE_DOWN, Scroll active workspace forward, workspace, e+1
bindd = SUPER, MOUSE_UP, Scroll active workspace backward, workspace, e-1
# Move/resize windows with mainMod + LMB/RMB and dragging
bindmd = SUPER, mouse:272, Move window, movewindow
bindmd = SUPER, mouse:273, Resize window, resizewindow

View File

@@ -1,16 +0,0 @@
# Application bindings
bindd = SUPER, RETURN, Terminal, exec, uwsm-app -- xdg-terminal-exec --dir="$(nomarchy-cmd-terminal-cwd)"
bindd = SUPER SHIFT, RETURN, Browser, exec, nomarchy-launch-browser
bindd = SUPER SHIFT, F, File manager, exec, uwsm-app -- nautilus --new-window
bindd = SUPER ALT SHIFT, F, File manager (cwd), exec, uwsm-app -- nautilus --new-window "$(nomarchy-cmd-terminal-cwd)"
bindd = SUPER SHIFT, B, Browser, exec, nomarchy-launch-browser
bindd = SUPER SHIFT ALT, B, Browser (private), exec, nomarchy-launch-browser --private
bindd = SUPER SHIFT, N, Editor, exec, nomarchy-launch-editor
# Add extra bindings
# bindd = SUPER SHIFT, A, ChatGPT, exec, nomarchy-launch-webapp "https://chatgpt.com"
# bindd = SUPER SHIFT, R, exec, alacritty -e ssh your-server
# Overwrite existing bindings, like putting Nomarchy Menu on Super + Space
# unbind = SUPER, SPACE
# bindd = SUPER, SPACE, Nomarchy menu, exec, nomarchy-menu

View File

@@ -51,5 +51,17 @@ let
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
./state.nix
./overrides.nix
./behavior.nix
./fonts.nix
./configs.nix
./security.nix

View File

@@ -1,47 +1,47 @@
{ 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 = {
toggles = {
suspend = lib.mkOption {
type = lib.types.bool;
default = true;
default = schema.home.suspend;
description = "Whether to show suspend in system menu.";
};
screensaver = lib.mkOption {
type = lib.types.bool;
default = true;
default = schema.home.screensaver;
description = "Whether the screensaver is enabled.";
};
idle = lib.mkOption {
type = lib.types.bool;
default = true;
default = schema.home.idle;
description = "Whether the idle lock is enabled.";
};
nightlight = lib.mkOption {
type = lib.types.bool;
default = false;
default = schema.home.nightlight;
description = "Whether the nightlight is enabled.";
};
waybar = lib.mkOption {
type = lib.types.bool;
default = true;
default = schema.home.waybar;
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 {
type = lib.types.int;
default = 4000;
default = schema.home.nightlightTemperature;
description = "Temperature for the nightlight.";
};
theme = lib.mkOption {
type = lib.types.str;
default = "summer-night";
default = schema.home.theme;
description = "System theme name.";
};
formFactor = lib.mkOption {
@@ -58,35 +58,35 @@
};
wallpaper = lib.mkOption {
type = lib.types.str;
default = "";
default = schema.home.wallpaper;
description = "System wallpaper path.";
};
panelPosition = lib.mkOption {
type = lib.types.enum [ "top" "bottom" ];
default = "top";
default = schema.home.panelPosition;
description = "Waybar panel position.";
};
hyprland = {
gaps_in = lib.mkOption {
type = lib.types.int;
default = 5;
default = schema.home.hyprland.gaps_in;
description = "Inner gaps for Hyprland.";
};
gaps_out = lib.mkOption {
type = lib.types.int;
default = 10;
default = schema.home.hyprland.gaps_out;
description = "Outer gaps for Hyprland.";
};
border_size = lib.mkOption {
type = lib.types.int;
default = 2;
default = schema.home.hyprland.border_size;
description = "Border size for Hyprland.";
};
};
fonts = {
monospace = lib.mkOption {
type = lib.types.str;
default = "JetBrainsMono Nerd Font";
default = schema.home.font;
description = "System monospace font.";
};
};

View File

@@ -1,97 +1,34 @@
{ 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
# replace upstream defaults. Override priority (highest to lowest):
# 1. User Nix options
# 2. User file overrides (~/.config/nomarchy/overrides/)
# 3. Upstream defaults
# STATUS: option surface only — the actual override mechanism is NOT yet
# implemented. The options are kept so configs that already set
# `nomarchy.overrides.enable = …;` continue to evaluate; setting them has
# no effect today. Tracked in docs/ROADMAP.md.
#
# Supported override paths:
# - hypr/ - Hyprland configs (bindings.conf, input.conf, etc.)
# - waybar/ - Waybar config and style
# - alacritty/ - Alacritty terminal config
# - walker/ - Walker launcher config
# - kitty/ - Kitty terminal config
# - btop/ - Btop resource monitor config
# - apps/ - Other application configs
# When implemented, this module should substitute sources in
# `xdg.configFile.<path>.source` based on the presence of matching files
# under ~/.config/nomarchy/overrides/.
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 = {
enable = lib.mkOption {
type = lib.types.bool;
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 {
type = lib.types.attrsOf lib.types.path;
default = {};
description = "Override paths discovered at build time. Populated by the override system.";
};
};
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
description = ''
Reserved for the future file-based override loader. Currently
unused.
'';
};
};
}

View File

@@ -2,42 +2,51 @@
let
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;
# Read unified state from ~/.config/nomarchy/state.json
togglesState = nomarchyLib.readHomeState config.home.homeDirectory;
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 = {
nomarchy = {
toggles = {
suspend = togglesState.suspend or true;
screensaver = togglesState.screensaver or true;
idle = togglesState.idle or true;
nightlight = togglesState.nightlight or false;
waybar = togglesState.waybar or true;
skipVsCodeTheme = togglesState.skipVsCodeTheme or false;
suspend = lib.mkDefault (togglesState.suspend or schema.home.suspend);
screensaver = lib.mkDefault (togglesState.screensaver or schema.home.screensaver);
idle = lib.mkDefault (togglesState.idle or schema.home.idle);
nightlight = lib.mkDefault (togglesState.nightlight or schema.home.nightlight);
waybar = lib.mkDefault (togglesState.waybar or schema.home.waybar);
};
nightlightTemperature = togglesState.nightlightTemperature or 4000;
theme = togglesState.theme or "summer-night";
wallpaper = togglesState.wallpaper or "";
panelPosition = togglesState.panelPosition or "top";
nightlightTemperature = lib.mkDefault (togglesState.nightlightTemperature or schema.home.nightlightTemperature);
theme = lib.mkDefault (togglesState.theme or schema.home.theme);
wallpaper = lib.mkDefault (togglesState.wallpaper or schema.home.wallpaper);
panelPosition = lib.mkDefault (togglesState.panelPosition or schema.home.panelPosition);
hyprland = {
gaps_in = togglesState.hyprland.gaps_in or 5;
gaps_out = togglesState.hyprland.gaps_out or 10;
border_size = togglesState.hyprland.border_size or 2;
gaps_in = lib.mkDefault (togglesState.hyprland.gaps_in or schema.home.hyprland.gaps_in);
gaps_out = lib.mkDefault (togglesState.hyprland.gaps_out or schema.home.hyprland.gaps_out);
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
isLightMode = nomarchyLib.isThemeLightMode {
themeName = togglesState.theme or "summer-night";
isLightMode = lib.mkDefault (nomarchyLib.isThemeLightMode {
themeName = togglesState.theme or schema.home.theme;
inherit assetsPath;
};
iconsTheme = nomarchyLib.getIconsTheme {
themeName = togglesState.theme or "summer-night";
});
iconsTheme = lib.mkDefault (nomarchyLib.getIconsTheme {
themeName = togglesState.theme or schema.home.theme;
inherit assetsPath;
};
});
};
};
}

View File

@@ -33,5 +33,7 @@
time.timeZone = lib.mkDefault config.nomarchy.system.timezone;
boot.kernelPackages = lib.mkDefault pkgs.linuxPackages_latest;
nixpkgs.config.allowUnfree = true;
}

View File

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

View File

@@ -70,8 +70,47 @@ in
})
(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;
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 = {
HandleLidSwitch = lib.mkDefault "suspend-then-hibernate";
HandleLidSwitchExternalPower = lib.mkDefault "suspend";
HandlePowerKey = "hibernate";
IdleAction = "suspend-then-hibernate";
IdleActionSec = toString (cfg.idleMinutes * 60);
HandlePowerKey = lib.mkDefault "hibernate";
IdleAction = lib.mkDefault "suspend-then-hibernate";
IdleActionSec = lib.mkDefault (toString (cfg.idleMinutes * 60));
};
};
};

View File

@@ -10,13 +10,39 @@ in
options.nomarchy.system.impermanence = {
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 {
# 1. The Rollback Script: Runs in initrd before filesystems are mounted
boot.initrd.postDeviceCommands = lib.mkAfter ''
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
mkdir -p /btrfs_tmp/old_roots
@@ -58,7 +84,7 @@ in
"/etc/machine-id"
"/etc/supergfxd.conf"
];
users.nomarchy = {
users.${cfg.user} = {
directories = [
".ssh"
".gnupg"

View File

@@ -11,6 +11,14 @@
# Optimize storage by hard-linking identical files
auto-optimise-store = lib.mkDefault true;
# Bump the substituter download buffer from the 64 MiB default to
# 512 MiB. Large NARs (kernels, electron apps, the full NixOS
# closure on first install) overrun 64 MiB and Nix prints
# "download buffer is full" until the consumer catches up — the
# warning is harmless but slows substitution and looks like an
# error. 512 MiB comfortably covers everything in our closure.
download-buffer-size = 524288000;
};
# Populates NIX_PATH with the nixpkgs input from the flake.

View File

@@ -1,27 +1,32 @@
{ 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 = {
dns = lib.mkOption {
type = lib.types.enum [ "Cloudflare" "Google" "DHCP" "Custom" ];
default = "DHCP";
default = schema.system.dns;
description = "Selected DNS provider.";
};
customDns = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
default = schema.system.customDns;
description = "List of custom DNS servers.";
};
wifi = {
powersave = lib.mkOption {
type = lib.types.bool;
default = true;
default = schema.system.wifi.powersave;
description = "Whether to enable wifi power saving.";
};
};
timezone = lib.mkOption {
type = lib.types.str;
default = "UTC";
default = schema.system.timezone;
description = "System timezone.";
};
formFactor = lib.mkOption {
@@ -39,23 +44,23 @@
features = {
fingerprint = lib.mkOption {
type = lib.types.bool;
default = false;
default = schema.system.features.fingerprint;
description = "Whether to enable fingerprint support.";
};
fido2 = lib.mkOption {
type = lib.types.bool;
default = false;
default = schema.system.features.fido2;
description = "Whether to enable FIDO2 support.";
};
hybridGPU = lib.mkOption {
type = lib.types.bool;
default = false;
default = schema.system.features.hybridGPU;
description = "Whether to enable hybrid GPU support (supergfxd).";
};
};
theme = lib.mkOption {
type = lib.types.str;
default = "summer-night";
default = schema.system.theme;
description = "Selected system theme.";
};

View File

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

View File

@@ -2,4 +2,13 @@
{
environment.systemPackages = [ pkgs.nomarchy-system-scripts ];
# /etc/nixos is owned by root, but `nomarchy-env-update` (and `nix
# flake` invocations) run as the user and shell out to git. Without
# this, git refuses with "dubious ownership in repository" and HM
# evaluation fails. Mark both the standard and impermanence-relocated
# paths as safe at the system level so every user is covered.
programs.git.config = {
safe.directory = [ "/etc/nixos" "/persist/etc/nixos" ];
};
}

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

@@ -1,30 +1,5 @@
#!/usr/bin/env bash
set -e
# Toggles the suspend menu option availability.
# Hybrid: updates state.json and runs env-update for persistence.
STATE_DIR="$HOME/.config/nomarchy"
STATE_FILE="$STATE_DIR/state.json"
mkdir -p "$STATE_DIR"
# Initialize if doesn't exist
[[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE"
# Get current state from env or state file
if [[ $NOMARCHY_TOGGLE_SUSPEND == "false" ]]; then
NEW_VALUE="true"
notify-send -u low " Suspend now available in system menu"
else
NEW_VALUE="false"
notify-send -u low " Suspend removed from system menu"
fi
# Update JSON using jq with --argjson for proper boolean handling
TMP_JSON=$(mktemp)
jq --argjson val "$NEW_VALUE" '.suspend = $val' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
echo "Suspend availability set to $NEW_VALUE. Updating environment..."
# Run nomarchy-env-update to apply changes to the menu
nomarchy-env-update
# Execute system suspend
systemctl suspend

View File

@@ -2,19 +2,28 @@
let
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;
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 = {
dns = systemState.dns or "DHCP";
customDns = systemState.customDns or [];
wifi.powersave = systemState.wifi.powersave or true;
timezone = systemState.timezone or "UTC";
dns = lib.mkDefault (systemState.dns or schema.system.dns);
customDns = lib.mkDefault (systemState.customDns or schema.system.customDns);
wifi.powersave = lib.mkDefault (systemState.wifi.powersave or schema.system.wifi.powersave);
timezone = lib.mkDefault (systemState.timezone or schema.system.timezone);
features = {
fingerprint = systemState.features.fingerprint or false;
fido2 = systemState.features.fido2 or false;
hybridGPU = systemState.features.hybridGPU or false;
fingerprint = lib.mkDefault (systemState.features.fingerprint or schema.system.features.fingerprint);
fido2 = lib.mkDefault (systemState.features.fido2 or schema.system.features.fido2);
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).
home/ Home Manager modules.
options.nix Most home-side nomarchy.* options.
behavior.nix nomarchy.behavior.* (deploy-default-config toggles).
overrides.nix File-based overrides from ~/.config/nomarchy/overrides/.
overrides.nix nomarchy.overrides.* (reserved; currently no-op — see ROADMAP).
config/ Plain dotfiles symlinked into ~/.config.
features/ Apps and desktop components. Add new apps here.

View File

@@ -59,63 +59,6 @@ _Source: `core/home/config/nomarchy/default/hypr/bindings/utilities.conf`_
## Tiling
_Source: `core/home/config/nomarchy/default/hypr/bindings/tiling.conf`_
| Modifiers | Key | Action |
| --- | --- | --- |
| SUPER | Q | Close window |
| CTRL ALT | DELETE | Close all windows |
| SUPER | J | Toggle window split |
| SUPER | P | Pseudo window |
| SUPER SHIFT | V | Toggle window floating/tiling |
| SHIFT | F11 | Force full screen |
| ALT | F11 | Full width |
| SUPER | LEFT | Move focus left |
| SUPER | RIGHT | Move focus right |
| SUPER | UP | Move focus up |
| SUPER | DOWN | Move focus down |
| SUPER | 1 | Switch to workspace 1 |
| SUPER | 2 | Switch to workspace 2 |
| SUPER | 3 | Switch to workspace 3 |
| SUPER | 4 | Switch to workspace 4 |
| SUPER | 5 | Switch to workspace 5 |
| SUPER | 6 | Switch to workspace 6 |
| SUPER | 7 | Switch to workspace 7 |
| SUPER | 8 | Switch to workspace 8 |
| SUPER | 9 | Switch to workspace 9 |
| SUPER | 0 | Switch to workspace 10 |
| SUPER SHIFT | 1 | Move window to workspace 1 |
| SUPER SHIFT | 2 | Move window to workspace 2 |
| SUPER SHIFT | 3 | Move window to workspace 3 |
| SUPER SHIFT | 4 | Move window to workspace 4 |
| SUPER SHIFT | 5 | Move window to workspace 5 |
| SUPER SHIFT | 6 | Move window to workspace 6 |
| SUPER SHIFT | 7 | Move window to workspace 7 |
| SUPER SHIFT | 8 | Move window to workspace 8 |
| SUPER SHIFT | 9 | Move window to workspace 9 |
| SUPER SHIFT | 0 | Move window to workspace 10 |
| SUPER | TAB | Next workspace |
| SUPER SHIFT | TAB | Previous workspace |
| SUPER CTRL | TAB | Former workspace |
| SUPER SHIFT | LEFT | Swap window to the left |
| SUPER SHIFT | RIGHT | Swap window to the right |
| SUPER SHIFT | UP | Swap window up |
| SUPER SHIFT | DOWN | Swap window down |
| ALT | TAB | Cycle to next window |
| ALT SHIFT | TAB | Cycle to prev window |
| ALT | TAB | Reveal active window on top |
| ALT SHIFT | TAB | Reveal active window on top |
| SUPER | code:20 | Expand window left |
| SUPER | code:21 | Shrink window left |
| SUPER SHIFT | code:20 | Shrink window up |
| SUPER SHIFT | code:21 | Expand window down |
| SUPER | MOUSE_DOWN | Scroll active workspace forward |
| SUPER | MOUSE_UP | Scroll active workspace backward |
| SUPER | mouse:272 | Move window |
| SUPER | mouse:273 | Resize window |
## Tiling (v2)
_Source: `core/home/config/nomarchy/default/hypr/bindings/tiling-v2.conf`_
| Modifiers | Key | Action |

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 |
| 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) |
| 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`,
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`
`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`
@@ -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).
### `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.
### `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`)
@@ -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.
### `nomarchy.toggles.skipVsCodeTheme`
`bool`, default `false`. Skip theme overrides in VSCode — useful if you manage VSCode themes yourself.
### `nomarchy.nightlightTemperature`
`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`
`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`
`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`
@@ -221,23 +243,7 @@ Wired in `features/desktop/waybar/default.nix` (filters the battery widget out o
### `nomarchy.configOverrides`
`nullOr path`, default `null`. Path to a directory containing config overrides. See "Overrides" below.
### `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).
`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.apps.opencode.enable`
@@ -253,19 +259,19 @@ Wired in `features/desktop/waybar/default.nix` (filters the battery widget out o
### `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`
`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`
`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
{
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/impermanence.nix``impermanence.enable`
- `core/home/options.nix` — most home-side `nomarchy.*` options
- `core/home/behavior.nix``nomarchy.behavior.*`
- `core/home/overrides.nix``nomarchy.overrides.*`
- `core/home/overrides.nix``nomarchy.overrides.*` (reserved; currently no-op)
- `themes/engine/loader.nix``nomarchy.themeLoader.*`
- `features/apps/vscode.nix``nomarchy.vscode.*`

View File

@@ -19,13 +19,16 @@ Guardrails (apply when adding anything):
### Now (ready to pick up)
- (empty — pick the top of **Next**.)
- **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)
- **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`.
- **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)
@@ -37,6 +40,14 @@ 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.
- **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.
- **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.
- **Package missing VSCode theme extensions via `extensionFromVscodeMarketplace`.** 15 of the 21 palettes that ship a `themes/palettes/<theme>/apps/vscode.json` declare a theme extension that isn't in `pkgs.vscode-extensions` — including `sainnhe.everforest` which is the default `summer-night` palette's theme. With the `577b3ae` fix in place, the 6 nixpkgs-packaged extensions install by default (catppuccin, catppuccin-latte, nord, tokyo-night, rose-pine, gruvbox), but the other 15 (`sainnhe.everforest`, `qufiwefefwoyn.kanagawa`, `monokai.theme-monokai-pro-vscode`, `oldjobobo.{lumon,miasma,retro-82}-theme`, `Bjarne.{ethereal,hackerman,vantablack,white}-nomarchy`, `shadesOfBuntu.flexoki-light`, `jovejonovski.ocean-green`, `TahaYVR.matteblack`) still leave `workbench.colorTheme` referencing an unloaded theme, so VSCode silently falls back. Fix: extend `features/apps/vscode.nix` to look up the active palette's extension via `pkgs.vscode-utils.extensionFromVscodeMarketplace { publisher; name; version; sha256; }` — each entry pinned by hash. Could be table-driven in `lib/` so a new palette only needs to add a row.
- **Reconcile nightlight: systemd-managed hyprsunset vs `pkill`/`hyprctl exec` toggle.** `features/desktop/nightlight.nix` always sets `services.hyprsunset.enable = lib.mkDefault true` and bakes the temperature (`toggles.nightlight ? nightlightTemperature : 6500`) into `extraArgs` at Nix-eval time. The toggle script `themes/engine/scripts/nomarchy-toggle-nightlight` bypasses systemd entirely: `pkill hyprsunset` on disable, `hyprctl dispatch exec hyprsunset --temperature 4000` on enable — racing the systemd-managed instance and ignoring the user's chosen `nightlightTemperature`. The comment "Always enabled, we control via IPC and state" doesn't match the implementation (there's no IPC; the temperature is rebuild-time, not runtime). Pick one: (a) toggle stays as `hyprctl dispatch hyprsunset temperature ${T}` (proper IPC, no daemon restart), with Nix dropping `extraArgs` to just `--temperature 6500` on boot; or (b) gate `services.hyprsunset.enable` on the toggle and run `systemctl --user restart hyprsunset` from the script. Both also need the script to read `nightlightTemperature` instead of hardcoding 4000.
## 3. Pillar: Script & menu audit
@@ -73,31 +84,31 @@ Each PR description should reference the row(s) in `docs/SCRIPTS.md` it closes,
## 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).
- Form-factor → laptop preset (Now, depends on Pillar 5).
- `disko-golden.nix` variants for software-RAID and BTRFS-pool-as-root (Shipped).
- Pre-flight resume polish (Next).
- "What's installed?" summary screen on boot of a freshly-installed system, sourced from `state.json` + `nomarchy-system-scripts` introspection.
- Optional non-LUKS branch in the installer for users who explicitly opt out of FDE.
- Pre-flight resume polish (Shipped).
- Software-profile multi-select (Shipped).
- Form-factor → laptop preset (Shipped).
## 5. Pillar: Power, hardware, presets
- Laptop preset (Next): TLP, upower, brightness, lid, hypridle tuning.
- 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.
- 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).
- 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
- `nomarchy-welcome` first-run wizard (Next).
- `nomarchy-welcome` first-run wizard (Shipped).
- `docs/KEYBINDINGS.md` auto-generator (Shipped).
- `docs/TROUBLESHOOTING.md` (Next).
- `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.
- `nomarchy-manual` — orphaned reference today; either implement as a curated `xdg-open` to the docs index, or delete.
- `docs/TROUBLESHOOTING.md` (Shipped).
- `docs/index.md` / README docs index (Shipped — `README.md` links every doc in `docs/`).
- `nomarchy-manual` — opens the local `~/.local/share/nomarchy/README.md` via `xdg-open` (Shipped).
## 7. Pillar: Test, CI, release
@@ -109,20 +120,32 @@ 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.
- 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: Distro Branding
## 8. Pillar: QA audit — features & components
Nomarchy is moving away from being a "flavor" of Omarchy to its own distinct identity. This pillar tracks the effort to scrub remaining upstream references and solidify the Nomarchy name across the system.
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.
- **ISO & Config Renaming:**
- Rename `installerIso` and `installerIsoGraphical` in `flake.nix` to `nomarchy-installer` and `nomarchy-live`.
- Update `hosts/nomarchy-installer.nix` and `hosts/nomarchy-live.nix` internal metadata.
- **Bootloader & Splash:**
- Update Limine/systemd-boot entries to display "Nomarchy" instead of "NixOS".
- Scrub "Omarchy" from `themes/engine/plymouth/nomarchy.script`.
- Ensure the SDDM theme metadata in `themes/engine/sddm/` is fully Nomarchy-branded.
- **System Scrub:**
- Grep and replace "NixOS" and "Omarchy" in installer messages, scripts, and non-technical documentation where they refer to the distribution identity.
- Update `/etc/os-release` via `environment.etc."os-release"` to ensure `NAME` and `PRETTY_NAME` reflect Nomarchy.
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
@@ -136,6 +159,26 @@ Nomarchy is moving away from being a "flavor" of Omarchy to its own distinct ide
(Move items here when they land — keep them brief, link the commit/PR.)
- _2026-05-21_ — **Pillar 8 / Component 6 (Apps): closeout.** Code-review-shaped sweep over `features/apps/{alacritty,btop,chromium,elephant,ghostty,kitty,lazygit,opencode,swayosd,tmux,vscode,walker}`. Three real theming bugs fixed inline: **(1)** `features/apps/kitty/config/kitty.conf:1` and `features/apps/ghostty/config/config:2` referenced palette-specific include files (`~/.config/nomarchy/current/theme/{kitty,ghostty}.conf`) that didn't exist for any of the 22 palettes — kitty include failed silently, ghostty's was optional (`?`-prefix), and both terminals rendered with their built-in defaults regardless of the active Nomarchy palette. Stylix's `kitty.enable = true` was a no-op because the module uses `xdg.configFile` instead of `programs.kitty`; ghostty has no Stylix target. Added theme-engine generators in `themes/engine/files.nix` mirroring the existing `waybar.css` pattern, mapping `palette.base*` to kitty/ghostty color directives. **(2)** `features/apps/btop/config/btop.conf:5` set `color_theme = "current"` but `themes/engine/loader.nix:72` deploys the active palette's btop theme to `~/.config/btop/themes/nomarchy.theme` — name mismatch, btop fell back to its built-in Default theme on every palette. Renamed to `"nomarchy"`. **(3)** `programs.vscode.profiles.default.userSettings.workbench.colorTheme` was set unconditionally from `themes/palettes/<theme>/apps/vscode.json`, but the matching theme extensions were bundled with `devExtensions` (default `false`) — so VSCode silently fell back to its built-in theme out of the box on every palette. Split `themeExtensions` (always-on, covers the 6 palettes whose theme extension is in nixpkgs) from `devExtensions` (opt-in). The remaining 15 palettes — including the default `summer-night` (`sainnhe.everforest`) — still break because their theme extensions aren't packaged in nixpkgs; logged as a new Later row. Chromium static `Default/Preferences` symlink already had an open Later row; verified the file's contents are duplicate of the managed-policy intent in `core/system/browser.nix`, so the existing entry's hypothesis is correct — left for the user to greenlight deletion. alacritty (Stylix-themed via `programs.alacritty.settings`), elephant (no UI), swayosd (base16 inline), walker (covered in Component 5), lazygit + tmux (terminal ANSI inheritance, transitively fixed by the kitty/ghostty changes), and opencode (minimal opt-in config) are healthy. Runtime verification (launch each app on each palette and eyeball the theming) remains the user's responsibility.
- _2026-05-21_ — **Pillar 8 / Component 5 (Desktop stack): closeout.** Code-review-shaped sweep over Hyprland, waybar, walker, hypridle, hyprsunset, mako, KEYBINDINGS.md (the runtime-rendering subset — waybar across panel positions × form factors × all 22 palettes, walker launcher modes, hypridle timeout feel — stays on the user). Five real bugs fixed inline: **(1)** 9 of 17 `~/.config/nomarchy/default/hypr/apps/*.conf` window-rule files were deployed but never sourced, including `system.conf` (the `tag +floating-window` rules every TUI helper class relies on + `class:org.nomarchy.screensaver` fullscreen rule that hypridle's 150s on-timeout depends on) and `pip.conf` (the PiP pin/size rules). `apps.conf` now sources all 17. **(2)** Two `$NNOMARCHY_TOGGLE_*` typos (double-N) in `nomarchy-menu:330` and `nomarchy-launch-screensaver:16` made `toggles.suspend` and `toggles.screensaver` vacuous — Suspend always showed in the system menu and the screensaver always launched at idle regardless of the documented option. **(3)** 4 broken per-palette waybar `style.css` overrides (`catppuccin`, `lumon`, `nord`, `retro-82`) fully replaced the default style with 214 lines of only `@define-color` declarations — picking those palettes produced a waybar with zero structural styling. Default style already imports per-palette colors via `themes/engine/files.nix`-generated `theme/waybar.css`, so deletion restores correct rendering; `summer-day`/`summer-night` kept as legitimate 100+-line redesigns. **(4)** `core/home/config/nomarchy/default/hypr/{bindings,plain-bindings}.conf` were explicitly-labeled deprecated files sourced by nothing (plain-bindings.conf referenced undefined `$terminal`/`$browser`/etc. Hyprland vars) — deleted; `docs/SCRIPTS.md` regenerated to drop stale callers and incidentally corrected 4 Origin columns whose scripts moved from `core/system/scripts/` to `features/scripts/utils/`. **(5)** Mako post-fix (commit `2a301a0`) verified: deployment + the 4 referenced scripts (`nomarchy-notification-dismiss`, `nomarchy-launch-wifi`, `nomarchy-launch-floating-terminal-with-presentation`, `nomarchy-menu-keybindings`) all resolve. Two structural inconsistencies logged to Later: keymap routing (already in 72f7e7b) and the new hyprsunset toggle-vs-systemd reconcile. `KEYBINDINGS.md` regenerated with zero diff — generator already covers both binding source locations. Runtime verification (boot live ISO, eyeball waybar/walker/screensaver flows across panel positions and palettes) remains the user's responsibility before declaring Component 5 fully closed.
- _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_ — 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 CLI wrappers and removed obsolete code. (1) Updated `nomarchy-font`, `nomarchy-theme`, and `nomarchy-wallpaper` CLI wrappers to use modern Walker menus; (2) Removed the obsolete and broken `themes/engine/switcher.nix` and its associated Nix-inlined scripts; (3) Cleaned up remaining `$NOMARCHY_PATH` references from the Omarchy era.
- _2026-05-03_ — Fixed `/etc/nixos` ownership after installation. Added a `chown -R $USERNAME:users /etc/nixos` step via `nixos-enter` at the end of `installer/install.sh` so the main user owns their configuration and can run `home-manager` commands without `sudo`.
- _2026-05-01_ — Installer & Script Audit Polish. (1) Fixed a critical bash dynamic scoping bug in `installer/install.sh` where `rc=0` assignments inside functions (Impermanence, Form Factor) were clobbering the main loop's return code, causing the installer to abort when "No" was selected; (2) Polished `hosts/nomarchy-live.nix` with auto-login for the `nixos` user and passwordless sudo for the `wheel` group; (3) Repurposed `nomarchy-toggle-suspend` to execute `systemctl suspend` directly and updated `nomarchy-menu` to reflect this; (4) Updated `nomarchy-launch-wifi` to use `nmtui` in Alacritty; (5) Regenerated `docs/SCRIPTS.md` to reflect the updated script mappings.
- _2026-04-30_ — `set -e` sweep across `nomarchy-*` scripts. Added `set -e` to 142 of 169 bash scripts that lacked it (27 already had it). Halts a class of "command failed silently in the middle of a chain, system left in half-applied state" bugs that produced repeat-fix commits. One deliberate exception: `nomarchy-menu` runs without `set -e` because it's an interactive UX loop where action failures should re-display the menu rather than abort the script. Pre-commit hook now enforces `bash -n` + `shellcheck --severity=error` so future scripts can't regress this.
- _2026-04-30_ — Installer disk-phase reliability. Hardened `installer/install.sh` and consolidated the disko configs: (1) `select_disk` now hides the live-ISO boot device(s) so the installer can't format its own boot media (`NOMARCHY_INSTALL_ALLOW_ISO_TARGET=1` to override); (2) added a 10 GiB minimum-capacity preflight; (3) `prewipe_target_drive` enumerates every active dm-crypt mapping backed by the target drive and closes them, drops the silent `|| true` from `wipefs`/`sgdisk`/`dd`, bounds `udevadm settle` to 30s, and refuses to continue if anything is still mounted; (4) wrapped the disko call in `run_disko_with_retry` with last-30-lines + Retry / View full log / Abort dialog on failure; (5) replaced the sed-templated `disko-golden.nix` + `disko-btrfs-multi.nix` pair with a single `disko-config.nix` Nix function called via `--argstr mainDrive … --arg extraDrives '[…]'` — eliminates a class of escaping bugs (cf. `3aadc36`); (6) added an EXIT trap so the tmpfs LUKS key file is removed even on early abort.
- _2026-04-30_ — Gaming home-side companion. New `nomarchy.gaming.enable` option (mirror of `nomarchy.system.gaming.enable`) and `core/home/gaming.nix` module that injects a Hyprland `windowrulev2 = fullscreen, class:^(steam_app_).*$` so Steam-launched games grab the whole screen. Closes the "Gaming — Hyprland window rule" Next-column row.

View File

@@ -24,38 +24,36 @@ Phase B (per-batch PRs) refines those into `port-from-omarchy`,
- `delete-dead` — Phase B verdict: remove and update callers.
- `stub-with-notify` — Phase B verdict: temporary `notify-send` stub.
## Scripts (170)
## Scripts (159)
| Script | Location | Callers | Status | Notes |
| --- | --- | --- | --- | --- |
| `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-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-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-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-keyboard` | `core/system/scripts` | core/home/config/nomarchy/default/hypr/bindings/media.conf | `kept` | |
| `nomarchy-build-iso` | `features/scripts/utils` | — | `unused?` | |
| `nomarchy-build-live-iso` | `features/scripts/utils` | — | `unused?` | |
| `nomarchy-build-iso` | `features/scripts/utils` | README.md | `kept` | |
| `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-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-screensaver` | `features/scripts/utils` | features/scripts/utils/nomarchy-launch-screensaver | `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-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-cmd-terminal-cwd` | `features/scripts/utils` | features/desktop/hyprland/config/bindings.conf | `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-scripts` | `features/scripts/utils` | bin/utils/nomarchy-docs-scripts | `kept` | |
| `nomarchy-drive-info` | `features/scripts/utils` | features/scripts/utils/nomarchy-drive-select | `kept` | |
| `nomarchy-drive-select` | `features/scripts/utils` | features/scripts/utils/nomarchy-drive-info,features/scripts/utils/nomarchy-drive-set-password | `kept` | |
| `nomarchy-drive-set-password` | `features/scripts/utils` | features/scripts/utils/nomarchy-drive-select,features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-env-update` | `features/scripts/utils` | core/home/bash.nix,core/system/scripts/nomarchy-pkg-add, +8 more | `kept` | |
| `nomarchy-font` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,core/system/fonts.nix, +6 more | `kept` | |
| `nomarchy-env-update` | `features/scripts/utils` | core/home/bash.nix,core/system/scripts.nix, +7 more | `kept` | |
| `nomarchy-font` | `features/scripts/utils` | bin/utils/nomarchy-docs-scripts,core/home/config/nomarchy-skill/SKILL.md, +6 more | `kept` | |
| `nomarchy-font-current` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-font-list` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-font, +2 more | `kept` | |
| `nomarchy-font-set` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-font, +4 more | `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-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-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-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` | |
@@ -79,67 +77,59 @@ Phase B (per-batch PRs) refines those into `port-from-omarchy`,
| `nomarchy-launch-about` | `features/scripts/utils` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-launch-audio` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,features/desktop/waybar/config/config.jsonc, +2 more | `kept` | |
| `nomarchy-launch-bluetooth` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,features/desktop/waybar/config/config.jsonc, +1 more | `kept` | |
| `nomarchy-launch-browser` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/plain-bindings.conf,core/home/config/nomarchy-skill/SKILL.md, +1 more | `kept` | |
| `nomarchy-launch-editor` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/plain-bindings.conf,features/desktop/hyprland/config/bindings.conf, +2 more | `kept` | |
| `nomarchy-launch-browser` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/desktop/hyprland/config/bindings.conf | `kept` | |
| `nomarchy-launch-editor` | `features/scripts/utils` | features/desktop/hyprland/config/bindings.conf,features/scripts/utils/nomarchy-menu, +1 more | `kept` | |
| `nomarchy-launch-floating-terminal-with-presentation` | `features/scripts/utils` | core/home/config/nomarchy/default/mako/core.ini,features/desktop/waybar/config/config.jsonc, +3 more | `kept` | |
| `nomarchy-launch-or-focus` | `features/scripts/utils` | core/home/config/nomarchy/extensions/menu.sh,features/desktop/hyprland/config/bindings.conf, +7 more | `kept` | |
| `nomarchy-launch-or-focus-tui` | `features/scripts/utils` | core/home/config/nomarchy/extensions/menu.sh,features/desktop/waybar/config/config.jsonc, +4 more | `kept` | |
| `nomarchy-launch-or-focus` | `features/scripts/utils` | core/home/config/nomarchy/extensions/menu.sh,features/desktop/hyprland/config/bindings.conf, +6 more | `kept` | |
| `nomarchy-launch-or-focus-tui` | `features/scripts/utils` | core/home/config/nomarchy/extensions/menu.sh,features/desktop/waybar/config/config.jsonc, +3 more | `kept` | |
| `nomarchy-launch-or-focus-webapp` | `features/scripts/utils` | features/desktop/hyprland/config/bindings.conf | `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-walker` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/bindings/clipboard.conf,core/home/config/nomarchy/default/hypr/bindings/utilities.conf, +1 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-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` | features/desktop/hyprland/config/bindings.conf,features/scripts/utils/nomarchy-launch-or-focus-webapp, +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-manual` | `features/scripts/utils` | features/scripts/utils/nomarchy-menu,themes/engine/scripts/nomarchy-theme-install | `kept` | |
| `nomarchy-menu` | `features/scripts/utils` | bin/utils/nomarchy-docs-scripts,core/home/config/nomarchy/default/hypr/bindings/utilities.conf, +10 more | `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, +9 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-npx-install` | `features/scripts/utils` | — | `unused?` | |
| `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` | `features/scripts/utils` | 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-drop` | `features/scripts/utils` | features/scripts/utils/nomarchy-voxtype-remove | `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` | `features/scripts/utils` | features/scripts/utils/nomarchy-pkg-drop | `kept` | |
| `nomarchy-powerprofiles-list` | `core/system/scripts` | features/scripts/utils/nomarchy-menu | `kept` | |
| `nomarchy-preflight-migration` | `core/system/scripts` | features/scripts/utils/nomarchy-env-update,features/scripts/utils/nomarchy-migrate-state | `kept` | |
| `nomarchy-preflight-migration` | `features/scripts/utils` | features/scripts/utils/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-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-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-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-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-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-pipewire` | `core/system/scripts` | 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-tmux` | `features/scripts/utils` | — | `unused?` | |
| `nomarchy-restart-trackpad` | `core/system/scripts` | — | `unused?` | |
| `nomarchy-restart-trackpad` | `core/system/scripts` | core/home/config/nomarchy-skill/SKILL.md | `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-wifi` | `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-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-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-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-write` | `features/scripts/utils` | features/scripts/utils/nomarchy-welcome | `kept` | |
| `nomarchy-sudo-keepalive` | `core/system/scripts` | — | `unused?` | |
| `nomarchy-sudo-passwordless-toggle` | `core/system/scripts` | — | `unused?` | |
| `nomarchy-sudo-reset` | `core/system/scripts` | — | `unused?` | |
| `nomarchy-sudo-passwordless-toggle` | `core/system/scripts` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-sudo-reset` | `core/system/scripts` | core/home/config/nomarchy-skill/SKILL.md | `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-sync` | `features/scripts/utils` | features/scripts/utils/nomarchy-backup,README.md | `kept` | |
@@ -150,15 +140,16 @@ 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-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-theme` | `features/scripts/utils` | core/home/config/nomarchy/default/elephant/nomarchy_background_selector.lua,core/home/config/nomarchy/default/elephant/nomarchy_themes.lua, +16 more | `kept` | |
| `nomarchy-theme-bg-install` | `themes/engine/scripts` | — | `unused?` | |
| `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` | 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-set` | `themes/engine/scripts` | core/home/config/nomarchy/default/elephant/nomarchy_background_selector.lua | `kept` | |
| `nomarchy-theme-current` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md | `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,themes/engine/scripts/nomarchy-theme-next | `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-refresh` | `themes/engine/scripts` | — | `unused?` | |
| `nomarchy-theme-remove` | `themes/engine/scripts` | — | `unused?` | |
| `nomarchy-theme-list` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-theme, +2 more | `kept` | |
| `nomarchy-theme-next` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-theme-refresh` | `themes/engine/scripts` | core/home/config/nomarchy-skill/SKILL.md | `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-asus-rog` | `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-themes-prebuild` | `themes/engine/scripts` | installer/install.sh | `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-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-screensaver` | `features/scripts/utils` | 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-tui-install` | `features/scripts/utils` | features/scripts/utils/nomarchy-tui-remove-all | `kept` | |
| `nomarchy-tui-remove` | `features/scripts/utils` | — | `unused?` | |
| `nomarchy-tui-remove-all` | `features/scripts/utils` | — | `unused?` | |
| `nomarchy-tz-select` | `core/system/scripts` | features/desktop/waybar/config/config.jsonc,features/scripts/utils/nomarchy-menu | `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` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `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, +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-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-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-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-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-remove` | `features/scripts/utils` | — | `unused?` | |
| `nomarchy-voxtype-status` | `features/scripts/utils` | features/desktop/waybar/config/config.jsonc,features/desktop/waybar/themes/summer-night/config.jsonc | `kept` | |
| `nomarchy-wallpaper` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/autostart.conf,features/desktop/hyprland/default.nix, +2 more | `kept` | |
| `nomarchy-webapp-handler-hey` | `features/scripts/utils` | — | `unused?` | |
| `nomarchy-webapp-handler-zoom` | `features/scripts/utils` | — | `unused?` | |
| `nomarchy-webapp-install` | `features/scripts/utils` | features/scripts/utils/nomarchy-webapp-remove-all | `kept` | |
| `nomarchy-webapp-remove` | `features/scripts/utils` | — | `unused?` | |
| `nomarchy-webapp-remove-all` | `features/scripts/utils` | — | `unused?` | |
| `nomarchy-welcome` | `features/scripts/utils` | — | `unused?` | |
| `nomarchy-wifi-powersave` | `core/system/scripts` | — | `unused?` | |
| `nomarchy-windows-vm` | `features/scripts/utils` | — | `unused?` | |
| `nomarchy-voxtype-remove` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `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-webapp-install` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-webapp-remove-all | `kept` | |
| `nomarchy-webapp-remove` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-webapp-remove-all` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
| `nomarchy-welcome` | `features/scripts/utils` | core/home/config/nomarchy/default/hypr/autostart.conf,lib/state-schema.nix | `kept` | |
| `nomarchy-wifi-powersave` | `core/system/scripts` | features/scripts/utils/nomarchy-sys-update,installer/install.sh | `kept` | |
| `nomarchy-windows-vm` | `features/scripts/utils` | core/home/config/nomarchy-skill/SKILL.md | `kept` | |
## Missing references
@@ -205,36 +194,6 @@ Tokens grepped from `core/`, `features/`, `themes/`, `installer/`, `hosts/`, `bi
| Token | Referenced in | Status |
| --- | --- | --- |
| `nomarchy-brightness` | core/home/config/nomarchy/default/hypr/bindings/media.conf,core/home/config/nomarchy/default/hypr/bindings/utilities.conf, +3 more | `missing` |
| `nomarchy-cmd` | core/home/config/nomarchy/default/hypr/bindings/media.conf,core/home/config/nomarchy/default/hypr/bindings/utilities.conf, +14 more | `missing` |
| `nomarchy-dev` | core/home/config/nomarchy-skill/SKILL.md | `missing` |
| `nomarchy-dryrun` | installer/install.sh | `missing` |
| `nomarchy-font-selector` | features/scripts/utils/nomarchy-font,themes/engine/switcher.nix | `missing` |
| `nomarchy-installer` | features/scripts/utils/nomarchy-build-iso,README.md | `missing` |
| `nomarchy-launch` | core/home/config/nomarchy/default/hypr/bindings/clipboard.conf,core/home/config/nomarchy/default/hypr/bindings/utilities.conf, +24 more | `missing` |
| `nomarchy-live` | features/scripts/utils/nomarchy-build-live-iso,features/scripts/utils/nomarchy-on-boot, +1 more | `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-pkg` | bin/utils/nomarchy-docs-scripts,core/home/config/nomarchy-skill/SKILL.md, +8 more | `missing` |
| `nomarchy-plymouth` | themes/engine/plymouth.nix | `missing` |
| `nomarchy-refresh` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-refresh-config, +1 more | `missing` |
| `nomarchy-restart` | core/home/config/nomarchy-skill/SKILL.md,core/system/scripts/nomarchy-restart-xcompose, +10 more | `missing` |
| `nomarchy-screenrecord-filename` | features/scripts/utils/nomarchy-cmd-screenrecord | `missing` |
| `nomarchy-scripts` | core/system/scripts/nomarchy-preflight-migration,features/scripts/battery-monitor.nix, +1 more | `missing` |
| `nomarchy-sddm-theme` | themes/engine/sddm.nix | `missing` |
| `nomarchy-setup` | core/home/config/nomarchy-skill/SKILL.md,features/scripts/utils/nomarchy-menu | `missing` |
| `nomarchy-system-scripts` | core/system/hardware.nix,core/system/scripts-derivation.nix, +1 more | `missing` |
| `nomarchy-theme-engine-scripts` | themes/engine/scripts.nix | `missing` |
| `nomarchy-theme-next` | core/home/config/nomarchy-skill/SKILL.md | `missing` |
| `nomarchy-theme-selector` | features/scripts/utils/nomarchy-theme,themes/engine/switcher.nix | `missing` |
| `nomarchy-themes-no-images` | themes/engine/files.nix | `missing` |
| `nomarchy-toggle` | core/home/config/nomarchy/default/hypr/bindings/utilities.conf,core/home/config/nomarchy-skill/SKILL.md, +3 more | `missing` |
| `nomarchy-vm` | features/scripts/utils/nomarchy-test-vm | `missing` |
| `nomarchy-wallpaper-selector` | features/scripts/utils/nomarchy-wallpaper,themes/engine/switcher.nix | `missing` |
| `nomarchy-webapp-handler` | features/scripts/utils/nomarchy-webapp-remove,features/scripts/utils/nomarchy-webapp-remove-all | `missing` |
| `nomarchy-windows` | features/scripts/utils/nomarchy-windows-vm | `missing` |
## Menu items
@@ -295,7 +254,6 @@ Walked from `features/scripts/utils/nomarchy-menu`. Each `case` arm in a `show_*
| `show_setup_config_menu` | Waybar | `nomarchy-restart-waybar` | `kept` |
| `show_setup_config_menu` | XCompose | `nomarchy-restart-xcompose` | `kept` |
| `show_setup_config_menu` | Overrides | `_(inline)_` | `kept` |
| `show_setup_system_menu` | Suspend | `nomarchy-toggle-suspend` | `kept` |
| `show_update_menu` | Nomarchy | `nomarchy-update` | `kept` |
| `show_update_menu` | Themes | `nomarchy-theme-update` | `kept` |
| `show_update_menu` | Process | `_(inline)_` | `kept` |
@@ -316,7 +274,7 @@ Walked from `features/scripts/utils/nomarchy-menu`. Each `case` arm in a `show_*
| `show_update_password_menu` | User | `_(inline)_` | `kept` |
| `show_system_menu` | Screensaver | `nomarchy-launch-screensaver` | `kept` |
| `show_system_menu` | Lock | `nomarchy-lock-screen` | `kept` |
| `show_system_menu` | Suspend | `_(inline)_` | `kept` |
| `show_system_menu` | Suspend | `nomarchy-toggle-suspend` | `kept` |
| `show_system_menu` | Hibernate | `_(inline)_` | `kept` |
| `show_system_menu` | Logout | `nomarchy-system-logout` | `kept` |
| `show_system_menu` | Restart | `nomarchy-system-reboot` | `kept` |

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`).
- `nixosConfigurations`: Defines pre-configured targets like `nomarchy-installer`, `nomarchy-live`, and a testing `vm`.
- **`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.
- **`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.
- **`options.nix`**: Defines the `nomarchy` user options (Toggles, Theme, Fonts, etc.).
- **`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`.
- **`configs.nix`**: Manages static configuration files and directories in `~/.config/`.
- **`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/`. Honors `nomarchy.configOverrides` as a bulk redirect to a replacement config dir.
- **`bash.nix`**: Shell environment, aliases, and specialized `env-update` hooks.
- **`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).
@@ -93,7 +102,6 @@ The theming system is the "soul" of Nomarchy, providing dynamic, consistent aest
### `themes/engine/` (The Logic)
- **`loader.nix`**: The core theme loader. It reads the active theme from state and selectively deploys app-specific themed configs (e.g., `btop.theme`, `kitty.conf`) to `~/.config/`.
- **`stylix.nix`**: Integration with Stylix for base color palette and wallpaper management.
- **`switcher.nix`**: Provides the `nomarchy-theme-selector` and `nomarchy-wallpaper-selector` tools.
- **`plymouth.nix`** & **`sddm.nix`**: System-level theming for the boot screen and login manager.
### `themes/palettes/` (The Data)
@@ -126,7 +134,6 @@ The `lib/` directory provides centralized logic and data structures to maintain
### `installer/` (Bootstrap)
- **`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-btrfs-luks.nix`**: A simpler reference layout for disk management (not used by the installer).
### `hosts/` (Targets)
- **`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,7 +2,7 @@
#* Name of a btop++/bpytop/bashtop formatted ".theme" file, "Default" and "TTY" for builtin themes.
#* Themes should be placed in "../share/btop/themes" relative to binary or "$HOME/.config/btop/themes"
color_theme = "current"
color_theme = "nomarchy"
#* If the theme set background should be shown, set to False if you want terminal background transparency.
theme_background = True

View File

@@ -7,7 +7,26 @@ let
else
{ name = "Default Dark Modern"; };
# Development extensions that match the system theme
# Theme extensions matching palette vscode.json `extension` fields. Always
# installed because workbench.colorTheme is set unconditionally from the
# active palette — without the matching extension VSCode silently falls
# back to its default theme. Only the extensions available in
# pkgs.vscode-extensions are listed. Palettes whose theme extension is on
# the VSCode marketplace but not packaged in nixpkgs (sainnhe.everforest —
# affects the DEFAULT summer-night palette — plus qufiwefefwoyn.kanagawa,
# monokai.theme-monokai-pro-vscode, oldjobobo.*, Bjarne.*,
# shadesOfBuntu.flexoki-light, jovejonovski.ocean-green, TahaYVR.matteblack)
# still break and need pkgs.vscode-utils.extensionFromVscodeMarketplace —
# tracked in ROADMAP Later.
themeExtensions = with pkgs.vscode-extensions; [
catppuccin.catppuccin-vsc # catppuccin, catppuccin-latte
enkia.tokyo-night # tokyo-night
arcticicestudio.nord-visual-studio-code # nord
mvllow.rose-pine # rose-pine
jdinhlife.gruvbox # gruvbox
];
# Development extensions — opt-in via nomarchy.vscode.devExtensions.
devExtensions = with pkgs.vscode-extensions; [
# Language support
ms-python.python
@@ -22,13 +41,6 @@ let
esbenp.prettier-vscode
dbaeumer.vscode-eslint
bradlc.vscode-tailwindcss
# Theme extensions (provide color themes matching nomarchy palettes)
catppuccin.catppuccin-vsc
enkia.tokyo-night
arcticicestudio.nord-visual-studio-code
sainnhe.everforest
mvllow.rose-pine
];
in
{
@@ -51,7 +63,10 @@ in
"editor.fontFamily" = "'${config.nomarchy.fonts.monospace}', 'Droid Sans Mono', monospace";
"terminal.integrated.fontFamily" = config.nomarchy.fonts.monospace;
};
extensions = lib.mkIf config.nomarchy.vscode.devExtensions (lib.mkDefault devExtensions);
extensions = lib.mkDefault (
themeExtensions
++ lib.optionals config.nomarchy.vscode.devExtensions devExtensions
);
};
};
}

View File

@@ -1,6 +1,8 @@
{ config, pkgs, lib, ... }:
{
home.packages = [ pkgs.lua ];
programs.walker = {
enable = lib.mkDefault true;
runAsService = lib.mkDefault true;

View File

@@ -2,14 +2,6 @@
let
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
{
imports = [
@@ -21,7 +13,6 @@ in
../themes/engine/files.nix
../themes/engine/scripts.nix
../themes/engine/stylix.nix
../themes/engine/switcher.nix
./apps/alacritty/default.nix
./apps/btop/default.nix
./apps/chromium/default.nix

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/monitors.conf".source = lib.mkDefault ./config/monitors.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/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.
# exec-once fails silently (black screen with no visible error) when timing

View File

@@ -0,0 +1,72 @@
$bg_dim = 0xffefebd4
$bg0 = 0xfffdf6e3
$bg1 = 0xfff4f0d9
$bg2 = 0xffefebd4
$bg3 = 0xffe6e2cc
$bg4 = 0xffe0dcc7
$bg5 = 0xffbdc3af
$bg_visual = 0xffeaedc8
$bg_red = 0xfffbe3da
$bg_green = 0xfff0f1d2
$bg_blue = 0xffe9f0e9
$bg_yellow = 0xfffaedcd
$fg = 0xff5c6a72
$red = 0xfff85552
$orange = 0xfff57d26
$yellow = 0xffdfa000
$green = 0xff8da101
$aqua = 0xff35a77c
$blue = 0xff3a94c5
$purple = 0xffdf69ba
$grey0 = 0xffa6b0a0
$grey1 = 0xff939f91
$grey2 = 0xff829181
general {
gaps_in = 6
gaps_out = 12
border_size = 3
col.active_border = $fg
col.inactive_border = $bg5
layout = dwindle
resize_on_border = true
}
decoration {
rounding = 10
blur {
enabled = true
size = 5
passes = 3
new_optimizations = true
ignore_opacity = true
}
shadow {
enabled = true
range = 20
render_power = 3
color = rgb(2e3538)
color_inactive = rgb(61694f)
scale = 1.0
offset = 0 10
}
}
animations {
enabled = true
bezier = overshot, 0.05, 0.9, 0.1, 1.05
bezier = smoothOut, 0.36, 0, 0.66, -0.56
bezier = smoothIn, 0.25, 1, 0.5, 1
animation = windows, 1, 3, overshot, slide
animation = windowsOut, 1, 3, smoothOut, slide
animation = windowsMove, 1, 3, default
animation = border, 1, 3, default
animation = fade, 1, 3, smoothIn
animation = workspaces, 1, 3, smoothIn, slide
}

View File

@@ -1,2 +0,0 @@
@define-color foreground #cdd6f4;
@define-color background #181824;

View File

@@ -1,2 +0,0 @@
@define-color foreground #d6e2ee;
@define-color background #213442;

View File

@@ -1,14 +0,0 @@
@define-color background #2e3440;
@define-color foreground #d8dee9;
@define-color accent #88c0d0;
/* Base style for Nord */
* {
font-family: JetBrainsMono Nerd Font, FontAwesome;
font-size: 13px;
}
window#waybar {
background-color: @background;
color: @foreground;
}

View File

@@ -1,3 +0,0 @@
@define-color bg #00172e;
@define-color foreground #f6dcac;
@define-color background alpha(@bg, 0.8);

View File

@@ -0,0 +1,124 @@
{
"margin-top": 0,
"margin-left": 120,
"margin-bottom": 0,
"margin-right": 120,
"height": 60,
"layer": "top",
"position": "top",
"output": "DP-2",
"spacing": 15,
"modules-left": ["custom/launcher", "clock", "clock#date"],
"modules-center": ["wlr/workspaces"],
"modules-right": ["pulseaudio", "network", "battery", "custom/powermenu"],
"wlr/workspaces": {
"disable-scroll": true,
"all-outputs": true,
"on-click": "activate",
"on-scroll-up": "hyprctl dispatch workspace e+1",
"on-scroll-down": "hyprctl dispatch workspace e-1",
"persistent_workspaces": {
"1": [],
"2": [],
"3": [],
"4": [],
"5": [],
"6": [],
"7": [],
"8": [],
"9": [],
"10": []
}
},
"custom/launcher": {
"interval": "once",
"format": "󰣇",
"on-click": "pkill wofi || wofi --show drun --term=kitty --width=20% --height=50% --columns 1 -I -s ~/.config/wofi/themes/everforest-light.css -o $MAIN_DISPLAY",
"tooltip": false
},
"backlight": {
"device": "nvidia_0",
"max-length": "4",
"format": "{icon}",
"tooltip-format": "{percent}%",
"format-icons": ["","","","", "", "", ""],
"on-click": "",
"on-scroll-up": "brightnessctl set 10%-",
"on-scroll-down": "brightnessctl set +10%"
},
"memory": {
"interval": 30,
"format": " {}%",
"format-alt":" {used:0.1f}G",
"max-length": 10
},
"custom/dunst": {
"exec": "~/.config/waybar/scripts/dunst.sh",
"on-click": "dunstctl set-paused toggle",
"restart-interval": 1,
"tooltip": false
},
"pulseaudio": {
"format": "{icon} {volume}%",
"format-bluetooth": "{icon}  {volume}%",
"format-bluetooth-muted": "婢  muted",
"format-muted": "婢 muted",
"format-icons": {
"headphone": "",
"hands-free": "",
"headset": "",
"phone": "",
"portable": "",
"car": "",
"default": ["", "", ""]
},
"on-click-right": "pavucontrol",
"on-click": "pactl set-sink-mute @DEFAULT_SINK@ toggle"
},
"network": {
"format-wifi": " {signalStrength}%",
"format-ethernet": " {signalStrength}%",
"format-disconnected": "󰤭",
"on-click": "sh ~/.config/wofi/scripts/wifimenu.sh"
},
"battery": {
"bat": "BAT0",
"adapter": "ADP0",
"interval": 60,
"states": {
"warning": 30,
"critical": 15
},
"max-length": 10,
"format": "{icon} {capacity}%",
"format-warning": "{icon} {capacity}%",
"format-critical": "{icon} {capacity}%",
"format-charging": " {capacity}%",
"format-plugged": " {capacity}%",
"format-alt": "{icon} {capacity}%",
"format-full": " 100%",
"format-icons": ["", "", "", "", "", "", "", "", "", ""]
},
"clock": {
"format": " {:%H:%M}"
},
"clock#date": {
"format": " {:%A, %B %d, %Y}"
},
"custom/powermenu": {
"format": "",
"on-click": "pkill wofi || sh .config/wofi/scripts/powermenu.sh 'everforest-light' '--height=17% -o $MAIN_DISPLAY'",
"tooltip": false
}
}

View File

@@ -0,0 +1,102 @@
@define-color bg_dim #efebd4;
@define-color bg0 #fdf6e3;
@define-color bg1 #f4f0d9;
@define-color bg2 #efebd4;
@define-color bg3 #e6e2cc;
@define-color bg4 #e0dcc7;
@define-color bg5 #bdc3af;
@define-color fg #5c6a72;
@define-color red #f85552;
@define-color orange #f57d26;
@define-color yellow #dfa000;
@define-color green #8da101;
@define-color aqua #35a77c;
@define-color blue #3a94c5;
@define-color purple #df69ba;
@define-color grey0 #a6b0a0;
@define-color grey1 #939f91;
@define-color grey2 #829181;
/* margin: top right bottom left */
/* Spacing outside the element */
/* padding: top right bottom left */
/* Spacing inside the element */
* {
font-family: JetBrainsMono Nerd Font, FontAwesome;
font-size: 16px;
font-weight: bold;
}
window#waybar {
background-color: @fg;
color: @bg0;
transition-property: background-color;
transition-duration: 0.5s;
border-radius: 0px 0px 15px 15px;
transition-duration: .5s;
border-bottom-width: 5px;
border-bottom-color: #3d4a52;
border-bottom-style: solid;
}
#custom-launcher,
#clock,
#clock-date,
#workspaces,
#pulseaudio,
#network,
#battery,
#custom-powermenu {
background-color: @bg0;
color: @fg;
padding-left: 10px;
padding-right: 10px;
margin-top: 7px;
margin-bottom: 12px;
border-radius: 10px;
border-bottom-width: 5px;
border-bottom-color: @bg5;
border-bottom-style: solid;
}
#workspaces {
padding: 0px;
}
#workspaces button.active {
background-color: @blue;
color: @bg0;
border-radius: 10px;
margin-bottom: -5px;
border-bottom-width: 5px;
border-bottom-color: #1e6791;
border-bottom-style: solid;
}
#custom-launcher {
background-color: @green;
color: @bg0;
border-bottom-color: #5d6b00;
margin-left: 15px;
padding-left: 20px;
padding-right: 21px;
}
#custom-powermenu {
background-color: @red;
color: @bg0;
border-bottom-color: #b32f2c;
margin-right: 15px;
padding-left: 20px;
padding-right: 23px;
}

View File

@@ -71,7 +71,6 @@ let
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_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;
};

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
# `nomarchy-pkg-` that come from glob references (`for c in nomarchy-pkg-*`).
# 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[@]}" \
"${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)
# 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 --------------------------------------------------------

17
features/scripts/utils/nomarchy-env-update Normal file → Executable file
View File

@@ -1,12 +1,13 @@
#!/usr/bin/env bash
# Nomarchy Environment Update Script
# 1. Runs the pre-flight state migration
# 2. Applies user-level Home Manager changes (Standalone)
# Standalone Home Manager iteration path. Use this for fast dotfile and
# theme changes that don't need a full system rebuild. For first-boot
# dotfiles and any system-level change, the NixOS module activation
# from `sudo nixos-rebuild switch` is the source of truth.
set -e
# Detect the repository location
if [ -f "/etc/nixos/flake.nix" ]; then
REPO_DIR="/etc/nixos"
elif [ -f "/etc/nomarchy/flake.nix" ]; then
@@ -16,13 +17,19 @@ else
exit 1
fi
# Use the pre-flight migration script to ensure the state is synced before evaluation
if command -v nomarchy-preflight-migration >/dev/null 2>&1; then
nomarchy-preflight-migration
fi
# Apply Home Manager changes from the local flake (Standalone)
echo "Applying user-level changes from $REPO_DIR#$USER..."
if command -v home-manager >/dev/null 2>&1; then
home-manager switch --flake "$REPO_DIR#$USER" --impure
else
# Bootstrap path: HM hasn't put `home-manager` on PATH yet (e.g. running
# straight after a partial install). Pull it from the flake registry.
nix --extra-experimental-features 'nix-command flakes' \
run 'home-manager/release-25.11' \
-- switch --flake "$REPO_DIR#$USER" --impure
fi
echo "Environment update complete."

View File

@@ -15,6 +15,9 @@ case "$COMMAND" in
nomarchy-font-list
;;
selector|*)
nomarchy-font-selector
theme=$(echo -e "$(nomarchy-font-list)" | nomarchy-launch-walker --dmenu --width 350 -p "Font...")
if [[ -n "$theme" && "$theme" != "CNCLD" ]]; then
nomarchy-font-set "$theme"
fi
;;
esac

View File

@@ -13,7 +13,7 @@ pgrep -f org.nomarchy.screensaver && exit 0
# Allow screensaver to be turned off but also force started
# Skip if screensaver is disabled in configuration
if [[ $NNOMARCHY_TOGGLE_SCREENSAVER == "false" ]] && [[ $1 != "force" ]]; then
if [[ $NOMARCHY_TOGGLE_SCREENSAVER == "false" ]] && [[ $1 != "force" ]]; then
exit 0
fi

View File

@@ -1,8 +1,8 @@
#!/bin/bash
set -e
# Launch the Nomarchy wifi controls (provided by the Impala TUI).
# Launch the Nomarchy wifi controls (using nmtui).
# Attempts to unblock the wifi service first in case it should be been blocked.
rfkill unblock wifi
nomarchy-launch-or-focus-tui impala
alacritty -e nmtui

View File

@@ -1,10 +1,18 @@
#!/usr/bin/env bash
set -e
# Nomarchy Manual Script
# Opens the Nomarchy manual in the default web browser.
# Open the Nomarchy docs index in the default handler.
# 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"
xdg-open "$URL"
if [[ -f "$README" ]]; then
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

@@ -263,12 +263,6 @@ show_setup_config_menu() {
show_setup_system_menu() {
local options=""
if [[ $NNOMARCHY_TOGGLE_SUSPEND == "false" ]]; then
options="$options󰒲 Enable Suspend"
else
options="$options󰒲 Disable Suspend"
fi
if nomarchy-hibernation-available; then
options="$options\n󰤁 Disable Hibernate"
else
@@ -276,7 +270,6 @@ show_setup_system_menu() {
fi
case $(menu "System" "$options") in
*Suspend*) nomarchy-toggle-suspend; back_to show_setup_system_menu ;;
*"Enable Hibernate"*) present_terminal nomarchy-hibernation-setup ;;
*"Disable Hibernate"*) present_terminal nomarchy-hibernation-remove ;;
*) back_to show_setup_menu ;;
@@ -334,14 +327,14 @@ show_about() {
show_system_menu() {
local options="󱄄 Screensaver\n Lock"
[[ $NNOMARCHY_TOGGLE_SUSPEND != "false" ]] && options="$options\n󰒲 Suspend"
[[ $NOMARCHY_TOGGLE_SUSPEND != "false" ]] && options="$options\n󰒲 Suspend"
nomarchy-hibernation-available && options="$options\n󰤁 Hibernate"
options="$options\n󰍃 Logout\n󰜉 Restart\n󰐥 Shutdown"
case $(menu "System" "$options") in
*Screensaver*) nomarchy-launch-screensaver force ;;
*Lock*) nomarchy-lock-screen ;;
*Suspend*) systemctl suspend ;;
*Suspend*) nomarchy-toggle-suspend ;;
*Hibernate*) systemctl hibernate ;;
*Logout*) nomarchy-system-logout ;;
*Restart*) nomarchy-system-reboot ;;

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

@@ -19,63 +19,6 @@ if nomarchy-hw-asus-rog; then
nomarchy-theme-set-keyboard-asus-rog >/dev/null 2>&1
fi
# 3. Declarative hardware configuration check (nixos-hardware)
# This part ensures that if we are on an installed system, the correct
# nixos-hardware module is selected in the configuration.
# Skip this in the Live ISO environment
if [[ $USER == "nixos" ]] || [[ -f /etc/nixos/hosts/nomarchy-live.nix ]]; then
# 3. Hardware detection
# Superseded by the installer's hardware-db.sh and declarative selection.
exit 0
fi
HW_FILE="/etc/nixos/hardware-selection.nix"
if [ -w "$HW_FILE" ]; then
PRODUCT_NAME=$(cat /sys/class/dmi/id/product_name 2>/dev/null || echo "Unknown")
BOARD_NAME=$(cat /sys/class/dmi/id/board_name 2>/dev/null || echo "Unknown")
CPU_VENDOR=$(lscpu | grep "Vendor ID" | awk '{print $3}')
NEW_HW_MODULES=""
if [[ "$CPU_VENDOR" == "AuthenticAMD" ]]; then
NEW_HW_MODULES="inputs.nixos-hardware.nixosModules.common-cpu-amd"
elif [[ "$CPU_VENDOR" == "GenuineIntel" ]]; then
NEW_HW_MODULES="inputs.nixos-hardware.nixosModules.common-cpu-intel"
fi
# Auto-detect specific known models for nixos-hardware
if echo "$PRODUCT_NAME" | grep -qi "XPS 15 9500"; then
NEW_HW_MODULES="$NEW_HW_MODULES\n inputs.nixos-hardware.nixosModules.dell-xps-15-9500"
elif echo "$PRODUCT_NAME" | grep -qi "XPS 13"; then
NEW_HW_MODULES="$NEW_HW_MODULES\n inputs.nixos-hardware.nixosModules.dell-xps-13-9300" # fallback example
elif echo "$PRODUCT_NAME" | grep -qi "Framework Laptop 16"; then
NEW_HW_MODULES="$NEW_HW_MODULES\n inputs.nixos-hardware.nixosModules.framework-16-7040-amd"
elif echo "$PRODUCT_NAME" | grep -qi "Framework Laptop 13"; then
NEW_HW_MODULES="$NEW_HW_MODULES\n inputs.nixos-hardware.nixosModules.framework-13-7040-amd"
elif echo "$PRODUCT_NAME" | grep -qi "Surface"; then
NEW_HW_MODULES="$NEW_HW_MODULES\n inputs.nixos-hardware.nixosModules.microsoft-surface-pro-8" # fallback example
elif echo "$PRODUCT_NAME" | grep -qi "Zephyrus G14"; then
NEW_HW_MODULES="$NEW_HW_MODULES\n inputs.nixos-hardware.nixosModules.asus-zephyrus-g14"
elif echo "$PRODUCT_NAME" | grep -qi "ThinkPad X1 Carbon"; then
NEW_HW_MODULES="$NEW_HW_MODULES\n inputs.nixos-hardware.nixosModules.lenovo-thinkpad-x1-carbon-gen9"
fi
# Check if the current HW file differs from our detection
if [ -n "$NEW_HW_MODULES" ] && ! grep -q "common-cpu" "$HW_FILE"; then
# This is a basic detection. We overwrite it if it's completely empty or missing common-cpu.
# It's better to let the user know, or auto-apply. We'll auto-apply for a smooth experience.
cat <<EOF > "$HW_FILE.tmp"
{ inputs, ... }:
{
imports = [
$NEW_HW_MODULES
];
}
EOF
if ! cmp -s "$HW_FILE" "$HW_FILE.tmp"; then
mv "$HW_FILE.tmp" "$HW_FILE"
# We notify the user instead of running sys-update silently, as it requires root and time.
notify-send -u normal "Hardware Auto-Detection" "New hardware profile detected. Please run 'sys-update' when ready."
else
rm "$HW_FILE.tmp"
fi
fi
fi

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,34 +0,0 @@
#!/bin/bash
set -e
COMMAND="$1"
NOMARCHY_PATH=${NOMARCHY_PATH:-$HOME/.local/share/nomarchy}
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
fi
# Apply NixOS changes from the local flake
echo "Applying system-level changes from $REPO_DIR..."
sudo nixos-rebuild switch --flake "$REPO_DIR#default" --impure
# The installer generates `nixosConfigurations.<hostname>` (see
# installer/install.sh: `nixosConfigurations.$HOSTNAME`), so the flake target
# 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."

View File

@@ -15,6 +15,6 @@ case "$COMMAND" in
nomarchy-theme-list
;;
selector|*)
nomarchy-theme-selector
nomarchy-launch-walker -m menus:nomarchythemes --width 800 --minheight 400
;;
esac

View File

@@ -5,6 +5,6 @@ set -e
# Prints the current Nomarchy version based on the upstream NixOS channel.
VERSION="25.11.0"
CODENAME="Markhor"
CODENAME="Sovereign"
echo "Nomarchy v${VERSION} (${CODENAME})"

View File

@@ -10,14 +10,12 @@ case "$COMMAND" in
set)
shift
# Wallpaper set usually involves writing to state.json and calling swww
# We can reuse the logic from nomarchy-wallpaper-selector if we extract it,
# but for now let's just trigger the selector.
nomarchy-wallpaper-selector
nomarchy-theme-bg-set "$@"
;;
next)
nomarchy-theme-bg-next
;;
selector|*)
nomarchy-wallpaper-selector
nomarchy-launch-walker -m menus:nomarchyBackgroundSelector --width 800 --minheight 400
;;
esac

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

@@ -21,57 +21,47 @@ fi
gum style \
--foreground 212 --border-foreground 212 --border double \
--align center --width 50 --margin "1 2" --padding "2 4" \
"Nomarchy" "The NixOS Distribution with Nomarchy Flavour"
"Nomarchy" "The Professional NixOS Desktop"
echo "Welcome! Let's personalize your new system."
echo ""
# 1. Select initial theme
echo "Step 1: Choose your starting theme"
nomarchy-theme-set "$(nomarchy-theme-list | gum filter --placeholder 'Select a theme...')"
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
echo "Step 2: Choose your preferred font"
nomarchy-font-set "$(nomarchy-font-list | gum filter --placeholder 'Select a font...')"
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
echo "Step 3: Choose your preferred panel position"
POSITION=$(gum choose "top" "bottom")
if [[ -n "$POSITION" ]]; then
nomarchy-state-write panelPosition "$POSITION"
# 4. Starter home.nix
echo ""
echo "Step 4: Starter home.nix"
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)
# Skip system-modifying steps in the Live ISO environment
if [[ "$USER" == "nixos" ]]; then
echo ""
echo "Step 5: Git Repository Check"
echo "Live ISO detected. Skipping home.nix generation and git repo check."
nomarchy-env-update
nomarchy-state-write welcome_done true --type bool
gum style --foreground 82 "Setup complete! Enjoy your Nomarchy experience."
sleep 3
exit 0
fi
# 4. Setup Local Repo (Crucial for nomarchy-env-update to work)
echo ""
echo "Step 4: Git Repository Check"
echo "Nomarchy relies on a local git repository for declarative updates."
if [ ! -d "/etc/nixos/.git" ]; then
echo "Warning: /etc/nixos is not a git repository. Declarative updates might fail."
@@ -82,7 +72,10 @@ if [ ! -d "/etc/nixos/.git" ]; then
fi
fi
# 6. Success
# 5. Success
echo ""
echo "Applying all changes..."
nomarchy-env-update
nomarchy-state-write welcome_done true --type bool
# Remove legacy flag file if it exists

View File

@@ -40,12 +40,6 @@ install_windows() {
mkdir -p "$HOME/.windows"
mkdir -p "$HOME/.config/windows"
mkdir -p "$HOME/.local/share/applications/icons"
# Install Windows VM icon and desktop file
if [[ -f $NOMARCHY_PATH/applications/icons/windows.png ]]; then
cp "$NOMARCHY_PATH/applications/icons/windows.png" "$HOME/.local/share/applications/icons/windows.png"
fi
cat << EOF | tee "$HOME/.local/share/applications/windows-vm.desktop" > /dev/null
[Desktop Entry]

View File

@@ -173,6 +173,7 @@
wayland.windowManager.hyprland.extraConfig = nixpkgs.lib.mkAfter ''
monitor = , highres, auto, 1
exec-once = sh -c 'sleep 3; notify-send -u critical -t 0 "Welcome to Nomarchy" "Run \`sudo /etc/install.sh\` in the open terminal or \`--dry-run\` to preview."'
exec-once = sh -c 'sleep 4; alacritty --title "Nomarchy Installer" -e bash -lc "echo; echo \"Welcome to the Nomarchy live ISO.\"; echo; echo \" sudo /etc/install.sh # install\"; echo \" sudo /etc/install.sh --dry-run # preview only\"; echo \" sudo /etc/install.sh --resume # resume after an interrupt\"; echo; exec bash"'
'';
};

View File

@@ -22,6 +22,7 @@
parted
btrfs-progs
cryptsetup
mkpasswd
inputs.disko.packages.${pkgs.stdenv.hostPlatform.system}.disko
(pkgs.makeDesktopItem {
name = "install-nomarchy";
@@ -64,6 +65,22 @@
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
services.displayManager.autoLogin.enable = true;
services.displayManager.autoLogin.user = "nixos";
# Allow passwordless sudo for the live user
security.sudo.wheelNeedsPassword = false;
# Override the upstream installer helpLine (says "NixOS", points nowhere
# useful for us). Shown on every TTY before login and again as the MOTD.
services.getty.helpLine = lib.mkForce ''

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,6 +19,7 @@
# function arguments are the right shape.
{ mainDrive
, extraDrives ? []
, ...
}:
let
@@ -43,8 +44,8 @@ let
content = {
type = "luks";
name = extraLuks drive;
passwordFile = "/tmp/nomarchy-luks.key";
settings.allowDiscards = true;
settings.passwordFile = "/dev/shm/nomarchy-luks.key";
content.type = "btrfs";
};
};
@@ -69,16 +70,24 @@ let
# snapshot) targets the right /dev/mapper entry.
mainLuksName = if hasExtras then "crypted_main" else "crypted";
# Multi-device BTRFS on LUKS requires that we explicitly tell systemd-initrd
# to wait for ALL LUKS devices to be decrypted before attempting to mount
# the filesystem, otherwise it might try to mount as soon as the first one
# appears and then hang or fail.
allLuksNames = [ mainLuksName ] ++ map extraLuks extraDrives;
btrfsMountOptions = [ "compress=zstd" "noatime" "x-systemd.device-timeout=0" ]
++ map (n: "x-systemd.requires=/dev/mapper/" + n) allLuksNames;
rootBtrfs = {
type = "btrfs";
extraArgs = btrfsExtraArgs;
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" ]; };
"@snapshots" = { mountpoint = "/.snapshots"; mountOptions = [ "compress=zstd" "noatime" ]; };
"@" = { mountpoint = "/"; mountOptions = btrfsMountOptions; };
"@persist" = { mountpoint = "/persist"; mountOptions = btrfsMountOptions; };
"@home" = { mountpoint = "/home"; mountOptions = btrfsMountOptions; };
"@nix" = { mountpoint = "/nix"; mountOptions = btrfsMountOptions; };
"@log" = { mountpoint = "/var/log"; mountOptions = btrfsMountOptions; };
"@snapshots" = { mountpoint = "/.snapshots"; mountOptions = btrfsMountOptions; };
};
postCreateHook = ''
MNTPOINT=$(mktemp -d)
@@ -115,8 +124,8 @@ in {
content = {
type = "luks";
name = mainLuksName;
passwordFile = "/tmp/nomarchy-luks.key";
settings.allowDiscards = true;
settings.passwordFile = "/dev/shm/nomarchy-luks.key";
content = rootBtrfs;
};
};

View File

@@ -19,13 +19,17 @@
HARDWARE_DB=(
# 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 13.*AMD Ryzen AI 300|isFramework=true|framework-13-amd-ai-300-series"
"Framework|Laptop 13.*AMD Ryzen 7040|isFramework=true|framework-13-7040-amd"
"Framework|Laptop 13.*13th Gen Intel|isFramework=true|framework-13-13th-gen-intel"
"Framework|Laptop 13.*12th Gen Intel|isFramework=true|framework-13-12th-gen-intel"
"Framework|Laptop 13.*11th Gen Intel|isFramework=true|framework-13-11th-gen-intel"
"Framework|Laptop \(13.*|isFramework=true|framework-13-11th-gen-intel"
"Framework|Laptop 16.*Ryzen AI 300|isFramework=true|framework-16-amd-ai-300-series"
"Framework|Laptop 13.*Ryzen AI 300|isFramework=true|framework-amd-ai-300-series"
"Framework|Laptop 13.*Ryzen 7040|isFramework=true|framework-13-7040-amd"
"Framework|Laptop 13.*Core Ultra|isFramework=true|framework-intel-core-ultra-series1"
"Framework|Laptop 13.*13th Gen Intel|isFramework=true|framework-13th-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 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 7390|isXPS=true|dell-xps-13-7390"
"Dell|Precision 5530|_|dell-precision-5530"
"Dell|Latitude 7480|_|dell-latitude-7480"
"Dell|Latitude 5400|_|dell-latitude-5400"
"Dell|Latitude 7490|_|dell-latitude-7490"
"Dell|Latitude 7430|_|dell-latitude-7430"
"Dell|Latitude 7420|_|dell-latitude-7420"
# Lenovo ThinkPad --------------------------------------------------------
"LENOVO|ThinkPad X1 Carbon Gen 11|_|lenovo-thinkpad-x1-carbon-gen11"
"LENOVO|ThinkPad X1 Carbon Gen 10|_|lenovo-thinkpad-x1-carbon-gen10"
"LENOVO|ThinkPad X1 Carbon Gen 9|_|lenovo-thinkpad-x1-carbon-gen9"
# X1 Carbon: the per-gen modules are named "x1-Nth-gen", not
# "x1-carbon-genN" — both naming schemes appeared in nixos-hardware and
# 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 Nano|_|lenovo-thinkpad-x1-nano-gen1"
"LENOVO|ThinkPad T14 Gen 3|_|lenovo-thinkpad-t14-amd-gen3"
"LENOVO|ThinkPad T14 Gen 2|_|lenovo-thinkpad-t14-amd-gen2"
"LENOVO|ThinkPad T14 Gen 1|_|lenovo-thinkpad-t14-amd-gen1"
"LENOVO|ThinkPad T480|_|lenovo-thinkpad-t480"
"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 ------------------------------------------------------
# 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 8|_|microsoft-surface-pro-8"
"Microsoft|Surface Pro 7|_|microsoft-surface-pro-7"
"Microsoft|Surface Pro 6|_|microsoft-surface-pro-6"
"Microsoft|Surface Laptop 5|_|microsoft-surface-laptop-5"
"Microsoft|Surface Laptop 4|_|microsoft-surface-laptop-4"
"Microsoft|Surface Laptop 3|_|microsoft-surface-laptop-3"
"Microsoft|Surface Pro 8|_|microsoft-surface-pro-intel"
"Microsoft|Surface Pro 7|_|microsoft-surface-pro-intel"
"Microsoft|Surface Pro 6|_|microsoft-surface-pro-intel"
"Microsoft|Surface Pro 3|_|microsoft-surface-pro-3"
"Microsoft|Surface Laptop.*Ryzen|_|microsoft-surface-laptop-amd"
"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 Zephyrus G14.*2024|_|asus-zephyrus-ga403"
"ASUS.*|ROG Zephyrus G14.*2023|_|asus-zephyrus-ga402"
"ASUS.*|ROG Zephyrus G14.*2021|_|asus-zephyrus-ga401"
# ASUS ROG Ally / Zephyrus / Strix --------------------------------------
# Ally is the handheld (Z1 Extreme / Z1). RC71L is the original Ally,
# RC72LA is the Ally X — both share the same nixos-hardware module
# 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 Strix G15|_|asus-rog-strix-g513"
"ASUS.*|Zenbook UX|_|asus-zenbook-ux"
"ASUS.*|ROG Zephyrus.*GU603|_|asus-zephyrus-gu603h"
"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.*|MacBookPro15|isT2Mac=true|apple-t2"
@@ -86,9 +111,14 @@ HARDWARE_DB=(
"System76|Galago Pro.*|_|system76"
"System76|Pangolin.*|_|system76"
# Raspberry Pi (ARM) — listed for completeness; installer is x86_64 only -
# "Raspberry Pi|Raspberry Pi 5|_|raspberry-pi-5"
# "Raspberry Pi|Raspberry Pi 4|_|raspberry-pi-4"
# Devices nixos-hardware doesn't cover (yet) -----------------------------
# Listed here so future contributors know they're known-unsupported, not
# 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=""
LUKS_PASSWORD=""
USER_PASSWORD=""
USER_PASSWORD_HASH=""
TIMEZONE="UTC"
KEYMAP_LAYOUT=""
KEYMAP_VARIANT=""
@@ -73,6 +74,9 @@ Usage: install.sh [--dry-run] [--resume] [-h|--help]
Doesn't touch the disk, doesn't run nixos-install.
--resume Reuse answers from a previous interrupted run
(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.
USAGE
}
@@ -92,20 +96,83 @@ parse_args() {
# 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 --`
# 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() {
# The leading timestamp lets --resume surface "how old is this state?"
# and is parsed back via NOMARCHY_INSTALL_STATE_SAVED_AT.
{
echo "NOMARCHY_INSTALL_STATE_SAVED_AT=\"$(date -Iseconds)\""
declare -p \
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"
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() {
if [[ "$RESUME" == "true" ]] && [[ -f "$STATE_FILE" ]]; then
if [[ "$RESUME" != "true" ]]; then
return
fi
# --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"
info "Resumed from $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
}
@@ -128,7 +195,7 @@ clear_step_state() {
case "$1" in
select_disk) TARGET_DRIVE="" ;;
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_timezone) TIMEZONE="" ;;
select_hardware) HARDWARE_MODULES=""; NOMARCHY_HW_OPTS="" ;;
@@ -206,13 +273,32 @@ check_environment() {
# Capture the exact commit we're installing from. The generated flake
# pins `nomarchy.url` to this revision so the installed system can't
# 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 "")
fi
if [[ -n "$NOMARCHY_REV" ]]; then
success "Pinning Nomarchy to $NOMARCHY_REV"
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
# Check internet
@@ -452,8 +538,8 @@ configure_user() {
section "User Configuration"
if [[ -n "$USERNAME" && -n "$HOSTNAME" ]]; then
# Password check skipped in dry run or if already set
if [[ "$DRY_RUN" == "true" ]] || [[ -n "$USER_PASSWORD" ]]; then
# Password check skipped in dry run or if already hashed in this session.
if [[ "$DRY_RUN" == "true" ]] || [[ -n "$USER_PASSWORD_HASH" ]]; then
success "User $USERNAME @ $HOSTNAME configured"
return 0
fi
@@ -493,6 +579,9 @@ configure_user() {
if [[ "$DRY_RUN" == "true" ]]; then
info "Dry run: skipping user password prompt."
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
return
fi
@@ -521,6 +610,20 @@ configure_user() {
fi
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"
save_state
}
@@ -836,12 +939,12 @@ confirm_form_factor() {
fi
info "Auto-detected: $default"
rc=0
local rc=0
nrun gum confirm "Treat this machine as a $default?" || rc=$?
if [[ $rc -eq 0 ]]; then
FORM_FACTOR="$default"
else
if [[ $rc -eq 130 || $rc -eq 1 ]]; then return 130; fi
if [[ $rc -eq 130 ]]; then return 130; fi
FORM_FACTOR=$([[ "$default" == "laptop" ]] && echo desktop || echo laptop)
fi
success "Form factor: $FORM_FACTOR"
@@ -869,16 +972,27 @@ configure_impermanence() {
info "This provides a clean, reproducible system."
echo ""
rc=0
gum confirm "Enable Impermanence?" || rc=$?
if [[ $rc -eq 0 ]]; then
local rc=0
nrun gum confirm "Enable Impermanence?" || rc=$?
case "$rc" in
0)
ENABLE_IMPERMANENCE="true"
success "Impermanence enabled"
else
if [[ $rc -eq 130 || $rc -eq 1 ]]; then return 130; fi
;;
1)
ENABLE_IMPERMANENCE="false"
info "Impermanence disabled (traditional persistent root)"
fi
;;
130)
return 130
;;
*)
ENABLE_IMPERMANENCE="false"
info "Impermanence disabled (traditional persistent root)"
;;
esac
save_state
return 0
}
# ============================================================================
@@ -1078,11 +1192,15 @@ prewipe_target_drive() {
success "Pre-wipe complete"
}
_LUKS_KEY_PATH="/dev/shm/nomarchy-luks.key"
_LUKS_KEY_PATH="/tmp/nomarchy-luks.key"
# Wrap the disko invocation so a failure surfaces the last few lines of
# output and offers Retry / View full log / Abort. set -e is suspended for
# the disko call so we can inspect its exit code; restored on every path.
#
# By default disko's chatty output is hidden behind a `gum spin` spinner;
# the full log is captured and shown on failure or via `gum pager`. Set
# NOMARCHY_VERBOSE_DISKO=1 to stream disko output live instead.
run_disko_with_retry() {
local main_drive="$1"
local extras_nix="$2"
@@ -1092,13 +1210,35 @@ run_disko_with_retry() {
while true; do
local rc=0
if [[ "$NOMARCHY_VERBOSE_DISKO" == "1" ]]; then
set +e
disko --mode disko \
disko --mode destroy,format,mount \
--yes-wipe-all-disks \
--argstr mainDrive "$main_drive" \
--arg extraDrives "$extras_nix" \
"$disko_file" 2>&1 | tee "$log"
rc=${PIPESTATUS[0]}
set -e
else
# Silent path: run disko in the background, write all output to
# $log, and show a spinner. Use a status file for the exit code
# because `gum spin` doesn't propagate the wrapped command's rc.
local status_file
status_file=$(mktemp --suffix=.disko.rc)
set +e
nrun gum spin --spinner dot --title "Partitioning disk(s) with disko..." -- \
bash -c '
disko --mode destroy,format,mount \
--yes-wipe-all-disks \
--argstr mainDrive "$1" \
--arg extraDrives "$2" \
"$3" > "$4" 2>&1
echo $? > "$5"
' _ "$main_drive" "$extras_nix" "$disko_file" "$log" "$status_file"
set -e
rc=$(cat "$status_file" 2>/dev/null || echo 1)
rm -f "$status_file"
fi
if [[ $rc -eq 0 ]]; then
rm -f "$log"
@@ -1196,11 +1336,11 @@ execute_installation() {
info "Initializing git repository..."
(
cd /mnt/etc/nixos
nrun git git init -q
nrun git git add .
nrun git git config user.name "Nomarchy Installer"
nrun git git config user.email "installer@nomarchy"
nrun git git commit -qm "Initial Nomarchy configuration"
nrun git init -q
nrun git add .
nrun git config user.name "Nomarchy Installer"
nrun git config user.email "installer@nomarchy"
nrun git commit -qm "Initial Nomarchy configuration"
)
success "Git repository initialized"
@@ -1210,7 +1350,7 @@ execute_installation() {
mkdir -p /mnt/persist/etc
mv /mnt/etc/nixos /mnt/persist/etc/
mkdir -p /mnt/etc
ln -s /persist/etc/nixos /mnt/etc/nixos
ln -s ../persist/etc/nixos /mnt/etc/nixos
success "Impermanence configured"
fi
@@ -1219,24 +1359,10 @@ execute_installation() {
nixos-install --flake "/mnt/etc/nixos#$HOSTNAME" --no-root-passwd
success "Nomarchy installed"
# 9.8 Activate Home Manager for $USERNAME inside the new system so the
# user's first login already has Nomarchy's dotfiles. `home-manager
# switch` must run as the target user with a real $HOME, so we use
# `runuser` (sudo -u keeps the caller's HOME → files land in /root).
info "Activating Home Manager for $USERNAME..."
if nixos-enter --root /mnt -- bash -c "
set -e
install -d -o '$USERNAME' -g users -m 0755 '/home/$USERNAME'
runuser -u '$USERNAME' -- env HOME='/home/$USERNAME' \
nix --extra-experimental-features 'nix-command flakes' \
run 'home-manager/release-25.11' -- switch \
--flake '/etc/nixos#$USERNAME' --impure
"; then
success "Home Manager activated"
else
error "Home Manager activation failed (non-fatal)."
info "Run \`nomarchy-env-update\` after the first login to retry."
fi
# 9.8 (Removed) Standalone Home Manager activation in a chroot.
# HM is now wired as a NixOS module in the generated flake, so
# `nixos-install` above already activated the user's dotfiles as part
# of system activation. No chroot dance required.
# 9.9 Pre-flight: catch evaluation errors in the freshly-installed
# configuration *now*, while we can still fix them with `vi`, instead of
@@ -1251,6 +1377,11 @@ execute_installation() {
info "The system is installed; fix /etc/nixos before rebooting if possible."
fi
# 9.10 Set ownership of /etc/nixos to the main user
info "Setting ownership of /etc/nixos for $USERNAME..."
nixos-enter --root /mnt -- chown -R "$USERNAME:users" /etc/nixos
success "Ownership updated"
success "Installation complete!"
rm -f "$STATE_FILE"
}
@@ -1318,9 +1449,17 @@ EOF
# ============================================================================
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=""
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
local PROFILE_SYSTEM_OPTS=""
@@ -1368,23 +1507,22 @@ generate_flake_config() {
description = "My Nomarchy Configuration";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11";
nomarchy.url = "$nomarchy_url";
nixos-hardware.url = "github:NixOS/nixos-hardware/master";
home-manager = {
url = "github:nix-community/home-manager/release-25.11";
inputs.nixpkgs.follows = "nixpkgs";
};
};
# Two-track Nomarchy workflow:
# * System changes → sudo nixos-rebuild switch --flake /etc/nixos#$HOSTNAME
# * Dotfiles/themes → nomarchy-env-update (home-manager switch, no rebuild)
# * System + dotfiles (full) → sudo nixos-rebuild switch --flake /etc/nixos#$HOSTNAME
# * Dotfiles only (fast iter) → nomarchy-env-update (standalone home-manager switch)
#
# Both consume the same \`pkgs\` below so overlays and allowUnfree stay in
# sync across the two paths.
outputs = { self, nixpkgs, nomarchy, home-manager, nixos-hardware, ... }@inputs:
# Home Manager is wired both ways:
# - As a NixOS module under \`nixosConfigurations.$HOSTNAME\` so first boot
# after install already has dotfiles in place and every nixos-rebuild
# reconciles them. This is what makes the install actually usable.
# - As a standalone \`homeConfigurations.$USERNAME\` so theme switches and
# dotfile iteration don't require a full system rebuild.
outputs = { self, nomarchy, ... }@inputs:
let
inherit (nomarchy.inputs) nixpkgs home-manager;
system = "x86_64-linux";
pkgs = import nixpkgs {
inherit system;
@@ -1394,22 +1532,37 @@ generate_flake_config() {
in
{
nixosConfigurations.$HOSTNAME = nixpkgs.lib.nixosSystem {
inherit pkgs;
specialArgs = { inputs = nomarchy.inputs // inputs; };
specialArgs = { inputs = nomarchy.inputs; };
modules = [
{
nixpkgs.hostPlatform = system;
nixpkgs.overlays = [ nomarchy.overlays.default ];
}
./hardware-configuration.nix
./hardware-selection.nix
nomarchy.nixosModules.system
./system.nix
home-manager.nixosModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.backupFileExtension = "hm-bak";
home-manager.extraSpecialArgs = { inputs = nomarchy.inputs; };
home-manager.users.$USERNAME = {
imports = [ nomarchy.nixosModules.home ./home.nix ];
home.stateVersion = "25.11";
};
}
];
};
# Standalone Home Manager — \`home-manager switch --flake /etc/nixos#$USERNAME\`
# (which is what \`nomarchy-env-update\` runs). Kept separate from the
# NixOS config so dotfile/theme iterations don't rebuild the system.
# (which is what \`nomarchy-env-update\` runs). Kept alongside the NixOS
# module above so dotfile/theme iterations can skip a full system rebuild.
homeConfigurations.$USERNAME = home-manager.lib.homeManagerConfiguration {
inherit pkgs;
extraSpecialArgs = { inputs = nomarchy.inputs // inputs; };
extraSpecialArgs = { inputs = nomarchy.inputs; };
modules = [
nomarchy.nixosModules.home
./home.nix
@@ -1449,6 +1602,11 @@ EOF
networking.hostName = "$HOSTNAME";
time.timeZone = "$TIMEZONE";
# UEFI bootloader. Disko lays out a 1 GiB ESP at /boot — switch to
# boot.loader.grub if you're installing on a legacy-BIOS machine.
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
# Keyboard & language — set by the installer.
console.keyMap = "$KEYMAP_LAYOUT";
i18n.defaultLocale = "$LOCALE";
@@ -1487,7 +1645,9 @@ $xkb_variant_line
users.users."$USERNAME" = {
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" ];
};
@@ -1638,6 +1798,30 @@ EOF
# Extra Home Manager modules go here (program configs, services, etc.).
}
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
}
# ============================================================================
@@ -1658,15 +1842,25 @@ finish() {
echo " 4. Your configuration lives at /etc/nixos/"
echo ""
echo "Rebuild commands:"
echo " • System: sudo nixos-rebuild switch --flake /etc/nixos#$HOSTNAME"
echo " • Dotfiles: nomarchy-env-update (runs home-manager switch)"
echo " • System + dotfiles: sudo nixos-rebuild switch --flake /etc/nixos#$HOSTNAME"
echo " • Dotfiles only: nomarchy-env-update (fast standalone home-manager switch)"
echo ""
echo "Tip: run 'nomarchy-themes-prebuild' once to pre-cache every theme"
echo " variant. Theme switches after that are instant (no rebuild)."
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
umount -R /mnt 2>/dev/null || true
reboot
else
umount -R /mnt 2>/dev/null || true
fi
}

View File

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

View File

@@ -1,12 +1,20 @@
# 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 }:
{
# Home state defaults (user preferences)
home = {
# Theme and appearance
theme = "nord";
theme = "summer-night";
wallpaper = "";
font = "JetBrainsMono Nerd Font";
panelPosition = "top";
@@ -18,7 +26,6 @@
idle = true;
nightlight = false;
waybar = true;
skipVsCodeTheme = false;
# Hyprland window manager settings
hyprland = {
@@ -31,7 +38,7 @@
# System state defaults (system-level configuration)
system = {
# Theme (can differ from home for system-level theming)
theme = "nord";
theme = "summer-night";
# Timezone
timezone = "UTC";
@@ -50,18 +57,7 @@
fingerprint = false;
fido2 = 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

@@ -33,6 +33,62 @@ in
@define-color accent #${palette.base0D};
'';
# Per-palette kitty colors. features/apps/kitty/config/kitty.conf includes
# this file; without it the include silently failed and kitty stayed on
# default colors for every palette (Stylix's kitty target only kicks in
# when programs.kitty.enable is set, which the module doesn't use).
xdg.configFile."nomarchy/current/theme/kitty.conf".text = ''
background #${palette.base00}
foreground #${palette.base05}
cursor #${palette.base05}
selection_background #${palette.base02}
selection_foreground #${palette.base05}
color0 #${palette.base01}
color1 #${palette.base08}
color2 #${palette.base0B}
color3 #${palette.base0A}
color4 #${palette.base0D}
color5 #${palette.base0E}
color6 #${palette.base0C}
color7 #${palette.base04}
color8 #${palette.base03}
color9 #${palette.base08}
color10 #${palette.base0B}
color11 #${palette.base0A}
color12 #${palette.base0D}
color13 #${palette.base0E}
color14 #${palette.base0C}
color15 #${palette.base07}
'';
# Per-palette ghostty colors. features/apps/ghostty/config/config uses an
# optional include (?-prefix) of this file; without it the include was
# silently skipped and ghostty rendered with its built-in defaults across
# every palette. ghostty has no Stylix target.
xdg.configFile."nomarchy/current/theme/ghostty.conf".text = ''
background = ${palette.base00}
foreground = ${palette.base05}
cursor-color = ${palette.base05}
selection-background = ${palette.base02}
selection-foreground = ${palette.base05}
palette = 0=#${palette.base01}
palette = 1=#${palette.base08}
palette = 2=#${palette.base0B}
palette = 3=#${palette.base0A}
palette = 4=#${palette.base0D}
palette = 5=#${palette.base0E}
palette = 6=#${palette.base0C}
palette = 7=#${palette.base04}
palette = 8=#${palette.base03}
palette = 9=#${palette.base08}
palette = 10=#${palette.base0B}
palette = 11=#${palette.base0A}
palette = 12=#${palette.base0D}
palette = 13=#${palette.base0E}
palette = 14=#${palette.base0C}
palette = 15=#${palette.base07}
'';
# Ensure theme-specific hyprland config exists
# Lookup priority: palette apps/ > feature themes/ > nord fallback
xdg.configFile."nomarchy/current/theme/apps/hyprland.conf" = lib.mkIf (!hasHyprlandConf) {
@@ -74,21 +130,19 @@ in
xdg.configFile."nomarchy/branding/logo.svg".source = ../../core/branding/logo.svg;
xdg.configFile."nomarchy/branding/icon.png".source = ../../core/branding/icon.png;
xdg.configFile."nomarchy/branding/icon.txt".source = ../../core/branding/icon.txt;
xdg.configFile."nomarchy/branding/screensaver.txt".source = ../../core/branding/screensaver.txt;
xdg.configFile."nomarchy/branding/about.txt".source = ../../core/branding/about.txt;
# Expose all themes to the system via local share for script accessibility
# We filter out images to prevent Nix Store bloat
xdg.dataFile."nomarchy/themes".source = builtins.path {
name = "nomarchy-themes-no-images";
name = "nomarchy-themes";
path = ../palettes;
filter = path: type:
let
baseName = baseNameOf path;
in
! (type == "regular" && (
lib.hasSuffix ".jpg" baseName ||
lib.hasSuffix ".png" baseName ||
lib.hasSuffix ".jpeg" baseName
));
};
# 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

View File

@@ -54,31 +54,16 @@ in
};
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 {
type = lib.types.bool;
default = true;
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
if [ -f "$file" ]; then
wrapProgram "$file" \
--prefix PATH : "$deps" \
--set NOMARCHY_PATH "/etc/nixos/nomarchy"
--prefix PATH : "$deps"
fi
done
'';

Some files were not shown because too many files have changed in this diff Show More