fix(install): swap=0 and unattended LUKS fail-closed
All checks were successful
Check / eval (push) Successful in 3m4s
All checks were successful
Check / eval (push) Successful in 3m4s
Pass bare swapSize "0" when the user wants no swap (was "0G", which still created @swap). Accept "0"/"0G" in disko-config. Unattended installs require NOMARCHY_LUKS_PASSPHRASE or explicit NOMARCHY_NO_LUKS=1 instead of silently installing cleartext. Verified: bash -n; nix-instantiate subvolume sets for 0/0G/16G. VISION § v1.0 install golden path (workflow test iteration).
This commit is contained in:
@@ -157,16 +157,6 @@ first-boot tips · human rebuild errors · neon-glass quarantine.
|
||||
|
||||
### Bugs / broken behavior (codebase exam 2026-07-09)
|
||||
|
||||
- **Installer: swap "none" still creates a 0G swap subvolume**
|
||||
`nomarchy-install.sh` always passes `--argstr swapSize "${SWAP_GB}G"`.
|
||||
When the user chooses 0 (no swap), disko gets `"0G"`, but
|
||||
`disko-config.nix` only treats the exact string `"0"` as no-swap —
|
||||
so it still creates `@swap` with size `0G`. Resume wiring correctly
|
||||
skips (`SWAP_GB != "0"`), so layout and config disagree. Fix: pass
|
||||
bare `"0"` (or teach disko to accept `"0G"`). Cheap pure check /
|
||||
unit test for the swapSize contract would lock this. Cost: small
|
||||
installer + disko-config change; high install-path impact.
|
||||
|
||||
- **Waybar doctor indicator never shows an icon**
|
||||
`modules/home/waybar.nix` `nomarchy-doctor-status` prints
|
||||
`"text":""` on failure. Waybar custom modules with empty `text`
|
||||
@@ -219,14 +209,6 @@ first-boot tips · human rebuild errors · neon-glass quarantine.
|
||||
battery-notify. Cost: small multi-file; `[blocked:hw]` to confirm
|
||||
on non-BAT* hardware.
|
||||
|
||||
- **Unattended install silently disables LUKS if passphrase unset**
|
||||
Interactive path defaults encrypt-on; unattended without
|
||||
`NOMARCHY_LUKS_PASSPHRASE` installs **unencrypted** with only a log
|
||||
line — no fail-closed. `test-install.sh` always sets a passphrase,
|
||||
so the regression path is untested. Options: require the env var
|
||||
when unattended, or an explicit `NOMARCHY_NO_LUKS=1`. Cost: small
|
||||
policy change in install script + test matrix note.
|
||||
|
||||
### Docs / option-surface drift (codebase exam 2026-07-09)
|
||||
|
||||
- **`option-docs` would fail: undocumented `nomarchy.hardware.i2c.*`**
|
||||
@@ -655,10 +637,9 @@ installer questionnaire.*
|
||||
|
||||
#### 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.
|
||||
- **Installer swap=0 + unattended LUKS fail-closed** — ✓ shipped
|
||||
(workflow test run 2026-07-09): pass bare `"0"`; disko accepts
|
||||
`"0"`/`"0G"`; unattended needs passphrase or `NOMARCHY_NO_LUKS=1`.
|
||||
|
||||
- **Offline mode must not silently fall back to network lock**
|
||||
When the network probe fails (or install is offline),
|
||||
|
||||
@@ -17,6 +17,13 @@ Template:
|
||||
|
||||
---
|
||||
|
||||
## 2026-07-09 — Install contracts: swap=0 + unattended LUKS (workflow test run)
|
||||
- **Task:** VISION § v1.0 install golden path — PROPOSED install P0s (NEXT head blocked: item 14 wait-cron, 20 `[human]`, 33 V3 visual, 41 hw). Test of VISION→BACKLOG→verify→commit loop.
|
||||
- **Did:** (1) `nomarchy-install` passes `swapSize=0` when swap disabled (was always `"${SWAP_GB}G"` → `"0G"` still created `@swap`). (2) `disko-config.nix` treats `"0"` and `"0G"` as no-swap. (3) Unattended LUKS fail-closed: require `NOMARCHY_LUKS_PASSPHRASE` or `NOMARCHY_NO_LUKS=1`.
|
||||
- **Verified:** V0 — `bash -n`; pure `nix-instantiate` of disko subvols for `0`/`0G`/`16G` (no `@swap` only on zero). Not V2 install matrix this run.
|
||||
- **Pending:** optional pure `checks.*` for swapSize; unattended no-swap/no-LUKS in `test-install.sh` when convenient.
|
||||
- **Next suggestion:** promote another VISION § A slice (firmware menu / doctor) or boreal default to NEXT.
|
||||
|
||||
## 2026-07-08 — Theme UI Review: Typography Polish & Icon Call (iteration #87)
|
||||
- **Task:** NEXT item 28 (Theme UI review).
|
||||
- **Did:**
|
||||
|
||||
@@ -15,12 +15,14 @@
|
||||
# proved fragile twice before.
|
||||
{ mainDrive
|
||||
, withLuks ? true
|
||||
, swapSize ? "0" # "0" = no swap; otherwise e.g. "16G" (sized for hibernation)
|
||||
, swapSize ? "0" # "0" or "0G" = no swap; otherwise e.g. "16G" (hibernation-sized)
|
||||
, ...
|
||||
}:
|
||||
|
||||
let
|
||||
btrfsMountOptions = [ "compress=zstd" "noatime" ];
|
||||
# Installer historically always appended "G"; accept both "0" and "0G".
|
||||
noSwap = swapSize == "0" || swapSize == "0G";
|
||||
|
||||
rootBtrfs = {
|
||||
type = "btrfs";
|
||||
@@ -32,7 +34,7 @@ let
|
||||
"@log" = { mountpoint = "/var/log"; mountOptions = btrfsMountOptions; };
|
||||
# snapper timeline snapshots (nomarchy.system.snapper.enable)
|
||||
"@snapshots" = { mountpoint = "/.snapshots"; mountOptions = btrfsMountOptions; };
|
||||
} // (if swapSize == "0" then { } else {
|
||||
} // (if noSwap then { } else {
|
||||
# Hibernation-ready swapfile on its own subvolume; disko's
|
||||
# mkswapfile handles the BTRFS NOCOW requirements.
|
||||
"@swap" = {
|
||||
|
||||
@@ -18,8 +18,11 @@
|
||||
# [NOMARCHY_TIMEZONE=UTC] [NOMARCHY_LUKS_PASSPHRASE=...] \
|
||||
# [NOMARCHY_LOCALE=en_US.UTF-8] [NOMARCHY_KB_LAYOUT=us] [NOMARCHY_KB_VARIANT=] \
|
||||
# [NOMARCHY_SWAP_GB=N (default: RAM size; 0 = none)] \
|
||||
# [NOMARCHY_LUKS_PASSPHRASE=... | NOMARCHY_NO_LUKS=1] \
|
||||
# [NOMARCHY_HW="auto"|"none"|"mod1 mod2"] [NOMARCHY_FINISH=none|reboot|poweroff]
|
||||
# nomarchy-install
|
||||
# Unattended encryption is fail-closed: set a passphrase, or explicit
|
||||
# NOMARCHY_NO_LUKS=1 — never silently install cleartext.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
@@ -126,7 +129,15 @@ section "Disk encryption"
|
||||
# logs you straight into the desktop (the passphrase already gates access).
|
||||
LUKS_PASSPHRASE=""
|
||||
if [[ "$UNATTENDED" == "1" ]]; then
|
||||
LUKS_PASSPHRASE="${NOMARCHY_LUKS_PASSPHRASE:-}"
|
||||
# Fail-closed: unattended without a passphrase used to install
|
||||
# cleartext (easy CI footgun). Require an explicit opt-out.
|
||||
if [[ "${NOMARCHY_NO_LUKS:-}" == "1" ]]; then
|
||||
LUKS_PASSPHRASE=""
|
||||
elif [[ -n "${NOMARCHY_LUKS_PASSPHRASE:-}" ]]; then
|
||||
LUKS_PASSPHRASE="$NOMARCHY_LUKS_PASSPHRASE"
|
||||
else
|
||||
fail "Unattended install needs NOMARCHY_LUKS_PASSPHRASE or NOMARCHY_NO_LUKS=1"
|
||||
fi
|
||||
elif gum confirm --default=yes "Encrypt the disk with LUKS? (default — also enables passwordless desktop login)"; then
|
||||
while true; do
|
||||
p1=$(gum input --password --placeholder "LUKS passphrase (min 8 chars)")
|
||||
@@ -317,11 +328,19 @@ if [[ $WITH_LUKS == true ]]; then
|
||||
unset LUKS_PASSPHRASE
|
||||
fi
|
||||
|
||||
# disko-config treats exact "0" as no-swap; "${SWAP_GB}G" would pass "0G"
|
||||
# and still create a useless @swap subvolume (layout vs resume disagreed).
|
||||
if [[ "$SWAP_GB" == "0" ]]; then
|
||||
DISKO_SWAP_SIZE="0"
|
||||
else
|
||||
DISKO_SWAP_SIZE="${SWAP_GB}G"
|
||||
fi
|
||||
|
||||
disko_log=$(mktemp --suffix=.disko.log)
|
||||
if ! disko --mode destroy,format,mount --yes-wipe-all-disks \
|
||||
--argstr mainDrive "$TARGET_DISK" \
|
||||
--arg withLuks "$WITH_LUKS" \
|
||||
--argstr swapSize "${SWAP_GB}G" \
|
||||
--argstr swapSize "$DISKO_SWAP_SIZE" \
|
||||
"$SHARE/disko-config.nix" >"$disko_log" 2>&1; then
|
||||
tail -n 30 "$disko_log"
|
||||
fail "disko failed — full log: $disko_log"
|
||||
|
||||
Reference in New Issue
Block a user