Compare commits

..

4 Commits

Author SHA1 Message Date
Bernardo Magri
21230a05eb feat(installer): review-then-edit loop with field-level re-prompt
Previously the review screen only offered Confirm/Abort, so a typo or
wrong-disk choice meant aborting the whole run and starting over (or
hand-editing /tmp/nomarchy-install.state.sh). On --resume the situation
was worse: every prompt re-runs (each short-circuits when its var is
set), the user lands on a review they can't change.

review_configuration() now offers Continue / Edit a field / Abort. Edit
opens a multi-select of every saved field; chosen fields clear and the
next loop iteration in main() re-prompts only those. The LUKS passphrase
short-circuits when already set, so editing other fields doesn't
re-prompt for it.

Net flow change:
- Fresh install: same prompts, then review with Edit option (typo fixes
  without restarting).
- --resume: state loads, every prompt skips (vars set), lands straight on
  review — exactly what the roadmap entry called for.

Verified via `bash -n`. Live VM dry-run not exercised in this session.
2026-04-26 09:21:40 +01:00
Bernardo Magri
4b2f16c2f0 docs: add TROUBLESHOOTING.md for the five common rebuild errors
Covers: option-already-declared (duplicate mkOption), attribute-missing
(forgot to import nomarchy.nixosModules.system), Stylix target conflict
(needs lib.mkForce, not bare bool), home-manager .hm-bak churn (left over
from backupFileExtension after first install), and impermanence path
missing (dir not in environment.persistence list).

Each entry has the literal error text, the cause, and a copy-paste fix.
Linked from README.md and docs/MIGRATION.md so users hit it before
guessing.
2026-04-26 09:16:40 +01:00
Bernardo Magri
21ee9c6035 feat(system): add gaming preset module
Opt-in `nomarchy.system.gaming.enable` (default false). Wires
`programs.steam` (with `remotePlay` and `localNetworkGameTransfers`
firewall holes opened via `mkDefault`), `programs.gamemode` (the
launching user must be in the `gamemode` group), and
`services.flatpak`.

Two pieces of the original roadmap entry split into separate
Next-column rows so the system-side preset ships now:

  1. Hyprland fullscreen-on-Steam-launch window rule (home-side).
  2. Declarative flathub remote (nixpkgs has no API for this; needs
     either an overlay or a one-shot systemd unit).

