23 Commits

Author SHA1 Message Date
Stefan Strobl
3eb618923c refactor: improve error handling and fix critical bugs
Enhance code robustness and fix several critical issues identified
during code review:

**Error Handling:**
- Add set -euo pipefail to all modules for consistent error detection
- Add sleep delays after partition refresh to prevent race conditions

**Bug Fixes:**
- Fix package verification flag from -R to -r (xbps-query)
- Fix swap file creation for btrfs (use dd instead of fallocate)
- Add atomic fstab updates with backup mechanism

**Optimizations:**
- Remove @swap btrfs subvolume, use regular directory instead
- Direct dd usage for btrfs swap files (COW-compatible)
- Optimize swap creation logic per filesystem type

**Security:**
- Add GRUB/LUKS2 compatibility check with version warning
- Create fstab backup before modifications

Changes affect 14 files across all installer phases. All changes
improve reliability, error detection, and filesystem compatibility.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-25 11:21:50 +01:00
Stefan Strobl
e6ccb81cb0 Document fstab and package install flow 2025-12-25 10:42:12 +01:00
Stefan Strobl
89c88c8a2e Fix non-interactive install and fstab setup 2025-12-25 10:31:30 +01:00
Stefan Strobl
8cc60e3dae docs: update README for v2.0 full CLI installer
Complete documentation rewrite to reflect v2.0 changes:
- Remove void-installer handoff instructions (obsolete)
- Document new automated workflow (11 phases)
- Add locale/timezone/keyboard configuration section
- Add user management documentation
- List default package installation
- Update usage (remove --skip-installer flag)
- Add troubleshooting section for new phases
- Document standard user groups (wheel, audio, video, etc.)

Major documentation changes:
1. Title: "Vollständiger CLI-Installer" (emphasizes automation)
2. Overview: Highlights "ohne manuelle Eingriffe" (no manual intervention)
3. Workflow: 11 phases instead of 7
4. Configuration: Three new sections (Locale, User, Packages)
5. Architecture: Added new modules to structure diagram

Removed sections:
- "Installer Bedienung nach dem Script" (manual installer instructions)
- --skip-installer flag documentation

Added sections:
- Package-Liste (complete list of installed packages)
- User-Konfiguration (user creation and group membership)
- Nach der Installation (post-install steps)
- Fehlerbehebung (troubleshooting for new phases)
- Zukünftige Erweiterungen (roadmap for config file support)

Decision: Keep German language for consistency with existing docs.
Technical terms (packages, services) use English for clarity.
2025-12-24 20:45:20 +01:00
Stefan Strobl
b2031eae4b feat(main): integrate new modules and remove installer phase
Remove dependency on void-installer by integrating new modules:
- Source packages.sh, locale.sh, users.sh, services.sh
- Remove installer.sh sourcing
- Add 4 new phases after mounts: packages, locale, users, services
- Remove run_installer() phase
- Remove SKIP_INSTALLER flag and --skip-installer option

Decision: Execute new phases in logical order.
Packages must install before locale (needs glibc-locales package).
Locale before users (user shell may depend on locale).
Users before services (services may need user context).

Workflow change:
Before: Mounts → void-installer (manual) → Post-Install
After:  Mounts → Packages → Locale → Users → Services → Post-Install

Alternative considered: Keep installer.sh as optional fallback
rejected. Clean break eliminates maintenance burden and reduces
complexity. Users who need custom package selection can modify
packages.sh directly.

Trade-off: Removes flexibility of void-installer's package
selection UI. Accepted because:
1. Requirements explicitly exclude GUI/TUI components
2. Config file support (future) will provide customization
3. Simpler codebase easier to maintain and debug

Module sourcing order preserves dependency chain:
logging → config → sanity → disk ops → installation → rollback
2025-12-24 20:45:07 +01:00
Stefan Strobl
0913adf00e feat(config): add prompts for locale, timezone, and user
Extend configuration module with new interactive prompts:
- LOCALE: System language (default: en_US.UTF-8)
- TIMEZONE: Time zone (default: UTC)
- KEYBOARD: Console keyboard layout (default: us)
- USERNAME: Standard user account (required)
- USER_SHELL: User shell (default: /bin/bash)
- USER_GROUPS: Group membership (default: wheel,audio,video,storage,network)

Decision: Use prompt_with_default for locale/timezone/keyboard
to provide sensible defaults. Use prompt_required for username
since no universal default exists.

