164 lines
7.6 KiB
Markdown
164 lines
7.6 KiB
Markdown
# OEM Install + Factory Reset Plan
|
|
|
|
## Goal
|
|
|
|
An install mode that installs the entire system but defers user account setup to
|
|
first boot. On first power-on the machine asks for the user, creates it, and
|
|
lands in the already-installed system — the classic OEM out-of-box experience.
|
|
The same machinery doubles as a "reset computer" option for wiping a machine
|
|
before selling it.
|
|
|
|
## Core idea: "OEM state" as a first-class system state
|
|
|
|
Define one canonical state — *fully installed, no user, setup pending* — and
|
|
make both paths produce it:
|
|
|
|
- **ISO OEM mode** ends the install in OEM state instead of creating the user.
|
|
- **Reset** returns a running machine to OEM state.
|
|
|
|
OEM state is: a fully installed system plus `/var/lib/omarchy/oem/` containing:
|
|
|
|
| File | Purpose |
|
|
|------|---------|
|
|
| `pending` | Flag that arms the first-boot setup service |
|
|
| `packages/node-v*.tar.gz` | Node tarball copied off the ISO's `/opt/packages` (~30MB) so `omarchy-finalize-user` works offline at first boot |
|
|
| `groups` | Deferred `usermod -aG` groups written by system setup (see below) |
|
|
| `luks-key` | Throwaway LUKS passphrase for encrypted installs, staged for auto-unlock until first-boot re-key |
|
|
|
|
## Why the seam is nearly clean already
|
|
|
|
Everything in the orchestrator through `finalize_limine_boot` is
|
|
user-independent except five touch points:
|
|
|
|
1. `installer.create_users` — useradd + password (root gets the same password).
|
|
2. Three `usermod -aG` calls inside `omarchy-setup-system`'s scripts
|
|
(`config/docker.sh`, `hardware/input-group.sh`, `hardware/apple/fix-t2.sh` —
|
|
docker/input/video). This is the *only* reason `omarchy-setup-system`
|
|
requires the user to exist (its getent check).
|
|
3. `run_chroot_finalizer` → `omarchy-finalize-user` as the user. Already
|
|
offline-safe: `/etc/skel` does the heavy seeding at useradd time,
|
|
`omarchy-mise-install` writes lazy wrappers, and the only external artifact
|
|
is the bundled Node tarball from `/opt/packages` (`install/user/mise.sh`).
|
|
4. `configure_login` — SDDM `state.conf` and the encrypted-install autologin.
|
|
5. Encryption — the user's password *is* the LUKS passphrase.
|
|
|
|
Nothing in hardware setup, Limine, hibernation, snapper, or package
|
|
installation cares who the user is.
|
|
|
|
## First boot: `omarchy-oem-setup.service`
|
|
|
|
Ships in the omarchy runtime package (so reset can arm it without the ISO):
|
|
|
|
- `ConditionPathExists=/var/lib/omarchy/oem/pending`
|
|
- `Before=display-manager.service`, conflicts `getty@tty1`, TUI on tty1.
|
|
- Runs the user form extracted from the configurator (Step 2: username,
|
|
password, full name, email). The configurator is bash+gum, so the form moves
|
|
into a runtime-shipped `omarchy-oem-setup` script that reuses the exact ISO
|
|
look (logo, Tokyo Night VT palette).
|
|
|
|
Then it does what the ISO would have done:
|
|
|
|
1. `useradd` with wheel + the groups from `/var/lib/omarchy/oem/groups`,
|
|
set user + root password.
|
|
2. `omarchy-finalize-user` as the user, with a setup context that points at the
|
|
stashed Node tarball (a sibling of the existing `iso-chroot` context).
|
|
3. The `configure_login` writes: SDDM `state.conf`, autologin for encrypted
|
|
installs.
|
|
4. If `luks-key` exists: `cryptsetup luksChangeKey` to the user's password,
|
|
delete the stash, remove the auto-unlock keyfile, rebuild initramfs.
|
|
5. Git identity from the form (the `OMARCHY_USER_NAME`/`OMARCHY_USER_EMAIL`
|
|
path in `install/user/git.sh`).
|
|
6. Remove `/var/lib/omarchy/oem/`, hand off to SDDM.
|
|
|
|
The user lands in their session exactly like a normal install.
|
|
|
|
## Encryption: keep it, Pop!_OS-style
|
|
|
|
Do not force OEM installs unencrypted. Install encrypted with a generated
|
|
throwaway passphrase, stage a keyfile so boot auto-unlocks during the OEM
|
|
window, and have first-boot setup re-key to the user's chosen password
|
|
(step 4 above). Limine and mkinitcpio don't care what the passphrase is. This
|
|
preserves encrypted-by-default without the machine builder ever knowing the end
|
|
user's password.
|
|
|
|
## ISO-side changes (this repo)
|
|
|
|
- **Configurator**: an OEM choice in the install-mode step that skips Step 2
|
|
(user), plus a cidata `oem` marker file that replaces the
|
|
`user_credentials.json` requirement — that's what actual OEM imaging rigs
|
|
will use via autoinstall.
|
|
- **Orchestrator**: `oem: true` in the `omarchy_install` JSON. Swaps the three
|
|
user phases (`create_users` inside `arch_install_system`,
|
|
`run_chroot_finalizer`, the user parts of `configure_login`) for a
|
|
"stage OEM state" phase that writes `/var/lib/omarchy/oem/` and enables
|
|
`omarchy-oem-setup.service` in the target. `InstallContext.username` needs an
|
|
OEM-safe fallback (creds are absent).
|
|
- **Factory snapshot**: at the end of *every* install (not just OEM), take a
|
|
read-only snapshot of `@` kept at the top level as `@factory` — outside
|
|
snapper's `.snapshots`, so cleanup timers and the Limine snapshot menu never
|
|
touch it. Zero bytes at creation; grows only with drift. This is what makes
|
|
reset a true factory reset.
|
|
|
|
## Runtime-side changes (omarchy repo)
|
|
|
|
- `omarchy-oem-setup` + `omarchy-oem-setup.service` (the OOBE above).
|
|
- `omarchy-reset-computer` (below).
|
|
- Relax `omarchy-setup-system`: when the install user doesn't exist yet, the
|
|
three `usermod -aG` scripts append to `/var/lib/omarchy/oem/groups` instead
|
|
of failing the getent check.
|
|
|
|
## Reset: `omarchy-reset-computer`
|
|
|
|
Behind a very explicit confirmation (typed, not y/n):
|
|
|
|
1. Point the default subvolume at a writable snapshot of `@factory`; stage a
|
|
next-boot wipe unit.
|
|
2. On reboot: recreate `@home` (subvolume delete/create — instant, no rm -rf),
|
|
clear `@log`, delete snapper snapshots, reset machine-id, SSH host keys,
|
|
NetworkManager connections, Tailscale state, SDDM state; run `fstrim`.
|
|
3. Re-arm OEM state: `pending` flag, `groups`, Node tarball (kept from install
|
|
or re-fetched into `@factory` at install time), and on encrypted machines a
|
|
throwaway LUKS re-key — the buyer never needs the seller's passphrase.
|
|
|
|
Next boot is the same OOBE as a fresh OEM install. User-installed packages and
|
|
/etc drift are gone, which is what "wipe and sell" actually promises.
|
|
|
|
Machines installed before `@factory` existed get a degraded reset — keep the
|
|
current system, wipe users/state only — with that caveat surfaced in the
|
|
confirmation.
|
|
|
|
### Honest limitations (document these)
|
|
|
|
- On unencrypted disks this is deletion, not forensic erasure. `fstrim` helps
|
|
on SSDs; offer `cryptsetup reencrypt` or `blkdiscard` + reinstall for the
|
|
paranoid.
|
|
- `luksChangeKey` changes the passphrase but not the volume key; freed extents
|
|
remain readable to someone with raw-device access and the new passphrase.
|
|
Same remedy as above.
|
|
- A reset machine boots with day-one packages until it updates — same as a
|
|
fresh install.
|
|
|
|
## Sequencing
|
|
|
|
The OOBE lives in the omarchy runtime package, but the ISO's OEM phase depends
|
|
on it existing. So:
|
|
|
|
1. Ship the runtime side first (`omarchy-oem-setup`, service, reset command,
|
|
`omarchy-setup-system` relaxation).
|
|
2. ISO OEM mode then asserts the service file exists in the target before
|
|
offering the mode.
|
|
3. Factory snapshot can land with either half; reset requires both.
|
|
|
|
## Validation
|
|
|
|
- VM: OEM install (encrypted + unencrypted) → reboot → OOBE → session; verify
|
|
LUKS passphrase is the user's, throwaway key gone, groups applied,
|
|
`omarchy-finalize-user` state marker present.
|
|
- VM: normal install → use system → `omarchy-reset-computer` → OOBE as new
|
|
user; verify no trace of prior user (home, NM connections, tailscale,
|
|
machine-id, host keys, snapper snapshots).
|
|
- Autoinstall: cidata drive with `oem` marker and no credentials → unattended
|
|
OEM install.
|
|
- Negative: OEM ISO built against a runtime package lacking the service fails
|
|
the install with a clear error, not a user-less boot loop.
|