furtka/docs/wizard-flow.md
Daniel Maksymilian Syrnicki f06d32989d Add wizard-flow spec with locked tech picks
8-screen first-boot installer spec extending Robert's 4-screen
wireframe with the YunoHost-style post-install pattern (domain,
SSL, diagnostic, confirm). Covers:

- Entry point via https://proksi.local with local CA cert install
- Screens S1-S8, each mapped to archinstall config fields or side
  effects (SSL cert issuance, DNS delegation, diagnostic gates)
- Data model mapping wizard fields to user_configuration.json +
  user_credentials.json
- Locked tech picks with rejected alternatives: Caddy (reverse
  proxy), Authentik (SSO), NS delegation (managed gateway DNS),
  local CA (HTTPS on proksi.local)
- Open questions for Robert: Backend on/off meaning, local CA vs
  Tailscale ACME, UI framework choice, language list, S2 auto-setup
  branch behavior

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 20:06:11 +02:00

190 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Installer Wizard Flow
End-to-end spec for the Homebase first-boot installer. Extends Robert's wireframes in [installer-wireframes.md](./installer-wireframes.md) with the post-install pattern proven by YunoHost (see [competitors.md](./competitors.md#yunohost)). Concrete tech picks are locked in at the bottom so Robert has unambiguous targets for the next coding session.
**Status:** Draft spec 2026-04-13. Not implemented yet. Open questions for Robert at the end.
---
## Goals
1. **Your dad can install this.** Boot USB, answer plain-language questions, get a working server. No terminal required on the happy path.
2. **HTTPS from boot #1.** No plaintext admin UI at any stage — unlike Umbrel ([#546](https://github.com/getumbrel/umbrel/issues/546)) and CasaOS. Even the pre-install wizard uses HTTPS via a local CA.
3. **No post-install CLI required.** By screen S8 (confirm), the user has a reachable domain, valid TLS, admin credentials, and the base OS installing. Everything else is reachable from the web UI.
---
## Entry point
1. User flashes ISO to USB (Etcher / `dd` / Rufus).
2. Boots target machine from USB into a **headless live environment**.
3. Live image auto-starts:
- `webinstaller` Flask app on port 443 with TLS
- mDNS advertising `proksi.local`
- Local CA cert generated on first boot, served at `http://proksi.local/ca.crt` (plaintext port 80 serves **only** the CA cert download and a redirect to HTTPS — nothing else)
4. User opens `https://proksi.local` on any device on the same LAN.
5. **First screen before S1:** browser warning page with a one-click "Download Homebase CA" button and OS-specific install instructions (macOS Keychain, Windows cert store, iOS profile, Android). After install, user clicks "Continue" and proceeds to S1 with no further warnings.
> **Why not self-signed (YunoHost model)?** Users learn to click through warnings, which kills trust. A single one-time CA install gives clean green padlocks for every Homebase service after.
---
## Wizard screens
Each screen is one Flask route under `/install/<step>`. The Flask `settings` dict in `webinstaller/app.py` accumulates answers and is flushed to `user_configuration.json` + `credentials.json` at S8.
### S1 — Welcome / Account
From Robert's wireframe Screen 1, trimmed.
| Field | Type | Notes |
|-------|------|-------|
| Hostname | text | Default `furtka`. Becomes `hostname` in archinstall config. |
| Admin username | text | Becomes first entry in `users[].username`. |
| Password | password | |
| Password (confirm) | password | Must match. |
| Language | select | Sets `archinstall-language` + `locale_config.locale`. |
**Dropped from wireframe:** "Backend on/off" and "Backend address" — meaning is unclear, see open questions.
### S2 — Drive Selection
From Robert's wireframe Screen 2.
- Lists drives via `webinstaller/drives.py::list_scored_devices()` (already implemented — do not duplicate).
- Each row shows: device path, size, score, SMART status.
- Radio selection for boot drive, with highest-score drive pre-selected.
- **"Auto setup" button** — accepts the recommendation, jumps past S3.
- Remaining drives carried into S3.
| Field | Type | Maps to |
|-------|------|---------|
| Boot drive | radio | `disk_config.device` |
### S3 — Per-Device Purpose
From Robert's wireframe Screen 3. One screen per non-boot drive.
| Purpose | Action at install time |
|---------|------------------------|
| Mass storage (default for large HDDs) | Added to a shared data pool mounted at `/mnt/data`. |
| App storage (default for fast drives) | Mounted at `/var/lib/docker/homebase-apps`, used for container volumes. |
| Backup target | Formatted and scheduled in the backup service (deferred). |
| I don't know | Same as "Mass storage" — safe fallback. |
Radio buttons; default is `I don't know`.
### S4 — Network
From Robert's wireframe Screen 4.
| Field | Type | Notes |
|-------|------|-------|
| Network mode | radio | DHCP (default) / static |
| Static config | group | IP, gateway, DNS — shown only if "static" picked |
| VPN | toggle | Deferred — stub this out in v1; no VPN config in the wizard yet |
### S5 — Domain (NEW)
The YunoHost-style domain picker. Three paths:
| Option | What it does | Who it's for |
|--------|--------------|--------------|
| **Free `*.homebase.cloud` subdomain** | User picks `myname.homebase.cloud`. DNS is ours, wildcard cert via DNS-01 issued automatically. Managed gateway path. | The "your dad" default. |
| **Bring your own domain** | User enters e.g. `example.com`. Wizard shows two NS records (`ns1.homebase.cloud`, `ns2.homebase.cloud`) to paste at their registrar. We handle the rest. | Users who already have a domain. |
| **Skip — LAN only** | No public domain. Apps accessible as `<app>.proksi.local` via mDNS, with certs from the local CA. | Paranoid users / offline networks. |
Screen validates propagation in the background (NS query every 5s, green check when `nslookup` resolves to our NS). User can click "Next" past yellow, but not past red.
### S6 — SSL / Gateway (NEW)
Auto-configured based on S5.
| S5 choice | S6 behavior |
|-----------|-------------|
| Free subdomain | Silent — wildcard cert already issued by our infra, Caddy picks it up. One line: "✅ TLS ready for `*.myname.homebase.cloud`." |
| BYO domain | Waits for NS propagation (can take minuteshours). Page auto-refreshes. Shows "⏳ Waiting for `example.com` to point to us…" → green check. |
| LAN only | Local CA cert used for `*.proksi.local`. One line: "✅ Local TLS ready." |
User can't advance past S6 until cert is ready (or they go back to S5 and pick a different option).
### S7 — Diagnostic (NEW)
YunoHost-style health check before confirming. Runs in parallel, shows results as they land.
| Check | Green | Yellow | Red |
|-------|-------|--------|-----|
| DNS resolves target domain | `nslookup` succeeds | Succeeds but propagation incomplete | Fails |
| Ports 80 + 443 reachable from WAN | Inbound probe succeeds | CGNAT detected (fallback to Tailscale/Cloudflare tunnel suggested) | Blocked |
| Boot drive mounted | — | — | Fails |
| Docker available | — | — | Missing |
| SMART healthy (all drives) | PASSED | No SMART data | FAILED |
| Admin password strength | ≥12 chars, mixed | 811 chars | <8 chars |
**Rules:** user can continue past yellow with a "I understand" checkbox. Red blocks Next and offers a fix-it shortcut back to the relevant screen.
### S8 — Overview + Confirm
Shows the full `user_configuration.json` and `credentials.json` side by side (credentials masked, with a "show" toggle). User clicks **Install** wizard POSTs to `/install/run`, which:
1. Writes the two JSON files to `/tmp/homebase/`.
2. Fires `archinstall --config /tmp/homebase/user_configuration.json --creds /tmp/homebase/user_credentials.json --silent`.
3. Streams output to a log pane on-screen.
4. On success: shows "🎉 Install complete. Remove USB and reboot. After reboot, log in at `https://<your-domain>`."
---
## Data model
The wizard accumulates answers in the Flask `settings` dict at `webinstaller/app.py:6`. At S8, two JSON files are written. The mapping:
| Wizard field | Target file | Target path |
|--------------|-------------|-------------|
| `hostname` | `user_configuration.json` | `hostname` |
| `language` | `user_configuration.json` | `archinstall-language` |
| `language` (derived) | `user_configuration.json` | `locale_config.locale` |
| S2 `boot_drive` | `user_configuration.json` | `disk_config.device` |
| S3 per-device mounts | `user_configuration.json` | `disk_config.additional_mounts` (new field; extend archinstall profile) |
| S4 `network_mode` | `user_configuration.json` | `network_config.type` |
| S5 `domain` | | Stored in Homebase state (`/etc/homebase/domain.conf`), read by Caddy + Authentik at first boot post-install |
| S5 `domain_mode` | | Same |
| `admin_username` | `user_credentials.json` | `users[0].username` |
| `admin_password` | `user_credentials.json` | `users[0].password` |
| `admin_password` (hash) | | Also pushed to Authentik bootstrap config |
**New archinstall fields** (beyond what's in the current `archinstall/user_configuration.json`):
- `disk_config.additional_mounts` list of `{device, mountpoint, purpose}` needs a custom archinstall profile script.
- Bootstrap script hook to deploy Caddy + Authentik + Homebase admin UI containers on first boot.
---
## Tech decisions (locked)
| Decision | Choice | Why | Rejected |
|----------|--------|-----|----------|
| Reverse proxy | **Caddy** | Automatic Let's Encrypt built-in. Caddyfile is the simplest config of any reverse proxy matches the "simple" ethos. Auto-reloads on config change. | Traefik (label-based config is elegant for Docker but overkill and Kubernetes-flavored), nginx (battle-tested but manual SSL = every competitor's failure mode). |
| Identity provider | **Authentik** | Bundled SSO from day one every app template wires to it at install (YunoHost's best move). Active development, clean admin UI, OIDC + SAML + LDAP. | Authelia (lighter but worse UI and no built-in user management needs external LDAP), external-only (loses the YunoHost wedge of SSO-by-default). |
| Managed gateway DNS | **NS delegation** to `ns1.homebase.cloud` / `ns2.homebase.cloud` | User delegates once at registrar; we handle wildcard cert via Let's Encrypt DNS-01, subdomain creation, propagation. The single biggest UX cliff every competitor dies on. | CNAME-per-subdomain (clunky, users see our hostnames in records), manual A records (the exact pain point we're solving). |
| Local HTTPS | **Local CA** generated at first boot | Single cert install in browser green padlock for every service, no warnings ever. | Self-signed (YunoHost's model users learn to click through warnings), public CA for `.proksi.local` (impossible `.local` is reserved for mDNS), Tailscale-style ACME (good but adds a vendor dependency). |
| Base OS | **Arch** (confirmed) | Rolling releases, Robert's existing Proxmox work. No user-visible difference. | Debian fallback remains documented in README. |
| Container runtime | **Docker + Compose** | Confirmed in README. Authentik and Caddy ship as Compose stacks. | Podman (cleaner rootless story, but Compose ecosystem is smaller and Authentik ships Docker-first). |
---
## What this spec does NOT cover (deferred)
- App store UI and install flow (separate spec after bootable ISO lands).
- Backup target scheduling (S3 stub points to this).
- VPN configuration (S4 stub points to this).
- Multi-node / cluster setup (not v1).
- Update mechanism (rolling updates via Arch pacman + `docker compose pull`).
---
## Open questions for Robert
1. **"Backend on/off" and "Backend address" fields** in your Screen 1 wireframe what do these configure? Dropped from S1 above until this is clear. If it's "connect to Homebase cloud for managed gateway," that's already covered by the S5 "free subdomain" path.
2. **Local CA vs Tailscale-style ACME** I locked in local CA because it's self-contained and works offline. Tailscale's approach (public cert for `*.ts.net`) is smoother but requires Tailscale. Your call.
3. **Wizard UI framework** currently Jinja templates + plain HTML (see `webinstaller/templates/`). If you want HTMX or Alpine for the async parts (S6 cert wait, S7 diagnostics), decide now so the templates don't need rework later.
4. **Language list** which languages ship in v1? Defaulting to English + German + Polish would cover us; anything else up to you.
5. **Auto-setup branch from S2** should it skip S3 entirely (purpose auto-assigned by drive type) or still show S3 with pre-filled defaults for confirmation?