Validation additions:
- Locale format check (basic regex for xx_XX.UTF-8 pattern)
- Username validation (must start with letter/underscore, contain
  only lowercase alphanumeric/underscore/hyphen)

Alternative considered: Auto-detect timezone via GeoIP rejected
(unreliable without network, adds complexity).

Trade-off: Locale format validation is lenient (warning only).
Strict validation rejected because locale names vary and false
positives would block installations. Warning informs user of
potential typo but allows override.

Summary display extended to show all new configuration values
for user review before destructive operations begin.
2025-12-24 20:44:53 +01:00
Stefan Strobl
dcd22b04c0 feat(services): add service activation module
Implement runit service activation for essential services:
- dhcpcd enabled by default for network connectivity
- Helper function enable_service() for extensibility
- Service validation before symlink creation

Decision: Enable only dhcpcd by default.
Minimal service activation reduces attack surface and resource
usage. Additional services (sshd, chronyd) can be added via
future config file support.

Alternative considered: NetworkManager rejected as default.
dhcpcd is lighter and sufficient for server/minimal systems.
Desktop users can switch to NetworkManager post-install.

Trade-off: Automatic dhcpcd activation means network works
immediately after boot but may conflict with users who prefer
NetworkManager. Future config will allow service selection.

Service definition validation: Check if /etc/sv/<service> exists
before creating symlink. Prevents broken links if package not
installed. Returns error code but continues installation to
avoid cascading failures from optional services.
2025-12-24 20:44:41 +01:00
Stefan Strobl
4ac64e6659 feat(users): add user management module
Implement user account creation and sudo configuration:
- Interactive root password setup in chroot
- Standard user creation with configurable groups
- Automatic sudo configuration for wheel group
- Password validation via visudo

Decision: Set passwords interactively for security.
Hash-based passwords rejected for initial implementation
(complexity vs security trade-off).

User groups: wheel (sudo), audio, video, storage, network.
Provides hardware access without additional configuration.

Alternative considered: Passwordless sudo rejected (security risk).
Creating user without sudo rejected (user would be unable to
administer system).

Trade-off: Interactive password input requires TTY and manual
entry. More secure than storing hashes but less convenient for
automated installations. Future config file support can add
hash option.

Sudoers validation: Use visudo -c to validate syntax before
committing changes. Rollback to backup if validation fails.
This prevents lockout from broken sudoers file.
2025-12-24 20:44:31 +01:00
Stefan Strobl
56c8234d35 feat(locale): add locale configuration module
Implement locale, timezone, and keyboard configuration for
internationalization support:
- Locale generation via /etc/default/libc-locales
- Timezone symlink to /usr/share/zoneinfo
- Keyboard layout in /etc/rc.conf

Decision: Default to en_US.UTF-8 for broadest compatibility.
Use standard Void Linux configuration methods (libc-locales,
rc.conf) instead of systemd-localed (Void uses runit).

Alternative considered: Generate all locales rejected - wastes
disk space and time. Only generate selected locale.

Trade-off: Timezone validation checks if file exists and falls
back to UTC if not found. This prevents installation failure
but may surprise users if they mistype timezone name.

Fallback behavior ensures installation completes even with
invalid timezone input, prioritizing robustness over strictness.
2025-12-24 20:44:21 +01:00
Stefan Strobl
f295eb5684 feat(packages): add package installation module
Implement package installation in chroot environment to replace
void-installer dependency. Module installs minimal base system:
- Base-System: base-system, linux, linux-firmware, grub, cryptsetup, dracut
- Network: dhcpcd, iproute2, iputils
- Tools: vim, nano, sudo

Decision: Install conservative package set by default.
Base packages are sufficient for bootable CLI system with network.
Desktop environments can be added later via config file support.

Alternative considered: Interactive package selection rejected
(violates CLI-only requirement from requirements document).

Trade-off: Fixed package list means less flexibility but simpler
initial implementation. Future config file support will allow
customization.

Validation: Package verification after installation ensures
critical packages are present before continuing.
2025-12-24 20:43:54 +01:00
Stefan Strobl
667453596a fix(rollback): cleanup bind mounts on failure
Add cleanup for bind mounts (/dev, /proc, /sys, /run) created
during post-install phase. Without this, failures in post-install
leave bind mounts active, preventing subsequent cleanup of the
main filesystem mounts.

Clean bind mounts first (innermost to outermost), then regular
mounts, then LUKS mapping to ensure proper teardown order.

