furtka/docs/wizard-flow.md
Daniel Maksymilian Syrnicki 03b2b7d451
Some checks failed
CI / lint (push) Successful in 26s
CI / test (push) Successful in 31s
CI / validate-json (push) Successful in 22s
CI / markdown-links (push) Failing after 2s
chore: rename project Homebase → Furtka, domain furtka.org
furtka.org registered via Strato 2026-04-13, so the working title is
retired. Python package, managed-gateway NS hostnames, and repo URLs all
follow. The CHANGELOG "Unreleased" section documents the switch so the
history is preserved at the 26.0-alpha → next-release boundary.

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

12 KiB
Raw Blame History

Installer Wizard Flow

End-to-end spec for the Furtka first-boot installer. Extends Robert's wireframes in installer-wireframes.md with the post-install pattern proven by YunoHost (see competitors.md). 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) 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 Furtka 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 Furtka 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/furtka-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 *.furtka.cloud subdomain User picks myname.furtka.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.furtka.cloud, ns2.furtka.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.furtka.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/furtka/.
  2. Fires archinstall --config /tmp/furtka/user_configuration.json --creds /tmp/furtka/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 Furtka state (/etc/furtka/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 + Furtka 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.furtka.cloud / ns2.furtka.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 Furtka 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?