feat: live ISO — nix daemon mode, autologin fix, GPU detection, app stack
- Switch nix from single-user to daemon mode (trusted-users = root live); Void socket at /var/nix/daemon-socket/socket confirmed - Fix lightdm autologin: use session-wrapper=/etc/lightdm/Xsession (Void lightdm 1.32 has no lightdm-session binary) - Fix session env: LIBGL_ALWAYS_SOFTWARE=1 via profile.d (session-env= is unsupported in this lightdm version) - GPU auto-detection at boot: VIRT→software GL, NVIDIA PRIME offload, Intel/AMD/generic→modesetting - Add nix-daemon to live runsvdir/default; remove unsupported -S mklive flag - first-login.sh: install Claude Code + nix user packages (google-chrome, spotify, discord, localsend, mission-center) + NVM/node + VS Code exts - build-live-iso.sh: write nix-packages.list from NIX_USER_PACKAGES - postinstall.sh: fix nix-daemon socket path to /var/nix/daemon-socket/socket - Dockerfile: add dconf-cli for build-time dconf compile - _inner-build-live.sh: use correct 'dconf compile' API (not 'dconf update') - .gitignore: add build/live-includes/ (generated staging tree) - docs/LIVE_ISO.md: document all findings, gotchas and architecture
This commit is contained in:
206
docs/LIVE_ISO.md
Normal file
206
docs/LIVE_ISO.md
Normal file
@@ -0,0 +1,206 @@
|
||||
# Live ISO Build — Findings & Architecture Notes
|
||||
|
||||
## Overview
|
||||
|
||||
The live ISO boots directly into a Cinnamon desktop session as user `live` with no password prompt. It is designed for hardware testing on XPS 9700 and serves as the installer delivery vehicle.
|
||||
|
||||
Builder: `iso/build-live-iso.sh` (host) → Docker container running `iso/_inner-build-live.sh` → `void-mklive/mklive.sh`
|
||||
|
||||
---
|
||||
|
||||
## Boot + Session Startup
|
||||
|
||||
### Kernel Cmdline
|
||||
```
|
||||
live.user=live console=tty0 console=ttyS0,115200
|
||||
```
|
||||
The `live.user=live` parameter is consumed by the vmklive dracut hook (`adduser.sh`) which creates the user inside the initramfs and sets password `voidlinux`.
|
||||
|
||||
### runit Stage 2 Override
|
||||
We override `/etc/runit/2` to run `/etc/runit/live-setup.sh` before handing off to `runsvdir`. The script:
|
||||
1. Adds extra groups (`plugdev input network docker`) to the live user
|
||||
2. Writes `/etc/sudoers.d/live` (full passwordless sudo)
|
||||
3. Configures `/etc/nix/nix.conf` (daemon mode, `trusted-users = root live`)
|
||||
4. Auto-detects GPU and writes `/etc/X11/xorg.conf.d/20-gpu.conf`
|
||||
|
||||
After live-setup.sh, stage 2 mirrors the real `runit-void` exactly:
|
||||
```sh
|
||||
runsvchdir "${runlevel}"
|
||||
ln -sf /etc/runit/runsvdir/current /run/runit/runsvdir/current
|
||||
exec runsvdir -P /run/runit/runsvdir/current
|
||||
```
|
||||
|
||||
### Services (runsvdir/default symlinks in overlay)
|
||||
Enabled at build time via symlinks in `build/live-includes/etc/runit/runsvdir/default/`:
|
||||
- `dbus`
|
||||
- `NetworkManager`
|
||||
- `lightdm`
|
||||
- `nix-daemon`
|
||||
|
||||
> **Note:** Do NOT use mklive.sh's `-S` flag for service enable — it is not supported by the version used. Services must be wired via runsvdir symlinks in the include overlay.
|
||||
|
||||
---
|
||||
|
||||
## LightDM Autologin
|
||||
|
||||
### Critical: `lightdm-session` does not exist on Void Linux
|
||||
The Void `lightdm` 1.32 package does **not** ship the `lightdm-session` binary. The default LightDM behaviour of spawning `lightdm-session` causes the session to crash immediately (exit code 1 in ~20ms) with no error message.
|
||||
|
||||
**Fix:** Set `session-wrapper=/etc/lightdm/Xsession` in `lightdm.conf`. The `/etc/lightdm/Xsession` wrapper **is** provided by the Void lightdm package and correctly sources `/etc/profile` → `/etc/profile.d/`.
|
||||
|
||||
### `greeter-env=` and `session-env=` are not supported
|
||||
These options are silently ignored in LightDM 1.32 on Void. To propagate environment variables to the session use `/etc/profile.d/` scripts instead.
|
||||
|
||||
### lightdm.conf autologin lines must be commented
|
||||
The vmklive dracut hook `display-manager-autologin.sh` uses `sed` to uncomment lines. The autologin lines in `lightdm.conf` must be present but commented out — the hook finds them by regex and uncomments them at boot.
|
||||
|
||||
```ini
|
||||
[Seat:*]
|
||||
#autologin-user=
|
||||
#autologin-user-timeout=0
|
||||
#autologin-session=
|
||||
#user-session=
|
||||
session-wrapper=/etc/lightdm/Xsession
|
||||
greeter-session=lightdm-gtk-greeter
|
||||
```
|
||||
|
||||
The `/etc/lightdm/.session` file (content: `cinnamon`) is read by the hook to set the session name.
|
||||
|
||||
---
|
||||
|
||||
## GPU Auto-Detection
|
||||
|
||||
`live-setup.sh` runs `lspci` at boot and writes `/etc/X11/xorg.conf.d/20-gpu.conf`:
|
||||
|
||||
| Detected | Xorg Config | Extra |
|
||||
|----------|-------------|-------|
|
||||
| Virtual (virtio/VMware/QEMU/VirtualBox) | `modesetting`, `AccelMethod none` | `LIBGL_ALWAYS_SOFTWARE=1` in `/etc/profile.d/live-env.sh` |
|
||||
| NVIDIA + proprietary driver (`nvidia_drv.so`) | PRIME offload: Intel `modesetting` + NVIDIA `nvidia` | No software GL |
|
||||
| NVIDIA without proprietary driver | `modesetting` | — |
|
||||
| Intel / AMD / other | `modesetting` | — |
|
||||
|
||||
`LIBGL_ALWAYS_SOFTWARE=1` is set via `/etc/profile.d/live-env.sh`, not via `session-env=` (unsupported).
|
||||
|
||||
---
|
||||
|
||||
## Nix Integration
|
||||
|
||||
### Daemon mode (not single-user)
|
||||
The Void `nix` xbps package ships `nix-daemon` with a runit service at `/etc/sv/nix-daemon`. The daemon puts its socket at:
|
||||
```
|
||||
/var/nix/daemon-socket/socket
|
||||
```
|
||||
|
||||
We use daemon mode (not single-user) because `/nix/store` stays root-owned. The live user is granted trust via `nix.conf`:
|
||||
```
|
||||
experimental-features = nix-command flakes
|
||||
sandbox = false
|
||||
auto-optimise-store = true
|
||||
trusted-users = root live
|
||||
```
|
||||
|
||||
`sandbox = false` is required because the live system has no `nixbld` users and no user namespaces in the dracut initramfs environment.
|
||||
|
||||
### Package list
|
||||
`/usr/local/libexec/nix-packages.list` is written at ISO build time from `NIX_USER_PACKAGES` in `config/install.conf`. At first login, `first-login.sh` reads this file and runs `nix profile install --impure` with `NIXPKGS_ALLOW_UNFREE=1`.
|
||||
|
||||
Current packages:
|
||||
- `nixpkgs#google-chrome`
|
||||
- `nixpkgs#spotify`
|
||||
- `nixpkgs#discord`
|
||||
- `nixpkgs#localsend`
|
||||
- `nixpkgs#mission-center`
|
||||
|
||||
### postinstall.sh socket path (installed system)
|
||||
In the **installed system** (not live), `installer/lib/postinstall.sh` polls for the nix-daemon socket. The correct path is:
|
||||
```
|
||||
/var/nix/daemon-socket/socket
|
||||
```
|
||||
Not `/nix/var/nix/daemon-socket/socket` (upstream Nix default) — Void's package uses `/var/nix/`.
|
||||
|
||||
---
|
||||
|
||||
## dconf / Theme
|
||||
|
||||
The Gruvbox-Dark GTK theme and Cinnamon dconf settings are pre-applied via a system-db. The dconf binary database must be compiled at **ISO build time**, not at runtime.
|
||||
|
||||
### Build-time compilation
|
||||
`iso/_inner-build-live.sh` runs inside the Debian Docker container. The Dockerfile installs `dconf-cli` for this step. The correct Debian `dconf-cli` API is:
|
||||
```sh
|
||||
dconf compile <output_binary_db> <input_keyfile_dir>
|
||||
# e.g.:
|
||||
dconf compile build/live-includes/etc/dconf/db/local \
|
||||
build/live-includes/etc/dconf/db/local.d
|
||||
```
|
||||
|
||||
> **Note:** `dconf update <path>` does not work in Debian's `dconf-cli` — it only updates the user's own db. `dconf compile` is the correct tool for building a system-db binary.
|
||||
|
||||
### dconf profile
|
||||
`/etc/dconf/profile/user` must point to the system-db:
|
||||
```
|
||||
user-db:user
|
||||
system-db:local
|
||||
```
|
||||
Without this file, the compiled system-db is ignored and Cinnamon shows a black wallpaper with default GTK theme.
|
||||
|
||||
---
|
||||
|
||||
## First-Login Setup (`installer/first-login.sh`)
|
||||
|
||||
Runs once via XDG autostart (`~/.config/autostart/void-live-first-login.desktop`) when Cinnamon first loads. Installs:
|
||||
|
||||
1. **Claude Code** — official installer from `https://claude.ai/install.sh`
|
||||
2. **Nix user packages** — from `/usr/local/libexec/nix-packages.list`
|
||||
3. **NVM + Node LTS**
|
||||
4. **VS Code extensions** — from `/etc/installer-vscode-extensions.txt`
|
||||
|
||||
Idempotent: creates `~/.first-login-done` on success. Logs to `~/.first-login.log`.
|
||||
|
||||
The script does NOT use `set -u` because `nvm.sh` references unbound variables.
|
||||
|
||||
---
|
||||
|
||||
## Build Pipeline
|
||||
|
||||
```
|
||||
iso/build-live-iso.sh (host — stages overlay, builds Docker image if needed)
|
||||
└─ Docker: void-installer-builder:latest
|
||||
└─ iso/_inner-build-live.sh
|
||||
├─ dconf compile (pre-bakes system-db)
|
||||
└─ void-mklive/mklive.sh -a x86_64 -r <repo> -I <include_dir> ...
|
||||
└─ squashfs + GRUB + ISO 9660
|
||||
```
|
||||
|
||||
Output: `out/void-live-stable.iso` (~2.9 GB)
|
||||
|
||||
### Build artifacts that must NOT be committed
|
||||
- `build/live-includes/` — generated staging tree (hundreds of binary assets)
|
||||
- `out/` — ISO output
|
||||
- `cache/` — cloned void-mklive, xbps package cache
|
||||
|
||||
---
|
||||
|
||||
## QEMU Testing
|
||||
|
||||
```bash
|
||||
cp /usr/share/OVMF/OVMF_VARS.fd out/OVMF_VARS.live.fd
|
||||
qemu-system-x86_64 -name void-live-test -machine q35,accel=kvm:tcg -cpu max \
|
||||
-m 4096 -smp 4 \
|
||||
-drive "if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE.fd" \
|
||||
-drive "if=pflash,format=raw,file=out/OVMF_VARS.live.fd" \
|
||||
-cdrom out/void-live-stable.iso -boot order=d,menu=off \
|
||||
-netdev user,id=n0 -device virtio-net-pci,netdev=n0 \
|
||||
-serial "unix:out/live-serial.sock,server,nowait" \
|
||||
-monitor "unix:out/qemu-monitor.sock,server,nowait" \
|
||||
-device virtio-vga -display gtk,gl=off &
|
||||
```
|
||||
|
||||
Serial console access (root shell for diagnostics):
|
||||
```python
|
||||
import socket, time
|
||||
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
s.connect('out/live-serial.sock')
|
||||
# send commands, read output
|
||||
```
|
||||
|
||||
GPU in QEMU: `virtio-vga` is detected as virtual → `modesetting + LIBGL_ALWAYS_SOFTWARE=1`.
|
||||
Reference in New Issue
Block a user