Decision: Check each bind mount individually rather than relying
on umount -R to handle everything, for better error reporting and
partial cleanup capability.

Context: Bind mounts are only created if post-install phase is
reached, but rollback must handle cleanup from any failure point.
2025-12-24 15:28:05 +01:00
Stefan Strobl
98aebc5f09 fix(postinstall): improve error handling and user feedback
Add three critical improvements to post-install phase:

1. Cleanup trap for bind mounts: Ensure /dev, /proc, /sys, /run
   are unmounted even if chroot script fails. Prevents orphaned
   mounts blocking cleanup.

2. Fallocate fallback: Use dd as fallback if fallocate fails
   (can happen on some filesystems or with insufficient space).
   Includes intelligent size conversion and progress display.

3. Progress messages: Add informative echo statements before
   long-running operations (xbps-reconfigure, grub-install,
   grub-mkconfig) so users know the system is working.

Decision: Use trap EXIT instead of manual cleanup to guarantee
execution on both success and error paths. Remove redundant
explicit cleanup call.

Trade-offs: dd fallback is slower but ensures swap file creation
succeeds. Progress messages add noise but significantly improve
UX during multi-minute operations.
2025-12-24 15:27:14 +01:00
Stefan Strobl
551cb98a9d fix(installer): verify mounts after installer completion
Add verification that critical mounts (root and ESP) are still
intact after void-installer exits. Catches cases where installer
accidentally reformats filesystems despite instructions.

Fail fast with clear error message if mounts disappeared, rather
than proceeding to post-install and encountering cryptic errors.

Decision: Check mounts immediately after installer rather than
during post-install to provide clear failure point and message.

Alternative considered: Monitor installer process to prevent
reformatting, rejected as too invasive and complex.
2025-12-24 15:25:42 +01:00
Stefan Strobl
c303a75192 fix(filesystems): add cleanup trap for temp btrfs mount
Add trap to ensure temp mount is cleaned up if btrfs subvolume
creation fails. Without this, failures leave mounted filesystems
and orphaned directories, blocking retry attempts.

Use mountpoint -q for robust mount detection before cleanup.

Decision: Use local trap within format_filesystems to avoid
interfering with main error handler. Reset trap after successful
completion to prevent double cleanup.

Trade-off: Slightly more complex code vs guaranteed cleanup on
error paths.
2025-12-24 15:24:29 +01:00
Stefan Strobl
c9fbc5486c fix(config): add size validation and improve swap normalization
Add validate_size() function to check format of user-provided
size inputs (ESP_SIZE, ROOT_END, SWAP_SIZE) before they reach
partitioning tools. Prevents cryptic parted errors from invalid
formats like "abc" or "1X".

Improve SWAP_SIZE normalization to handle all zero variants
(0, 0G, 0GB, 0GiB, 0M, 0MB, 0MiB) consistently.

Decision: Validate at config phase rather than partition phase
to provide early, clear feedback. Use regex pattern that matches
parted's expected format (number + optional unit or percentage).

Alternative considered: Let parted handle validation, rejected
because error messages would be less user-friendly.
2025-12-24 15:22:50 +01:00
Stefan Strobl
4e0d9573e6 fix(partitioning): export partition variables for module access
Export ESP_PART and ROOT_PART variables to ensure they are
available in all subsequent modules (encryption, filesystems,
postinstall). While sourcing makes them accessible, explicit
exports clarify the sharing model and prevent potential issues
in different shell contexts.

Decision: Use explicit export instead of relying on implicit
variable propagation through source to make dependencies clear.
2025-12-24 15:22:10 +01:00
Stefan Strobl
051485cede Relax partition refresh dependency 2025-12-24 14:57:05 +01:00
Stefan Strobl
a6e5399572 Implement wrapper workflow and btrfs layout 2025-12-24 14:36:53 +01:00
Stefan Strobl
1fc9fa0f5f feat(support): add phase 1 narrative for logging and rollback
Add literate programming phase 1 documentation for:
- logging.sh: transparency and auditability during operations
- rollback.sh: cleanup strategy after failures

Decision: Plain text logs for readability over structured JSON.
Log destination is /tmp/void-wrapper-YYYY-MM-DD-HHMMSS.log to
avoid conflicts in multi-run sessions. Logs remain ephemeral
in live environment, not copied to target system.

Decision: Three log levels (INFO, WARN, ERROR) with clear
separation between stdout (user-facing) and log file (detailed
trace). Secret masking prevents passphrase exposure.