The flatpak service is enabled but the user must add flathub
manually after first boot — documented in OPTIONS.md.
2026-04-26 09:10:52 +01:00
Bernardo Magri
8266dc7ee2 feat(system): add accessibility preset module
Opt-in `nomarchy.system.accessibility.enable` (default false —
accessibility is a personal preference, not hardware-derived). Wires
`services.gnome.at-spi2-core`, installs `pkgs.orca`, and sets
`XCURSOR_SIZE` to a configurable `accessibility.cursorSize` (default
32, up from NixOS's 24).

The original roadmap entry bundled Hyprland-side bits (slower
key-repeat, Orca launch keybinding, high-contrast palette). Those
require touching home-manager / theme files and a new palette
directory; split into a separate Next-column row so the system-side
preset ships now and the desktop integration follows independently.
2026-04-26 09:06:02 +01:00
10 changed files with 283 additions and 20 deletions

View File

@@ -83,7 +83,7 @@ Add user-level packages, aliases, and dotfiles here.
nomarchy.home.terminal = "kitty";
```
For the full list of `nomarchy.*` options you can set in `system.nix` and `home.nix`, see the [Options Reference](docs/OPTIONS.md). For where the project is heading next, see the [Roadmap](docs/ROADMAP.md).
For the full list of `nomarchy.*` options you can set in `system.nix` and `home.nix`, see the [Options Reference](docs/OPTIONS.md). Hit a rebuild error? Check [Troubleshooting](docs/TROUBLESHOOTING.md). For where the project is heading next, see the [Roadmap](docs/ROADMAP.md).
### Applying Changes
After editing your files, apply them instantly. **IMPORTANT:** Nomarchy requires the `--impure` flag for evaluation. You **MUST** use the following aliases rather than standard NixOS commands:

View File

@@ -0,0 +1,14 @@
{ config, lib, pkgs, ... }:
let
cfg = config.nomarchy.system.accessibility;
in
{
config = lib.mkIf cfg.enable {
services.gnome.at-spi2-core.enable = lib.mkDefault true;
environment.systemPackages = [ pkgs.orca ];
environment.variables.XCURSOR_SIZE = toString cfg.cursorSize;
};
}

View File

@@ -21,6 +21,8 @@
./snapper.nix
./laptop.nix
./desktop.nix
./accessibility.nix
./gaming.nix
./hibernate.nix
./containers.nix
./pam.nix

20
core/system/gaming.nix Normal file
View File

@@ -0,0 +1,20 @@
{ config, lib, ... }:
let
cfg = config.nomarchy.system.gaming;
in
{
config = lib.mkIf cfg.enable {
programs.steam = {
enable = true;
remotePlay.openFirewall = lib.mkDefault true;
localNetworkGameTransfers.openFirewall = lib.mkDefault true;
};
# gamemode adjusts CPU governor and reschedules processes when a
# game requests it. The launching user must be in the `gamemode` group.
programs.gamemode.enable = true;
services.flatpak.enable = true;
};
}

View File

@@ -126,6 +126,37 @@
};
};
accessibility = {
enable = lib.mkEnableOption ''
Accessibility preset: AT-SPI2 framework, the Orca screen reader
on PATH, and a larger default cursor size. Off by default
accessibility is a personal preference, not a hardware-derived
signal. The Hyprland-side keybinding to launch Orca is a
separate roadmap item.
'';
cursorSize = lib.mkOption {
type = lib.types.int;
default = 32;
description = ''
XCURSOR_SIZE when accessibility is on. NixOS default is 24;
32 is a safer floor for low-vision users. Bump to 48 if the
user explicitly asks.
'';
};
};
gaming = {
enable = lib.mkEnableOption ''
Gaming preset: Steam (with remote-play firewall holes),
gamemode (CPU governor + nice on Steam launch via the user's
gamemode group), and flatpak. NOTE: flatpak's flathub remote
is not added declaratively after first boot, run
`flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo`.
The Hyprland fullscreen-on-Steam-launch window rule is a
separate roadmap item.
'';
};
containers = {
enable = lib.mkEnableOption ''
Rootless Podman with Docker compatibility (`docker` `podman`),

View File

@@ -216,6 +216,9 @@ Impermanence is **off** unless you set `nomarchy.system.impermanence.enable = tr
and it requires a BTRFS layout with a `root-blank` snapshot. Don't enable it
on an existing install — the live ISO is the right path for that.
If your first rebuild errors out, the five most common failures and their fixes
live in [Troubleshooting](TROUBLESHOOTING.md).
---
## Fallback: clean install via the live ISO

View File

@@ -79,6 +79,18 @@ Wired in `features/desktop/waybar/default.nix` (filters the battery widget out o
`bool`, default `nomarchy.system.formFactor == "desktop"`. Desktop preset: pins `powerManagement.cpuFreqGovernor` to `"performance"` (via `mkDefault`) and enables `services.zfs.autoScrub` + `services.zfs.trim` so a future ZFS pool gets sensible maintenance without further config. The ZFS knobs are no-ops until you add `boot.supportedFilesystems = [ "zfs" ]` (plus `networking.hostId`) and a pool. Battery-widget filtering is handled by `formFactor` itself, so this preset doesn't repeat it.
### `nomarchy.system.accessibility.enable`
`bool`, default `false`. Accessibility preset: enables `services.gnome.at-spi2-core` (AT-SPI2 framework), installs `pkgs.orca` (screen reader) into `environment.systemPackages`, and sets `XCURSOR_SIZE` to `accessibility.cursorSize`. Off by default — accessibility is a personal preference, not hardware-derived. The Hyprland-side bits (slower key-repeat, Orca launch keybinding, high-contrast palette) are a separate roadmap item.
### `nomarchy.system.accessibility.cursorSize`
`int`, default `32`. `XCURSOR_SIZE` when `accessibility.enable = true`. NixOS default is 24; 32 is a safer floor for low-vision users.
### `nomarchy.system.gaming.enable`
`bool`, default `false`. Gaming preset: enables `programs.steam` (with `remotePlay` and `localNetworkGameTransfers` firewall holes opened by `mkDefault`), `programs.gamemode` (the launching user must be in the `gamemode` group), and `services.flatpak`. The flathub remote isn't added declaratively — after first boot, run `flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo`. The Hyprland fullscreen-on-Steam-launch window rule is a separate roadmap item.
### `nomarchy.system.containers.enable`
`bool`, default `false`. Rootless Podman with Docker compatibility (`docker``podman`), plus `podman-compose`, `podman-tui`, and `dive`.

View File

@@ -23,12 +23,11 @@ Guardrails (apply when adding anything):
### Next (bigger lifts that build on Now)
- **Accessibility preset.** Larger cursor, slower key-repeat defaults, `services.orca`, screen reader keybinding, high-contrast theme variant.
- **Gaming preset.** `programs.steam.enable`, `programs.gamemode.enable`, `services.flatpak.enable` with a curated remote, and a Hyprland window-rule to fullscreen Steam-launched apps.
- **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 — Hyprland window rule.** Companion to the gaming preset: a Hyprland `windowrulev2 = fullscreen, class:^(steam_app_).*$` (or similar) so games launched from Steam grab the whole screen. Lives in `core/home/config/nomarchy/default/hypr/` and gates on a new `nomarchy.gaming.enable` mirror.
- **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`.
- **First-run welcome wizard.** Extend `nomarchy-welcome` from a one-shot greeter into a guided picker: theme, panel position, monospace font, "what's a sane home.nix to start with?". Runs once, persists "done" in `state.json`.
- **Plymouth theme variants per palette.** Currently one Plymouth theme; could template per-palette so the boot splash matches the active theme.
- **`docs/TROUBLESHOOTING.md`.** The five most common rebuild errors (`option ... already declared`, `attribute ... missing`, Stylix target conflicts, home-manager backupFileExtension churn, impermanence path missing) with copy-paste fixes.
- **Installer pre-flight resume polish.** If the user Ctrl-Cs mid-install and runs `--resume`, re-show the review screen before re-prompting for any unsaved fields.
### Later (speculative or research-shaped)
@@ -124,6 +123,10 @@ Each PR description should reference the row(s) in `docs/SCRIPTS.md` it closes,
(Move items here when they land — keep them brief, link the commit/PR.)
- _2026-04-26_ — Installer review-then-edit flow (`installer/install.sh`). Review screen now offers Continue / Edit a field / Abort. Edit opens a multi-select of saved fields; chosen fields clear and the next loop iteration re-prompts only those. Benefits both fresh installs (typo fixes without abort+restart) and `--resume` (lands on review immediately, since the loaded vars short-circuit each prompt). LUKS passphrase is held in memory across loop iterations so re-edits don't re-ask for it.
- _2026-04-26_ — `docs/TROUBLESHOOTING.md`. The five most common rebuild errors (option-already-declared, attribute-missing, Stylix target conflict, home-manager `.hm-bak` churn, impermanence path missing) with copy-paste fixes. Linked from `README.md` and `docs/MIGRATION.md`.
- _2026-04-26_ — Gaming preset module (`core/system/gaming.nix`). Opt-in `nomarchy.system.gaming.enable` (default false). Wires `programs.steam` (with `remotePlay`/`localNetworkGameTransfers` firewall holes via `mkDefault`), `programs.gamemode`, and `services.flatpak`. Flathub remote and Hyprland window-rule split into separate Next-column rows.
- _2026-04-26_ — Accessibility preset module (`core/system/accessibility.nix`). New `nomarchy.system.accessibility.{enable,cursorSize}` options (opt-in, default off — accessibility isn't a hardware-derived signal). Enables `services.gnome.at-spi2-core`, installs Orca, and sets `XCURSOR_SIZE=32` (configurable). Hyprland-side companion (key-repeat slowdown, Orca keybinding, high-contrast palette) split into a new Next-column row.
- _2026-04-26_ — Desktop preset module (`core/system/desktop.nix`). New `nomarchy.system.desktop.enable` option; defaults to `formFactor == "desktop"` (mirror of the laptop preset's auto-enable). Pins `powerManagement.cpuFreqGovernor` to `"performance"` and enables `services.zfs.{autoScrub,trim}` so a future ZFS pool gets sensible maintenance for free.
- _2026-04-26_ — Laptop preset module (`core/system/laptop.nix`). New `nomarchy.system.laptop.{enable,thermald}` options; `enable` defaults to `formFactor == "laptop"` so the installer's existing `formFactor` write auto-flips it on. Wires TLP (governors + 75/80 charge thresholds), force-disables `power-profiles-daemon`, enables `upower` and `thermald` (x86_64), adds the brightnessctl udev rule for backlight without root, and sets a logind lid-switch policy that defers to `hibernation.enable`. Closes both the Now item and the largest Next item.
- _2026-04-25_ — Software-profile multi-select in the installer. Users can now pick Dev, Gaming, Office, Media, and CLI Utils profiles during install; logic emits corresponding `home.packages` and system toggles into the generated config.

121
docs/TROUBLESHOOTING.md Normal file
View File

@@ -0,0 +1,121 @@
# Troubleshooting
The five rebuild errors a Nomarchy user is most likely to hit, with copy-paste fixes. If your error isn't here, the [Options Reference](OPTIONS.md) and [Migration Guide](MIGRATION.md) cover most other surfaces.
---
## 1. `error: The option 'X' is already declared in multiple modules`
**Looks like:**
```
error: The option `services.foo.enable' is already declared in multiple modules:
- /nix/store/…-source/modules/foo.nix
- /nix/store/…-source/core/system/foo.nix
```
**Cause:** Two modules — usually one of yours and one of Nomarchy's — both `mkOption` the same path. Nix can't merge two declarations of the *same option*; it can only merge two *values* for one option.
**Fix:** Find the duplicate. If it's yours and Nomarchy's, delete yours; Nomarchy already declares it. If it's two of yours, delete one. To grep:
```bash
grep -rn "options\..*foo\.enable" /etc/nixos
```
---
## 2. `error: attribute 'X' missing`
**Looks like:**
```
error: attribute 'nomarchy' missing
at /etc/nixos/system.nix:7:5:
7| nomarchy.system.formFactor = "laptop";
```
**Cause:** You're setting `nomarchy.*` options but didn't import the Nomarchy modules. The flake's `nixosModules.system` declares the option namespace; without the import, the option doesn't exist.
**Fix:** In your `/etc/nixos/flake.nix`, make sure both modules are in the system's `modules = [ … ]` list:
```nix
modules = [
nomarchy.nixosModules.system
./system.nix
./hardware-selection.nix
];
```
The home side is the same shape — `nomarchy.nixosModules.home` plus your `./home.nix`. See [MIGRATION.md](MIGRATION.md) for the full skeleton.
---
## 3. Stylix target conflict
**Looks like:**
```
error: The option `stylix.targets.gtk.enable' has conflicting definition values:
- In `/nix/store/…/stylix.nix': true
- In `/etc/nixos/home.nix': false
Use `lib.mkForce' or `lib.mkDefault' to resolve.
```
**Cause:** Stylix's per-target options are `bool`, not `enum`, so two equally-priority `true`/`false` values from two modules collide. Nomarchy's theming engine sets most targets to `true` via `mkDefault`, so the conflict almost always means *you* set one without `mkDefault`.
**Fix:** Wrap your override in `lib.mkForce`:
```nix
stylix.targets.gtk.enable = lib.mkForce false;
```
If you want the default-on behavior back, just delete your line — Nomarchy's default fires automatically.
---
## 4. home-manager `backupFileExtension` churn
**Looks like:** every rebuild leaves another `~/.config/foo/bar.conf.hm-bak` next to the file home-manager just wrote, until your `~/.config` is half backups.
**Cause:** home-manager refuses to overwrite files it didn't itself write; it backs them up first. Nomarchy's flake sets `backupFileExtension = "hm-bak"` (see `flake.nix:161,210`) so the first rebuild after a fresh ISO install doesn't fail — but every subsequent rebuild then re-backs-up the same files because the previous backup is still there.
**Fix:** After the first successful rebuild, delete the backups:
```bash
find ~/.config -name '*.hm-bak' -print -delete
```
If churn continues, you have a config under `~/.config/<app>/` that home-manager wants to manage but you've also touched by hand. Either let home-manager own it (don't edit by hand; use `nomarchy.*` options or `~/.config/nomarchy/overrides/`) or delete the home-manager declaration if you want the file to remain user-mutable.
---
## 5. impermanence path missing after a wipe
**Looks like:** after enabling `nomarchy.system.impermanence.enable = true;` and rebooting, an app forgets state — Bluetooth pairings vanish, NetworkManager forgets Wi-Fi, GPG keys are gone — or the rebuild itself errors with:
```
error: The path '/persist' does not exist
```
**Cause:** Impermanence requires (a) a `/persist` mountpoint that survives the boot wipe, and (b) every directory you want to keep must be in the persistence list. Nomarchy persists the basics in `core/system/impermanence.nix:46-72` (NetworkManager, Bluetooth, fprint, SSH host keys, the user's `.ssh` / `.gnupg` / Documents / Downloads / Pictures / Videos / Projects). Anything else you care about — Steam library, Flatpak data, custom dotfiles — must be added.
**Fix:** Make sure `/persist` is mounted (check `mount | grep persist`). Then add the missing path in your `system.nix`:
```nix
environment.persistence."/persist".users.nomarchy.directories = [
".local/share/Steam"
".var/app" # flatpak data
".local/share/keyrings" # already in Nomarchy defaults — example only
];
```
Per-app data lives in `~/.local/share/<app>` or `~/.var/app/<id>` (flatpak); check the app's docs. After adding, rebuild and reboot — the path is created on the next mount of `/persist`.
---
## Where to look next
- **Option reference:** [docs/OPTIONS.md](OPTIONS.md) — every `nomarchy.*` setting.
- **Existing-NixOS install:** [docs/MIGRATION.md](MIGRATION.md) — how to layer Nomarchy onto a working NixOS without reformatting.
- **Repo layout:** [docs/STRUCTURE.md](STRUCTURE.md) — where each module lives.
- **Roadmap:** [docs/ROADMAP.md](ROADMAP.md) — what's planned and what's shipped.

View File

@@ -315,6 +315,11 @@ get_luks_passphrase() {
return
fi
# Already set this session (review-edit loop iterated). Don't re-prompt;
# password isn't persisted across runs but is held in memory until
# execute_installation unsets it.
[[ -n "${LUKS_PASSWORD:-}" ]] && return
section "Disk Encryption"
info "Your disk will be encrypted with LUKS2."
@@ -741,16 +746,60 @@ review_configuration() {
if [[ "$DRY_RUN" == "true" ]]; then
info "Dry run: skipping destructive confirmation."
return
return 0
fi
nrun gum style --foreground 9 "This will DESTROY all data on $TARGET_DRIVE"
echo ""
if ! nrun gum confirm "Proceed with installation?"; then
error "Aborted"
exit 1
fi
local action
action=$(nrun gum choose --header "Choose:" \
"Continue with installation" \
"Edit a field" \
"Abort")
case "$action" in
"Continue with installation") return 0 ;;
"Edit a field") edit_fields; return 2 ;;
*) error "Aborted"; exit 1 ;;
esac
}
# Multi-select clear: each cleared field is re-prompted on the next loop
# iteration in main() because every prompt short-circuits when its var is
# non-empty. Hardware clears the cached `nixos-hardware` modules and per-host
# option lines together so they stay consistent. Passwords are never offered
# here — get_luks_passphrase short-circuits when LUKS_PASSWORD is already set,
# so editing a field doesn't re-ask for the LUKS passphrase.
edit_fields() {
section "Edit Fields"
local choices
choices=$(nrun gum choose --no-limit --header "Pick fields to re-enter (space to select, enter to confirm):" \
"Drive ($TARGET_DRIVE)" \
"User and host ($USERNAME @ $HOSTNAME)" \
"Keymap and locale ($KEYMAP_LAYOUT / $LOCALE)" \
"Timezone ($TIMEZONE)" \
"Hardware ($HARDWARE_MODULES)" \
"Form factor ($FORM_FACTOR)" \
"Impermanence ($ENABLE_IMPERMANENCE)" \
"Profiles (${SELECTED_PROFILES:-none})" \
"Nomarchy rev (${NOMARCHY_REV:-main})")
[[ -z "$choices" ]] && return
while IFS= read -r f; do
case "$f" in
"Drive"*) TARGET_DRIVE="" ;;
"User and host"*) USERNAME=""; HOSTNAME="" ;;
"Keymap"*) KEYMAP_LAYOUT=""; KEYMAP_VARIANT=""; LOCALE="" ;;
"Timezone"*) TIMEZONE="" ;;
"Hardware"*) HARDWARE_MODULES=""; NOMARCHY_HW_OPTS="" ;;
"Form factor"*) FORM_FACTOR="" ;;
"Impermanence"*) ENABLE_IMPERMANENCE="" ;;
"Profiles"*) SELECTED_PROFILES="" ;;
"Nomarchy rev"*) NOMARCHY_REV="" ;;
esac
done <<<"$choices"
}
# ============================================================================
@@ -1291,6 +1340,12 @@ main() {
load_state
check_environment
# Loop: prompts (each skips when its var is set) → review → Continue|Edit.
# On Edit, edit_fields() clears the chosen vars and the next iteration
# re-prompts only those. Continue breaks. --resume lands here with vars
# already loaded, so the first pass goes straight to review.
while true; do
select_disk
get_luks_passphrase
configure_user
@@ -1300,7 +1355,9 @@ main() {
confirm_form_factor
configure_impermanence
select_profiles
review_configuration
if review_configuration; then break; fi
done
execute_installation
# Skip the reboot prompt on a dry run — nothing to reboot into.