feat(install): copy templates/downstream and patch install values
All checks were successful
Check / eval (push) Successful in 3m4s
All checks were successful
Check / eval (push) Successful in 3m4s
Make the downstream template the single source of truth for machine flakes: nomarchy-install copies flake/system/home/theme-state and patch-template.py only fills hostname, user, keyboard, detected hardware, snapper, resume, and password. Install and flake-init users now share the same commented opt-ins and starter home.packages. Also: BACKLOG boreal-as-default proposal; installer audit notes; HARDWARE.md from prior work if uncommitted.
This commit is contained in:
@@ -73,7 +73,7 @@ Flat on purpose. Two module trees, one options file each, no hidden layers.
|
|||||||
├── pkgs/
|
├── pkgs/
|
||||||
│ ├── nomarchy-theme-sync/ # state writer + rebuild dispatcher (Python)
|
│ ├── nomarchy-theme-sync/ # state writer + rebuild dispatcher (Python)
|
||||||
│ └── nomarchy-install/ # live-ISO installer (gum + disko + mkFlake)
|
│ └── nomarchy-install/ # live-ISO installer (gum + disko + mkFlake)
|
||||||
├── templates/downstream/ # `nix flake init -t` starter for users
|
├── templates/downstream/ # machine flake SoT (`flake init` + installer copy/patch)
|
||||||
├── docs/TESTING.md # how to verify changes (incl. AI-agent rules)
|
├── docs/TESTING.md # how to verify changes (incl. AI-agent rules)
|
||||||
├── docs/HARDWARE.md # firmware, drivers, profiles, unsupported machines
|
├── docs/HARDWARE.md # firmware, drivers, profiles, unsupported machines
|
||||||
├── docs/OVERRIDES.md # how downstream users override defaults
|
├── docs/OVERRIDES.md # how downstream users override defaults
|
||||||
|
|||||||
142
agent/BACKLOG.md
142
agent/BACKLOG.md
@@ -103,6 +103,15 @@ implement. Bernardo moves accepted items into a tier.*
|
|||||||
|
|
||||||
### Pre-existing
|
### Pre-existing
|
||||||
|
|
||||||
|
- **Default theme → boreal**
|
||||||
|
Ship Boreal as the distro default instead of Tokyo Night: root
|
||||||
|
`theme-state.json`, `templates/downstream/theme-state.json` (and any
|
||||||
|
greeter/plymouth fallbacks that hardcode Tokyo Night when state is
|
||||||
|
missing). Preset already exists under `themes/boreal/`. Capture
|
||||||
|
`preview.png` if still missing so the picker looks first-class.
|
||||||
|
Cost: copy/apply palette into seed state files + grep for tokyo-night
|
||||||
|
defaults; V0 contrast, V1/V2 theme apply, V3 visual on hardware.
|
||||||
|
|
||||||
- **Battery charge-limit — make the toggle instant** `[blocked:hw]`
|
- **Battery charge-limit — make the toggle instant** `[blocked:hw]`
|
||||||
(follow-up to the preset toggle shipped 2026-07-07, iteration #55).
|
(follow-up to the preset toggle shipped 2026-07-07, iteration #55).
|
||||||
The control-center now has a proper toggle (Off / 80% / 90% / 60% /
|
The control-center now has a proper toggle (Off / 80% / 90% / 60% /
|
||||||
@@ -240,15 +249,21 @@ implement. Bernardo moves accepted items into a tier.*
|
|||||||
`rollback`, …). Misleading for direct invocation / scripting.
|
`rollback`, …). Misleading for direct invocation / scripting.
|
||||||
Cost: one string in `rofi.nix`.
|
Cost: one string in `rofi.nix`.
|
||||||
|
|
||||||
- **Template / seed state drift**
|
- **Template / seed state drift (residual after SoT work)**
|
||||||
(1) `templates/downstream/theme-state.json` lacks `border` and
|
(1) `templates/downstream/theme-state.json` lacks `border` and
|
||||||
`settings` keys present in repo root state (eval merges defaults —
|
`settings` keys present in repo root state (eval merges defaults);
|
||||||
not a break, but new installs ship a thinner shape); (2) no
|
(2) no commented `nomarchy.system.snapper.enable` or
|
||||||
commented `nomarchy.system.snapper.enable` or `hardware.i2c.*` in
|
`hardware.i2c.*` in template `system.nix` despite opt-in convention.
|
||||||
`system.nix` despite opt-in convention; (3) installer-generated
|
Main home/system sync is **Installer consumes template as SoT**
|
||||||
`home.nix` is minimal (keyboard only) while `flake init` template
|
below — do that first, then close these residual gaps on the
|
||||||
ships a fat default package suite — product divergence worth
|
template itself. Cost: small template polish.
|
||||||
documenting or aligning. Cost: small template/docs.
|
|
||||||
|
- **Installer ↔ template SoT — follow-ups** (shipped 2026-07-09: copy +
|
||||||
|
`patch-template.py`). Remaining: (1) optional CI that the install
|
||||||
|
share ships the same template files; (2) V2 `test-install.sh` after
|
||||||
|
ISO rebuild (HM pre-activate now pulls starter packages — larger
|
||||||
|
closure); (3) residual theme-state / i2c comment polish on template
|
||||||
|
(see residual seed drift item).
|
||||||
|
|
||||||
- **Secondary docs polish**
|
- **Secondary docs polish**
|
||||||
README layout tree omits many real modules and pkgs
|
README layout tree omits many real modules and pkgs
|
||||||
@@ -600,6 +615,117 @@ of that doc — design notes live there (§4–§10).*
|
|||||||
Prefer the commented-guidance item first; promote only with
|
Prefer the commented-guidance item first; promote only with
|
||||||
hardware-queue coverage. Cost: high; `[big]` + `[blocked:hw]`.
|
hardware-queue coverage. Cost: high; `[big]` + `[blocked:hw]`.
|
||||||
|
|
||||||
|
### Installer process (audit 2026-07-09)
|
||||||
|
|
||||||
|
*Full-path review of live ISO → `nomarchy-install` → disko → config
|
||||||
|
generation → offline/online `nixos-install` → HM pre-activate → first
|
||||||
|
boot. Philosophy applied: **opinionated, stability-first** — prefer safer
|
||||||
|
defaults and fewer install questions over option sprawl. Day-2 firmware /
|
||||||
|
fingerprint / re-detect stay in Hardware product above, not in the
|
||||||
|
installer questionnaire.*
|
||||||
|
|
||||||
|
#### Already strong (do not “fix” with more choices)
|
||||||
|
|
||||||
|
- Single whole-disk GPT, UEFI + systemd-boot only; multi-disk RAID /
|
||||||
|
dual-boot / BIOS deferred (ROADMAP LATER).
|
||||||
|
- LUKS2 **default** + greeter auto-login (one gate); type-to-confirm wipe;
|
||||||
|
live medium excluded from disk list; careful pre-wipe.
|
||||||
|
- BTRFS subvols + snapper on by default; hibernation swap default = RAM;
|
||||||
|
hardware autodetection with **safe defaults on, heavy bits commented**.
|
||||||
|
- Offline-capable install (compose-lock, ISO store pin, daemon
|
||||||
|
substituter); HM pre-activate so first boot is themed.
|
||||||
|
- No Secure Boot theater; no install-time app-suite *wizard* / custom
|
||||||
|
partitioner / pbkdf knobs. (Starter apps come from the **template**
|
||||||
|
`home.packages` once the installer consumes the template as SoT —
|
||||||
|
see above; not a separate install questionnaire.)
|
||||||
|
- Live: no idle suspend (avoids mid-install sleep); WiFi via normal
|
||||||
|
desktop NM path.
|
||||||
|
|
||||||
|
#### Bugs (stability contracts — fix, don’t optionalize)
|
||||||
|
|
||||||
|
- **Installer swap=0 / unattended LUKS fail-open** — already under
|
||||||
|
*Bugs / broken behavior* above (`"0G"` vs `"0"`; missing
|
||||||
|
`NOMARCHY_LUKS_PASSPHRASE` → cleartext). Same audit reconfirmed both
|
||||||
|
as install-path P0.
|
||||||
|
|
||||||
|
- **Offline mode must not silently fall back to network lock**
|
||||||
|
When the network probe fails (or install is offline),
|
||||||
|
`compose-lock.py` failure currently **warns** and runs `nix flake
|
||||||
|
lock` (needs network). That can turn a promised offline install into a
|
||||||
|
hung/partial one. **Fix:** if offline path is active and compose-lock
|
||||||
|
fails → **fail the install** with a clear message (dirty ISO / missing
|
||||||
|
narHash). Network `flake lock` only when the probe said online.
|
||||||
|
Cost: small branch in `nomarchy-install.sh`; V2 offline install test.
|
||||||
|
|
||||||
|
#### Safety / clarity (one line each — no new install menus)
|
||||||
|
|
||||||
|
- **Review panel: warn when target already has Windows / BitLocker /
|
||||||
|
NTFS / crypto_LUKS**
|
||||||
|
Typed disk-name confirm is solid, but multi-OS machines still risk
|
||||||
|
“wrong disk.” At selection or review, if `lsblk -f`/`blkid` shows
|
||||||
|
BitLocker, NTFS, Microsoft, or existing LUKS on the **chosen** disk,
|
||||||
|
add one explicit line: data will be destroyed. Still single-disk,
|
||||||
|
whole-disk only — **no** dual-boot path. Cost: few lines of probe +
|
||||||
|
review string; V0 + V2 optional.
|
||||||
|
|
||||||
|
- **Review panel: offline vs online source line**
|
||||||
|
Review always implies “no network needed” even when the online path
|
||||||
|
(skip full store copy, use substituters) is active. Make the Source
|
||||||
|
line conditional on the same `cache.nixos.org` probe so expectations
|
||||||
|
match. Cost: tiny; V0.
|
||||||
|
|
||||||
|
- **User password minimum length = LUKS (8 chars)**
|
||||||
|
LUKS requires ≥8; account password only requires non-empty. Align to
|
||||||
|
8 for one consistent rule — not a strength meter. Cost: one check;
|
||||||
|
V0.
|
||||||
|
|
||||||
|
#### Live ISO discoverability (one durable cue)
|
||||||
|
|
||||||
|
- **Live: one always-visible “Install Nomarchy” action**
|
||||||
|
Install is only in: 3s notify-send toast, getty helpLine, and
|
||||||
|
tribal knowledge of `nomarchy-install`. No desktop entry / menu row.
|
||||||
|
**One** durable surface on the live host only: e.g.
|
||||||
|
`xdg.desktopEntries` (Terminal=true) **or** a single Tools/System
|
||||||
|
menu item that opens a terminal into `nomarchy-install`. Prefer that
|
||||||
|
over more install-time questions or a second installer UI. Also fix
|
||||||
|
stale `hosts/live.nix` header (“No installer yet” — already in docs
|
||||||
|
drift pack). Cost: live.nix only; V1 ISO rebuild + V2 smoke.
|
||||||
|
|
||||||
|
#### Generated config / post-install polish
|
||||||
|
|
||||||
|
- **HM pre-activate failure → one durable recovery hint on target**
|
||||||
|
Installer already prints the `home-manager switch` one-liner and logs
|
||||||
|
to `/var/log/nomarchy-hm-preactivate.log`. If pre-activate fails, also
|
||||||
|
drop a small flag file or MOTD snippet on the target so first login
|
||||||
|
still shows the fix after the live session is gone. Avoid permanent
|
||||||
|
first-boot checklists (fwupd/fprintd belong in menu/doctor). Cost:
|
||||||
|
small install-script write; V2.
|
||||||
|
|
||||||
|
- **MIGRATION.md: installer snapshot layout vs `@home-snapshots`**
|
||||||
|
Installer creates disko `@snapshots` → `/.snapshots` and a first-boot
|
||||||
|
oneshot for nested `/home/.snapshots`. MIGRATION still speaks of
|
||||||
|
top-level `@home-snapshots` (TuringMachine vocabulary). One paragraph
|
||||||
|
clarifying installer layout vs migration machines prevents wrong
|
||||||
|
expectations. Cost: docs only.
|
||||||
|
|
||||||
|
#### Test / pure contracts (after P0 bugs)
|
||||||
|
|
||||||
|
- **Installer pure checks + expanded unattended matrix** — overlaps
|
||||||
|
existing *Tooling* items (disko `swapSize` contract, compose-lock
|
||||||
|
py_compile, hardware-db ∈ nixos-hardware). After swap/LUKS fixes:
|
||||||
|
document or script unattended no-swap and explicit no-LUKS paths in
|
||||||
|
`test-install.sh` (env flags only — no interactive options). Do **not**
|
||||||
|
grow the interactive installer questionnaire.
|
||||||
|
|
||||||
|
#### Explicit non-goals for the installer (defer)
|
||||||
|
|
||||||
|
Do **not** add install-time: dual-boot / preserve partitions, multi-disk
|
||||||
|
RAID, Secure Boot/lanzaboote, recovery-key *wizard* (optional silent
|
||||||
|
recovery print is the only recovery-key idea worth considering later),
|
||||||
|
custom partitioner, online/offline user toggle, app suite selection,
|
||||||
|
pbkdf/TRIM knobs, or a second installer frontend. Stability and the
|
||||||
|
single golden path win.
|
||||||
|
|
||||||
|
|
||||||
## Decisions `[human]`
|
## Decisions `[human]`
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,9 @@ those, it probably shouldn't exist.
|
|||||||
in the downstream template's `home.packages` — opt-out is deleting the
|
in the downstream template's `home.packages` — opt-out is deleting the
|
||||||
line.
|
line.
|
||||||
- **Opt-in features** ship a commented example in
|
- **Opt-in features** ship a commented example in
|
||||||
`templates/downstream/home.nix` or `system.nix`.
|
`templates/downstream/home.nix` or `system.nix`. That template is the
|
||||||
|
single source of truth for machine files: `nomarchy-install` copies it
|
||||||
|
and patches install-time values only (never a thinner second catalog).
|
||||||
- **Menu:** new entries go in the right submenu (Tools › / System ›; root
|
- **Menu:** new entries go in the right submenu (Tools › / System ›; root
|
||||||
stays six entries), end lists with the shared `↩ Back`, self-gate on
|
stays six entries), end lists with the shared `↩ Back`, self-gate on
|
||||||
the feature's availability, and add the direct
|
the feature's availability, and add the direct
|
||||||
|
|||||||
@@ -50,7 +50,11 @@ stdenvNoCC.mkDerivation {
|
|||||||
# Empty flake registry: no network lookups for indirect refs.
|
# Empty flake registry: no network lookups for indirect refs.
|
||||||
echo '{"flakes":[],"version":2}' > "$share/registry.json"
|
echo '{"flakes":[],"version":2}' > "$share/registry.json"
|
||||||
mkdir -p "$share/template"
|
mkdir -p "$share/template"
|
||||||
cp ${templateDir}/home.nix ${templateDir}/theme-state.json "$share/template/"
|
# Full downstream template is the SoT; install script copies + patches.
|
||||||
|
cp ${templateDir}/flake.nix ${templateDir}/system.nix \
|
||||||
|
${templateDir}/home.nix ${templateDir}/theme-state.json \
|
||||||
|
"$share/template/"
|
||||||
|
install -Dm644 patch-template.py "$share/patch-template.py"
|
||||||
|
|
||||||
# nixos-install / nixos-generate-config / nixos-enter / nix / systemd
|
# nixos-install / nixos-generate-config / nixos-enter / nix / systemd
|
||||||
# tools come from the live system on purpose — they must match it.
|
# tools come from the live system on purpose — they must match it.
|
||||||
|
|||||||
@@ -330,21 +330,14 @@ rm -f "$LUKS_KEY_PATH" "$disko_log"
|
|||||||
success "Disk partitioned and mounted at /mnt"
|
success "Disk partitioned and mounted at /mnt"
|
||||||
|
|
||||||
# Hibernation plumbing: the swapfile's physical offset goes into the
|
# Hibernation plumbing: the swapfile's physical offset goes into the
|
||||||
# kernel cmdline. Deactivate swap first so nixos-generate-config doesn't
|
# kernel cmdline (patched into system.nix). Deactivate swap first so
|
||||||
# also emit a swapDevices entry (we write our own, with resume wiring).
|
# nixos-generate-config doesn't also emit a swapDevices entry.
|
||||||
RESUME_CONFIG=""
|
resume_offset=""
|
||||||
|
root_uuid=""
|
||||||
if [[ "$SWAP_GB" != "0" ]]; then
|
if [[ "$SWAP_GB" != "0" ]]; then
|
||||||
swapoff -a 2>/dev/null || true
|
swapoff -a 2>/dev/null || true
|
||||||
resume_offset=$(btrfs inspect-internal map-swapfile -r /mnt/swap/swapfile)
|
resume_offset=$(btrfs inspect-internal map-swapfile -r /mnt/swap/swapfile)
|
||||||
root_uuid=$(findmnt -no UUID /mnt)
|
root_uuid=$(findmnt -no UUID /mnt)
|
||||||
RESUME_CONFIG=$(cat <<NIX
|
|
||||||
|
|
||||||
# Swapfile (hibernation-ready: resume points into it).
|
|
||||||
swapDevices = [{ device = "/swap/swapfile"; }];
|
|
||||||
boot.resumeDevice = "/dev/disk/by-uuid/$root_uuid";
|
|
||||||
boot.kernelParams = [ "resume_offset=$resume_offset" ];
|
|
||||||
NIX
|
|
||||||
)
|
|
||||||
success "Swapfile created (resume offset $resume_offset)"
|
success "Swapfile created (resume offset $resume_offset)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -358,183 +351,75 @@ nixos-generate-config --root /mnt
|
|||||||
mv /mnt/etc/nixos/hardware-configuration.nix "$FLAKE_DIR/"
|
mv /mnt/etc/nixos/hardware-configuration.nix "$FLAKE_DIR/"
|
||||||
rm -rf /mnt/etc/nixos
|
rm -rf /mnt/etc/nixos
|
||||||
|
|
||||||
cp "$SHARE/template/theme-state.json" "$FLAKE_DIR/"
|
# templates/downstream is the single source of truth (same files as
|
||||||
|
# `nix flake init -t`). Copy, then patch install-time values only.
|
||||||
|
cp "$SHARE/template/flake.nix" \
|
||||||
|
"$SHARE/template/system.nix" \
|
||||||
|
"$SHARE/template/home.nix" \
|
||||||
|
"$SHARE/template/theme-state.json" \
|
||||||
|
"$FLAKE_DIR/"
|
||||||
|
|
||||||
# home.nix is generated (not copied from the template) so the chosen
|
# Detected hardware → flags for the patcher (safe defaults active).
|
||||||
# keyboard layout reaches the Hyprland session — standalone HM cannot
|
has_intel=false; has_amd=false; has_fp=false
|
||||||
# read system.nix.
|
intel_guc_off=false; has_camera_ir=false
|
||||||
cat > "$FLAKE_DIR/home.nix" <<EOF
|
if [[ ${#HW_NOMARCHY[@]} -gt 0 ]]; then
|
||||||
# Your user environment. The Nomarchy desktop (Hyprland, Waybar,
|
for nm in "${HW_NOMARCHY[@]}"; do
|
||||||
# Ghostty, theming engine, Stylix) comes from homeModules.nomarchy;
|
case "$nm" in
|
||||||
# tune it via the nomarchy.* options, add your own packages and
|
hardware.intel.enable=true) has_intel=true ;;
|
||||||
# programs below.
|
hardware.intel.guc=false) intel_guc_off=true ;;
|
||||||
{ pkgs, ... }:
|
hardware.amd.enable=true) has_amd=true ;;
|
||||||
|
hardware.fingerprint.enable=true) has_fp=true ;;
|
||||||
|
hardware.camera.hideIrSensor=true) has_camera_ir=true ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
is_laptop=false
|
||||||
|
[[ " ${HW_PROFILES[*]:-} " == *" common-pc-laptop "* ]] && is_laptop=true
|
||||||
|
thermald=false
|
||||||
|
[[ $is_laptop == true ]] && grep -q GenuineIntel /proc/cpuinfo 2>/dev/null && thermald=true
|
||||||
|
|
||||||
{
|
# JSON for patch-template.py (stdin). Hardware profiles as a JSON array.
|
||||||
# Keyboard for the desktop session; console + LUKS prompt get the
|
hw_json="["
|
||||||
# same layout from system.nix (xkb + console.useXkbConfig).
|
first=1
|
||||||
nomarchy.keyboard.layout = "$KB_LAYOUT";
|
|
||||||
nomarchy.keyboard.variant = "$KB_VARIANT";
|
|
||||||
|
|
||||||
# Examples:
|
|
||||||
# nomarchy.terminal = "kitty"; # swap the default terminal
|
|
||||||
# nomarchy.waybar.enable = false; # bring your own bar
|
|
||||||
# nomarchy.stylix.enable = false; # opt out of GTK/Qt theming
|
|
||||||
|
|
||||||
home.packages = with pkgs; [
|
|
||||||
# firefox
|
|
||||||
];
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
hw_nix=""
|
|
||||||
for p in "${HW_PROFILES[@]:-}"; do
|
for p in "${HW_PROFILES[@]:-}"; do
|
||||||
[[ -n "$p" ]] && hw_nix+=" \"$p\""
|
[[ -z "$p" ]] && continue
|
||||||
|
if [[ $first -eq 1 ]]; then first=0; else hw_json+=","; fi
|
||||||
|
hw_json+=$(printf '%s' "$p" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read().rstrip("\n")))')
|
||||||
done
|
done
|
||||||
|
hw_json+="]"
|
||||||
|
|
||||||
cat > "$FLAKE_DIR/flake.nix" <<EOF
|
resume_json="null"
|
||||||
|
root_uuid_json="null"
|
||||||
|
if [[ -n "$resume_offset" && "$SWAP_GB" != "0" ]]; then
|
||||||
|
resume_json=$(printf '%s' "$resume_offset" | python3 -c 'import json,sys; print(json.dumps(int(sys.stdin.read().strip())))')
|
||||||
|
root_uuid_json=$(printf '%s' "$root_uuid" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read().rstrip("\n")))')
|
||||||
|
fi
|
||||||
|
|
||||||
|
python3 "$SHARE/patch-template.py" "$FLAKE_DIR" <<PYJSON
|
||||||
{
|
{
|
||||||
description = "$HOSTNAME_ — my Nomarchy machine";
|
"hostname": $(printf '%s' "$HOSTNAME_" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read().rstrip("\n")))'),
|
||||||
|
"username": $(printf '%s' "$USERNAME" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read().rstrip("\n")))'),
|
||||||
# The only input. nixpkgs, home-manager etc. come pinned through it —
|
"timezone": $(printf '%s' "$TIMEZONE" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read().rstrip("\n")))'),
|
||||||
# tested together upstream. Generated by nomarchy-install; your machine
|
"locale": $(printf '%s' "$LOCALE" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read().rstrip("\n")))'),
|
||||||
# lives in system.nix and home.nix, this file is never hand-edited.
|
"keyboardLayout": $(printf '%s' "$KB_LAYOUT" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read().rstrip("\n")))'),
|
||||||
inputs.nomarchy.url = "${NOMARCHY_FLAKE_URL}";
|
"keyboardVariant": $(printf '%s' "$KB_VARIANT" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read().rstrip("\n")))'),
|
||||||
|
"hashedPassword": $(printf '%s' "$HASHED_PASSWORD" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read().rstrip("\n")))'),
|
||||||
outputs = { nomarchy, ... }:
|
"autoLogin": $([[ $WITH_LUKS == true ]] && echo true || echo false),
|
||||||
nomarchy.lib.mkFlake {
|
"laptop": $is_laptop,
|
||||||
src = ./.;
|
"thermald": $thermald,
|
||||||
username = "$USERNAME";
|
"hardwareProfiles": $hw_json,
|
||||||
hardwareProfile = [$hw_nix ];
|
"hardware": {
|
||||||
};
|
"intel": $has_intel,
|
||||||
|
"intelGucOff": $intel_guc_off,
|
||||||
|
"amd": $has_amd,
|
||||||
|
"fingerprint": $has_fp,
|
||||||
|
"cameraIr": $has_camera_ir,
|
||||||
|
"npu": $(if [[ -n "$NPU_VENDOR" ]]; then printf '%s' "$NPU_VENDOR" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read().rstrip("\n")))'; else echo null; fi)
|
||||||
|
},
|
||||||
|
"resumeOffset": $resume_json,
|
||||||
|
"rootUuid": $root_uuid_json
|
||||||
}
|
}
|
||||||
EOF
|
PYJSON
|
||||||
|
|
||||||
AUTOLOGIN_CONFIG=""
|
|
||||||
if [[ $WITH_LUKS == true ]]; then
|
|
||||||
AUTOLOGIN_CONFIG=$(cat <<NIX
|
|
||||||
|
|
||||||
# The LUKS passphrase already gates this machine — skip the second
|
|
||||||
# password prompt and boot straight into the desktop.
|
|
||||||
nomarchy.system.greeter.autoLogin = "$USERNAME";
|
|
||||||
NIX
|
|
||||||
)
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Laptop power: power-profiles-daemon ships by default; mark this a laptop
|
|
||||||
# so battery-only features apply, and enable thermald on Intel. Keyed off
|
|
||||||
# the same battery probe that chose the common-pc-laptop hardware profile.
|
|
||||||
POWER_CONFIG=""
|
|
||||||
if [[ " ${HW_PROFILES[*]:-} " == *" common-pc-laptop "* ]]; then
|
|
||||||
power_lines=" # Laptop power management (power-profiles-daemon + menu/Waybar
|
|
||||||
# switcher). Uncomment to stop charging at 80% to extend battery life.
|
|
||||||
nomarchy.system.power.laptop = true;
|
|
||||||
# nomarchy.system.power.batteryChargeLimit = 80;"
|
|
||||||
if grep -q GenuineIntel /proc/cpuinfo 2>/dev/null; then
|
|
||||||
power_lines+="
|
|
||||||
nomarchy.system.power.thermal.enable = true; # thermald (Intel)"
|
|
||||||
fi
|
|
||||||
POWER_CONFIG=$(cat <<NIX
|
|
||||||
|
|
||||||
$power_lines
|
|
||||||
NIX
|
|
||||||
)
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Hardware enablement (nomarchy.hardware.*): what hardware-db.sh detected
|
|
||||||
# above the nixos-hardware commons. Safe defaults active; the heavier or
|
|
||||||
# experimental opt-ins written commented for the user to flip on.
|
|
||||||
HARDWARE_CONFIG=""
|
|
||||||
if [[ ${#HW_NOMARCHY[@]} -gt 0 || -n "$NPU_VENDOR" ]]; then
|
|
||||||
has_intel=0; has_amd=0; has_fp=0; intel_guc_off=0; has_camera_ir=0
|
|
||||||
if [[ ${#HW_NOMARCHY[@]} -gt 0 ]]; then
|
|
||||||
for nm in "${HW_NOMARCHY[@]}"; do
|
|
||||||
case "$nm" in
|
|
||||||
hardware.intel.enable=true) has_intel=1 ;;
|
|
||||||
hardware.intel.guc=false) intel_guc_off=1 ;;
|
|
||||||
hardware.amd.enable=true) has_amd=1 ;;
|
|
||||||
hardware.fingerprint.enable=true) has_fp=1 ;;
|
|
||||||
hardware.camera.hideIrSensor=true) has_camera_ir=1 ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
hw_lines=" # Hardware enablement (auto-detected). Safe defaults are active;
|
|
||||||
# the heavier opt-ins are commented — uncomment to turn them on."
|
|
||||||
if [[ $has_intel -eq 1 ]]; then
|
|
||||||
hw_lines+="
|
|
||||||
nomarchy.hardware.intel.enable = true; # GuC/HuC firmware (i915.enable_guc=3)"
|
|
||||||
if [[ $intel_guc_off -eq 1 ]]; then
|
|
||||||
hw_lines+="
|
|
||||||
nomarchy.hardware.intel.guc = false; # GPU on the xe driver → GuC is default-on"
|
|
||||||
fi
|
|
||||||
hw_lines+="
|
|
||||||
# nomarchy.hardware.intel.computeRuntime = true; # OpenCL/oneVPL GPU compute (opt-in)"
|
|
||||||
fi
|
|
||||||
if [[ $has_amd -eq 1 ]]; then
|
|
||||||
hw_lines+="
|
|
||||||
nomarchy.hardware.amd.enable = true; # amd-pstate EPP + radeonsi VA-API
|
|
||||||
# nomarchy.hardware.amd.rocm.enable = true; # ROCm GPU compute (multi-GB, opt-in)
|
|
||||||
# nomarchy.hardware.amd.rocm.gfxOverride = \"\"; # e.g. \"11.0.0\" for an unlisted iGPU"
|
|
||||||
fi
|
|
||||||
if [[ $has_fp -eq 1 ]]; then
|
|
||||||
hw_lines+="
|
|
||||||
nomarchy.hardware.fingerprint.enable = true; # fprintd (enroll: fprintd-enroll)
|
|
||||||
# nomarchy.hardware.fingerprint.pam = true; # use it for login + sudo (opt-in)"
|
|
||||||
fi
|
|
||||||
if [[ $has_camera_ir -eq 1 ]]; then
|
|
||||||
hw_lines+="
|
|
||||||
nomarchy.hardware.camera.hideIrSensor = true; # dual-sensor webcam: hide the IR node (color cam only)"
|
|
||||||
fi
|
|
||||||
if [[ -n "$NPU_VENDOR" ]]; then
|
|
||||||
hw_lines+="
|
|
||||||
# nomarchy.hardware.npu.enable = true; # $NPU_VENDOR NPU driver (experimental; userspace runtime BYO)
|
|
||||||
# nomarchy.hardware.latestKernel = true; # newest kernel if the NPU driver isn't in the shipped one"
|
|
||||||
fi
|
|
||||||
HARDWARE_CONFIG=$(cat <<NIX
|
|
||||||
|
|
||||||
$hw_lines
|
|
||||||
NIX
|
|
||||||
)
|
|
||||||
fi
|
|
||||||
|
|
||||||
# initialHashedPassword is safe to template: mkpasswd's alphabet is
|
|
||||||
# [a-zA-Z0-9./$] — no Nix string metacharacters.
|
|
||||||
cat > "$FLAKE_DIR/system.nix" <<EOF
|
|
||||||
# Your machine: hostname, users, services. The distro itself comes from
|
|
||||||
# Nomarchy (via flake.nix); override its defaults here with plain NixOS
|
|
||||||
# options, or the nomarchy.system.* toggles.
|
|
||||||
{ pkgs, username, ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
boot.loader.systemd-boot.enable = true;
|
|
||||||
boot.loader.efi.canTouchEfiVariables = true;
|
|
||||||
|
|
||||||
networking.hostName = "$HOSTNAME_";
|
|
||||||
time.timeZone = "$TIMEZONE";
|
|
||||||
i18n.defaultLocale = "$LOCALE";
|
|
||||||
|
|
||||||
# One keyboard layout everywhere: xkb is the source of truth, and the
|
|
||||||
# distro defaults (console.useXkbConfig + the systemd initrd, set in
|
|
||||||
# the nomarchy module) derive the virtual console and the LUKS
|
|
||||||
# passphrase prompt from it — so only the chosen layout is written here.
|
|
||||||
# The Hyprland session reads the same layout from nomarchy.keyboard.* in
|
|
||||||
# home.nix.
|
|
||||||
services.xserver.xkb.layout = "$KB_LAYOUT";
|
|
||||||
services.xserver.xkb.variant = "$KB_VARIANT";
|
|
||||||
|
|
||||||
# Your login user — \`username\` flows in from flake.nix automatically.
|
|
||||||
users.users.\${username} = {
|
|
||||||
isNormalUser = true;
|
|
||||||
extraGroups = [ "wheel" "networkmanager" "video" "input" ];
|
|
||||||
initialHashedPassword = "$HASHED_PASSWORD";
|
|
||||||
};
|
|
||||||
$AUTOLOGIN_CONFIG$POWER_CONFIG$HARDWARE_CONFIG$RESUME_CONFIG
|
|
||||||
# Hourly/daily BTRFS timeline snapshots + nixos-rebuild-snap.
|
|
||||||
nomarchy.system.snapper.enable = true;
|
|
||||||
|
|
||||||
system.stateVersion = "26.05";
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# The flake.lock: composed offline — nomarchy is path-locked to the very
|
# The flake.lock: composed offline — nomarchy is path-locked to the very
|
||||||
# source the ISO carries (original stays the forge URL, so a later
|
# source the ISO carries (original stays the forge URL, so a later
|
||||||
|
|||||||
251
pkgs/nomarchy-install/patch-template.py
Normal file
251
pkgs/nomarchy-install/patch-template.py
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Patch a copied templates/downstream machine flake with install-time values.
|
||||||
|
|
||||||
|
The template is the single source of truth for commented opt-ins and the
|
||||||
|
starter app suite. The installer copies it, then this script only:
|
||||||
|
|
||||||
|
* replaces known placeholders (hostname, username, locale, keyboard, …)
|
||||||
|
* fills the __NOMARCHY_INSTALLER__ region with detected/active config
|
||||||
|
* sets hardwareProfile on flake.nix
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
patch-template.py <flake-dir> # reads a JSON object from stdin
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
BEGIN = " # __NOMARCHY_INSTALLER_BEGIN__"
|
||||||
|
END = " # __NOMARCHY_INSTALLER_END__"
|
||||||
|
|
||||||
|
|
||||||
|
def nix_str(s: str) -> str:
|
||||||
|
"""Escape a string for a Nix double-quoted literal."""
|
||||||
|
return (
|
||||||
|
s.replace("\\", "\\\\")
|
||||||
|
.replace('"', '\\"')
|
||||||
|
.replace("${", "\\${")
|
||||||
|
.replace("\n", "\\n")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def replace_once(text: str, old: str, new: str, label: str) -> str:
|
||||||
|
if old not in text:
|
||||||
|
sys.exit(f"patch-template: missing placeholder for {label}: {old!r}")
|
||||||
|
return text.replace(old, new, 1)
|
||||||
|
|
||||||
|
|
||||||
|
def patch_flake(text: str, v: dict) -> str:
|
||||||
|
text = replace_once(
|
||||||
|
text,
|
||||||
|
'description = "My Nomarchy machine";',
|
||||||
|
f'description = "{nix_str(v["hostname"])} — my Nomarchy machine";',
|
||||||
|
"flake description",
|
||||||
|
)
|
||||||
|
text = replace_once(
|
||||||
|
text,
|
||||||
|
'username = "me"; # <- your login name',
|
||||||
|
f'username = "{nix_str(v["username"])}"; # <- your login name',
|
||||||
|
"flake username",
|
||||||
|
)
|
||||||
|
profiles = v.get("hardwareProfiles") or []
|
||||||
|
if profiles:
|
||||||
|
items = " ".join(f'"{nix_str(p)}"' for p in profiles)
|
||||||
|
hw_line = f" hardwareProfile = [ {items} ];"
|
||||||
|
else:
|
||||||
|
hw_line = " # hardwareProfile = null; # no nixos-hardware profiles selected"
|
||||||
|
# Replace the optional hardwareProfile comment block with the install choice.
|
||||||
|
text, n = re.subn(
|
||||||
|
r"\n # Optional: a nixos-hardware module name for your machine, e\.g\.\n"
|
||||||
|
r" # hardwareProfile = \"framework-13-7040-amd\";\n"
|
||||||
|
r" # Names: https://github.com/NixOS/nixos-hardware\n"
|
||||||
|
r" # \(the future installer fills this in automatically from DMI data\)\n",
|
||||||
|
f"\n{hw_line}\n"
|
||||||
|
f" # Names: https://github.com/NixOS/nixos-hardware\n",
|
||||||
|
text,
|
||||||
|
count=1,
|
||||||
|
)
|
||||||
|
if n != 1:
|
||||||
|
sys.exit("patch-template: could not patch hardwareProfile block in flake.nix")
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def patch_home(text: str, v: dict) -> str:
|
||||||
|
layout = nix_str(v["keyboardLayout"])
|
||||||
|
variant = nix_str(v.get("keyboardVariant") or "")
|
||||||
|
text = replace_once(
|
||||||
|
text,
|
||||||
|
' nomarchy.keyboard.layout = "us";',
|
||||||
|
f' nomarchy.keyboard.layout = "{layout}";',
|
||||||
|
"home keyboard layout",
|
||||||
|
)
|
||||||
|
text = replace_once(
|
||||||
|
text,
|
||||||
|
' nomarchy.keyboard.variant = "";',
|
||||||
|
f' nomarchy.keyboard.variant = "{variant}";',
|
||||||
|
"home keyboard variant",
|
||||||
|
)
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def build_installer_region(v: dict) -> str:
|
||||||
|
lines: list[str] = [
|
||||||
|
BEGIN,
|
||||||
|
" # Written by nomarchy-install from live detection. Safe defaults are",
|
||||||
|
" # active; heavier opt-ins stay in the commented catalog below.",
|
||||||
|
]
|
||||||
|
|
||||||
|
if v.get("autoLogin"):
|
||||||
|
user = nix_str(v["username"])
|
||||||
|
lines += [
|
||||||
|
" # LUKS passphrase already gates this machine — skip the greeter password.",
|
||||||
|
f' nomarchy.system.greeter.autoLogin = "{user}";',
|
||||||
|
]
|
||||||
|
|
||||||
|
if v.get("laptop"):
|
||||||
|
lines += [
|
||||||
|
" # Laptop power (PPD + menu/Waybar). Uncomment to cap charge at 80%.",
|
||||||
|
" nomarchy.system.power.laptop = true;",
|
||||||
|
" # nomarchy.system.power.batteryChargeLimit = 80;",
|
||||||
|
]
|
||||||
|
if v.get("thermald"):
|
||||||
|
lines.append(
|
||||||
|
" nomarchy.system.power.thermal.enable = true; # thermald (Intel)"
|
||||||
|
)
|
||||||
|
|
||||||
|
hw = v.get("hardware") or {}
|
||||||
|
if any(hw.get(k) for k in ("intel", "amd", "fingerprint", "cameraIr", "npu")):
|
||||||
|
lines.append(" # Hardware enablement (auto-detected).")
|
||||||
|
if hw.get("intel"):
|
||||||
|
lines.append(
|
||||||
|
" nomarchy.hardware.intel.enable = true; # GuC/HuC (i915)"
|
||||||
|
)
|
||||||
|
if hw.get("intelGucOff"):
|
||||||
|
lines.append(
|
||||||
|
" nomarchy.hardware.intel.guc = false; # xe driver → GuC default-on"
|
||||||
|
)
|
||||||
|
lines.append(
|
||||||
|
" # nomarchy.hardware.intel.computeRuntime = true; # OpenCL/oneVPL (opt-in)"
|
||||||
|
)
|
||||||
|
if hw.get("amd"):
|
||||||
|
lines += [
|
||||||
|
" nomarchy.hardware.amd.enable = true; # amd-pstate + VA-API",
|
||||||
|
" # nomarchy.hardware.amd.rocm.enable = true; # ROCm (multi-GB, opt-in)",
|
||||||
|
' # nomarchy.hardware.amd.rocm.gfxOverride = ""; # e.g. "11.0.0" for unlisted iGPU',
|
||||||
|
]
|
||||||
|
if hw.get("fingerprint"):
|
||||||
|
lines += [
|
||||||
|
" nomarchy.hardware.fingerprint.enable = true; # fprintd (enroll: fprintd-enroll)",
|
||||||
|
" # nomarchy.hardware.fingerprint.pam = true; # login + sudo (opt-in)",
|
||||||
|
]
|
||||||
|
if hw.get("cameraIr"):
|
||||||
|
lines.append(
|
||||||
|
" nomarchy.hardware.camera.hideIrSensor = true; # dual-sensor: hide IR node"
|
||||||
|
)
|
||||||
|
if hw.get("npu"):
|
||||||
|
vendor = nix_str(hw["npu"])
|
||||||
|
lines += [
|
||||||
|
f" # nomarchy.hardware.npu.enable = true; # {vendor} NPU (experimental; userspace BYO)",
|
||||||
|
" # nomarchy.hardware.latestKernel = true; # if the NPU driver needs a newer kernel",
|
||||||
|
]
|
||||||
|
|
||||||
|
if v.get("resumeOffset") is not None:
|
||||||
|
root_uuid = nix_str(v["rootUuid"])
|
||||||
|
offset = v["resumeOffset"]
|
||||||
|
lines += [
|
||||||
|
" # Swapfile (hibernation-ready: resume points into it).",
|
||||||
|
' swapDevices = [{ device = "/swap/swapfile"; }];',
|
||||||
|
f' boot.resumeDevice = "/dev/disk/by-uuid/{root_uuid}";',
|
||||||
|
f' boot.kernelParams = [ "resume_offset={offset}" ];',
|
||||||
|
]
|
||||||
|
|
||||||
|
# Always on for installer layout (BTRFS + @snapshots).
|
||||||
|
lines += [
|
||||||
|
" # Hourly/daily BTRFS timeline snapshots + nixos-rebuild-snap.",
|
||||||
|
" nomarchy.system.snapper.enable = true;",
|
||||||
|
END,
|
||||||
|
]
|
||||||
|
return "\n".join(lines) + "\n"
|
||||||
|
|
||||||
|
|
||||||
|
def patch_system(text: str, v: dict) -> str:
|
||||||
|
text = replace_once(
|
||||||
|
text,
|
||||||
|
' networking.hostName = "my-nomarchy";',
|
||||||
|
f' networking.hostName = "{nix_str(v["hostname"])}";',
|
||||||
|
"hostName",
|
||||||
|
)
|
||||||
|
text = replace_once(
|
||||||
|
text,
|
||||||
|
' time.timeZone = "UTC";',
|
||||||
|
f' time.timeZone = "{nix_str(v["timezone"])}";',
|
||||||
|
"timeZone",
|
||||||
|
)
|
||||||
|
text = replace_once(
|
||||||
|
text,
|
||||||
|
' i18n.defaultLocale = "en_US.UTF-8";',
|
||||||
|
f' i18n.defaultLocale = "{nix_str(v["locale"])}";',
|
||||||
|
"locale",
|
||||||
|
)
|
||||||
|
text = replace_once(
|
||||||
|
text,
|
||||||
|
' services.xserver.xkb.layout = "us";',
|
||||||
|
f' services.xserver.xkb.layout = "{nix_str(v["keyboardLayout"])}";',
|
||||||
|
"xkb layout",
|
||||||
|
)
|
||||||
|
text = replace_once(
|
||||||
|
text,
|
||||||
|
' services.xserver.xkb.variant = "";',
|
||||||
|
f' services.xserver.xkb.variant = "{nix_str(v.get("keyboardVariant") or "")}";',
|
||||||
|
"xkb variant",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Inject password into the user attrset (template has no password for flake-init).
|
||||||
|
user_block = """ users.users.${username} = {
|
||||||
|
isNormalUser = true;
|
||||||
|
extraGroups = [ "wheel" "networkmanager" "video" "input" ];
|
||||||
|
};"""
|
||||||
|
# HASHED_PASSWORD is sha-512 crypt; alphabet is safe in Nix double quotes.
|
||||||
|
hashed = nix_str(v["hashedPassword"])
|
||||||
|
user_patched = f""" users.users.${{username}} = {{
|
||||||
|
isNormalUser = true;
|
||||||
|
extraGroups = [ "wheel" "networkmanager" "video" "input" ];
|
||||||
|
initialHashedPassword = "{hashed}";
|
||||||
|
}};"""
|
||||||
|
text = replace_once(text, user_block, user_patched, "user password")
|
||||||
|
|
||||||
|
if BEGIN not in text or END not in text:
|
||||||
|
sys.exit("patch-template: system.nix missing __NOMARCHY_INSTALLER__ markers")
|
||||||
|
region = build_installer_region(v)
|
||||||
|
text = re.sub(
|
||||||
|
re.escape(BEGIN) + r".*?" + re.escape(END) + r"\n?",
|
||||||
|
region,
|
||||||
|
text,
|
||||||
|
count=1,
|
||||||
|
flags=re.DOTALL,
|
||||||
|
)
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
if len(sys.argv) != 2:
|
||||||
|
sys.exit("usage: patch-template.py <flake-dir>")
|
||||||
|
flake_dir = Path(sys.argv[1])
|
||||||
|
vals = json.load(sys.stdin)
|
||||||
|
|
||||||
|
mapping = {
|
||||||
|
"flake.nix": patch_flake,
|
||||||
|
"home.nix": patch_home,
|
||||||
|
"system.nix": patch_system,
|
||||||
|
}
|
||||||
|
for name, fn in mapping.items():
|
||||||
|
path = flake_dir / name
|
||||||
|
path.write_text(fn(path.read_text(), vals))
|
||||||
|
print(f"patch-template: patched {', '.join(mapping)} in {flake_dir}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -1,5 +1,11 @@
|
|||||||
# My Nomarchy machine
|
# My Nomarchy machine
|
||||||
|
|
||||||
|
This directory is the **single source of truth** for a Nomarchy machine
|
||||||
|
flake. `nix flake init -t …` copies it as-is; **`nomarchy-install` copies
|
||||||
|
the same files and only patches** install-time values (username, hostname,
|
||||||
|
keyboard, detected hardware, password hash, …). Keep commented opt-ins and
|
||||||
|
`home.packages` here — do not invent a second catalog in the installer.
|
||||||
|
|
||||||
1. Overwrite the placeholder hardware config with the real one:
|
1. Overwrite the placeholder hardware config with the real one:
|
||||||
`nixos-generate-config --show-hardware-config > hardware-configuration.nix`
|
`nixos-generate-config --show-hardware-config > hardware-configuration.nix`
|
||||||
2. Set `flake.nix` up **once** (Nomarchy repo URL, your username, optionally
|
2. Set `flake.nix` up **once** (Nomarchy repo URL, your username, optionally
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
# tested together upstream. This file is written once (by you or the
|
# tested together upstream. This file is written once (by you or the
|
||||||
# installer) and never hand-edited afterwards; your machine lives in
|
# installer) and never hand-edited afterwards; your machine lives in
|
||||||
# system.nix and home.nix. v1 is the release branch.
|
# system.nix and home.nix. v1 is the release branch.
|
||||||
|
# Installer copies this template and patches username + hardwareProfile.
|
||||||
inputs.nomarchy.url = "git+https://git.bemagri.xyz/bernardo/nomarchy.git?ref=v1";
|
inputs.nomarchy.url = "git+https://git.bemagri.xyz/bernardo/nomarchy.git?ref=v1";
|
||||||
|
|
||||||
outputs = { nomarchy, ... }:
|
outputs = { nomarchy, ... }:
|
||||||
|
|||||||
@@ -2,17 +2,21 @@
|
|||||||
# Ghostty, theming engine, Stylix) comes from homeModules.nomarchy;
|
# Ghostty, theming engine, Stylix) comes from homeModules.nomarchy;
|
||||||
# tune it via the nomarchy.* options, add your own packages and
|
# tune it via the nomarchy.* options, add your own packages and
|
||||||
# programs below.
|
# programs below.
|
||||||
|
#
|
||||||
|
# Source of truth for install and flake-init: nomarchy-install copies this
|
||||||
|
# file and only patches keyboard layout/variant.
|
||||||
{ pkgs, ... }:
|
{ pkgs, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
|
# Session keyboard — match services.xserver.xkb.* in system.nix (console
|
||||||
|
# + LUKS prompt follow xkb via the distro module). Installer patches these.
|
||||||
|
nomarchy.keyboard.layout = "us";
|
||||||
|
nomarchy.keyboard.variant = "";
|
||||||
|
|
||||||
# ── Overrides (the desktop is on by default; tweak or opt out) ──────
|
# ── Overrides (the desktop is on by default; tweak or opt out) ──────
|
||||||
# nomarchy.terminal = "kitty"; # swap the default terminal
|
# nomarchy.terminal = "kitty"; # swap the default terminal
|
||||||
# nomarchy.waybar.enable = false; # bring your own bar
|
# nomarchy.waybar.enable = false; # bring your own bar
|
||||||
# nomarchy.stylix.enable = false; # opt out of GTK/Qt theming
|
# nomarchy.stylix.enable = false; # opt out of GTK/Qt theming
|
||||||
# nomarchy.keyboard.layout = "de"; # session keyboard (set the matching
|
|
||||||
# # services.xserver.xkb.layout in
|
|
||||||
# # system.nix too; the console + LUKS
|
|
||||||
# # prompt follow it automatically)
|
|
||||||
|
|
||||||
# ── Opt-in features — uncomment and tweak to enable ─────────────────
|
# ── Opt-in features — uncomment and tweak to enable ─────────────────
|
||||||
# nomarchy.nightlight = { # scheduled blue-light filter (hyprsunset)
|
# nomarchy.nightlight = { # scheduled blue-light filter (hyprsunset)
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
# The distro itself comes from nomarchy.nixosModules.nomarchy — override
|
# The distro itself comes from nomarchy.nixosModules.nomarchy — override
|
||||||
# any of its defaults here with plain NixOS options (they use mkDefault),
|
# any of its defaults here with plain NixOS options (they use mkDefault),
|
||||||
# or via the nomarchy.system.* toggles.
|
# or via the nomarchy.system.* toggles.
|
||||||
|
#
|
||||||
|
# Source of truth for install and flake-init: nomarchy-install copies this
|
||||||
|
# file and only patches placeholders + the __NOMARCHY_INSTALLER__ region.
|
||||||
{ pkgs, username, ... }:
|
{ pkgs, username, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -12,12 +15,28 @@
|
|||||||
time.timeZone = "UTC";
|
time.timeZone = "UTC";
|
||||||
i18n.defaultLocale = "en_US.UTF-8";
|
i18n.defaultLocale = "en_US.UTF-8";
|
||||||
|
|
||||||
|
# One keyboard layout everywhere: xkb is the source of truth; the distro
|
||||||
|
# defaults (console.useXkbConfig + systemd initrd) derive the virtual
|
||||||
|
# console and the LUKS passphrase prompt from it. Match home.nix
|
||||||
|
# nomarchy.keyboard.*.
|
||||||
|
services.xserver.xkb.layout = "us";
|
||||||
|
services.xserver.xkb.variant = "";
|
||||||
|
|
||||||
# Your login user — `username` flows in from flake.nix automatically.
|
# Your login user — `username` flows in from flake.nix automatically.
|
||||||
|
# The installer adds initialHashedPassword here; flake-init: set a password
|
||||||
|
# (or users.mutableUsers) before first boot if needed.
|
||||||
users.users.${username} = {
|
users.users.${username} = {
|
||||||
isNormalUser = true;
|
isNormalUser = true;
|
||||||
extraGroups = [ "wheel" "networkmanager" "video" "input" ];
|
extraGroups = [ "wheel" "networkmanager" "video" "input" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# __NOMARCHY_INSTALLER_BEGIN__
|
||||||
|
# Installer fills this region (power, hardware, snapper, resume, autoLogin).
|
||||||
|
# Flake-init: leave empty and use the commented catalog below, or enable
|
||||||
|
# snapper when on BTRFS with a /.snapshots subvolume:
|
||||||
|
# nomarchy.system.snapper.enable = true;
|
||||||
|
# __NOMARCHY_INSTALLER_END__
|
||||||
|
|
||||||
# ── Overrides (uncomment to change) ─────────────────────────────────
|
# ── Overrides (uncomment to change) ─────────────────────────────────
|
||||||
# nomarchy.system.greeter.enable = false; # bring your own login manager
|
# nomarchy.system.greeter.enable = false; # bring your own login manager
|
||||||
# environment.systemPackages = [ pkgs.htop ];
|
# environment.systemPackages = [ pkgs.htop ];
|
||||||
@@ -48,6 +67,8 @@
|
|||||||
# latestKernel = true; # newest kernel for very-new hardware (drivers not yet in the default)
|
# latestKernel = true; # newest kernel for very-new hardware (drivers not yet in the default)
|
||||||
# camera.hideIrSensor = true; # dual-sensor webcam: hide the IR node so apps get the color cam
|
# camera.hideIrSensor = true; # dual-sensor webcam: hide the IR node so apps get the color cam
|
||||||
# # (installer-set when a paired RGB+IR webcam is detected)
|
# # (installer-set when a paired RGB+IR webcam is detected)
|
||||||
|
# i2c.enable = true; # /dev/i2c-* (RGB, sensors); ddcci is distro-default for external brightness
|
||||||
|
# i2c.ddcci = true; # already mkDefault true — set false to opt out
|
||||||
# };
|
# };
|
||||||
#
|
#
|
||||||
# nomarchy.services.tailscale.enable = true; # mesh VPN — connect from System › VPN
|
# nomarchy.services.tailscale.enable = true; # mesh VPN — connect from System › VPN
|
||||||
|
|||||||
Reference in New Issue
Block a user