Decision: Best-effort rollback limited to reversible operations.
Unmount filesystems and close LUKS mappings, but never revert
partitioning or formatting (irreversible without data loss).
Only cleanup after explicit user confirmation.

Rationale: Full automatic rollback rejected due to complexity
and risk. Partial cleanup with clear manual recovery instructions
is safer and more transparent. Track failed phase to provide
targeted recovery advice.

Open questions for phase 2:
- Support verbose/debug mode beyond standard levels?
- Display log path at end for manual review?
- Handle log rotation in multi-run sessions?
2025-12-24 09:52:49 +01:00
Stefan Strobl
a1fdbdaf6b feat(install): add phase 1 narrative for installer integration
Add literate programming phase 1 documentation for:
- installer.sh: handoff to official Void installer
- postinstall.sh: boot configuration for encrypted system

Decision: Keep official installer in the loop rather than
full automation. The installer handles package selection and
system configuration choices better than a custom script would.
Reduces maintenance burden significantly.

Decision: GRUB as default bootloader for wide Void support
without systemd dependencies. systemd-boot and rEFInd rejected
due to availability constraints. EFISTUB rejected due to manual
UEFI entry management overhead.

Technical details documented for post-install phase:
- dracut crypt module enables LUKS unlock in initramfs
- /etc/crypttab uses UUID references for device stability
- GRUB_ENABLE_CRYPTODISK=y enables GRUB LUKS unlock
- rd.luks.uuid kernel parameter tells dracut which container
- grub-install embeds cryptodisk, grub-mkconfig updates menu

Open questions preserved for phase 2 implementation:
- Should rescue initramfs be generated?
- Verify GRUB LUKS2 support before reboot?
- Document kernel update process for users?
2025-12-24 09:52:06 +01:00
Stefan Strobl
0d29a9ae62 feat(disk): add phase 1 narrative for disk preparation modules
Add literate programming phase 1 documentation for:
- partitioning.sh: UEFI layout with ESP and encrypted root
- encryption.sh: LUKS encryption strategy and key handling
- filesystems.sh: filesystem choices and swap configuration
- mounts.sh: mount tree preparation for installer handoff

Decision: Swap file instead of swap partition for automatic
encryption and flexibility. Swap partition would require either
a second LUKS container (two passphrases at boot), unencrypted
swap (security risk), or LVM (out of scope).

Decision: /boot inside encrypted root for simplicity. GRUB
unlocks LUKS once, kernel and initramfs are protected. ESP
remains unencrypted per UEFI requirements.

Decision: Default to btrfs with explicit ext4 opt-in. Btrfs
provides snapshots and flexibility. Swap file on btrfs requires
No-COW subvolume to avoid corruption.

Trade-off: LUKS2 vs LUKS1. LUKS2 provides better security
features and tooling, but GRUB <2.06 may have incomplete
support. Users informed of potential boot issues with clear
recovery path rather than silent downgrade.
2025-12-24 09:51:25 +01:00
Stefan Strobl
d6be456c39 feat(core): add phase 1 narrative for core orchestration modules
Add literate programming phase 1 documentation for:
- main.sh: wrapper orchestration and user journey
- config.sh: configuration management and defaults
- sanity.sh: preflight safety checks

Decision: UEFI-only support to reduce complexity. BIOS would
require separate /boot partition and additional testing burden
disproportionate to modern hardware usage.

Decision: LUKS2 as default encryption for improved security
tooling, with clear guidance for LUKS1 fallback if GRUB boot
fails. Auto-detection rejected due to version fragmentation.

Decision: Interactive configuration as default. Presets are
out of scope for phase 1 to keep the flow understandable.

The user journey documents the complete end-to-end flow from
live medium boot to first encrypted boot, establishing clear
handoff points between wrapper and installer.
2025-12-24 09:50:14 +01:00
Stefan Strobl
dbf40c30d8 docs: add literate programming guidelines and project notes
Add comprehensive documentation for literate programming approach:
- Phase 1 guidelines focusing on why/what without implementation
- General literate programming principles for code documentation
- Project notes explaining wrapper script concept and strategy

Decision: Follow literate programming to make the wrapper
maintainable and educational. Comments tell the story, code
implements it. This foundation enables better collaboration
and knowledge transfer.

The notes.md establishes the core concept: a reproducible
wrapper around the Void installer that automates error-prone
LUKS encryption setup while keeping user control over system
configuration choices.
2025-12-24 09:50:00 +01:00