Summary
Display a QR code on the progress screen during installation. User scans with phone → lightweight web app collects preferences and account info → data staged into the target system for instant first-boot personalization.
No other OS installer does this. macOS pairs iPhone via ecosystem lock-in. We do it with any phone via a web page.
Simplest Secure User Flow (2-3 taps)
┌─────────────────────────────────────────────────┐
│ INSTALLER SCREEN │
│ │
│ ┌──────────┐ Scan to personalize your │
│ │ QR CODE │ new system while you wait! │
│ │ │ │
│ └──────────┘ │
│ │
│ QR encodes: │
│ https://192.168.1.50:48372/s#c=H7KM3R&fp=a3b.. │
└─────────────────────────────────────────────────┘
- Scan QR → browser opens (self-signed TLS, cert fingerprint pinned in QR)
- Page auto-authenticates via pairing code in URL fragment (zero extra taps)
- Enter display name, snap avatar (camera), pick dark/light theme
- Optional: "Connect Calendar?" → OAuth → scoped CalDAV token
- Tap "Send" → installer confirms → QR disappears
Data Collected
| Data |
Method |
Staged To |
| Timezone |
Intl.DateTimeFormat() — automatic |
dconf keyfile |
| Language |
navigator.languages — automatic |
locale.conf |
| Display name |
Text input |
AccountsService |
| Avatar photo |
Camera/upload |
/var/lib/AccountsService/icons/ |
| Dark/light pref |
Toggle |
dconf keyfile |
| Accessibility |
Checkboxes |
dconf keyfile |
| Calendar accounts |
OAuth on phone |
EDS .source files (identity only) |
Security Architecture (reviewed by security-review agent)
Threat Model & Mitigations
| Concern |
Severity |
Mitigation |
| HTTP plaintext on LAN |
CRITICAL |
Self-signed TLS cert generated at startup. Fingerprint encoded in QR — QR is the trust anchor. ~20 lines of Go. |
| No auth on HTTP server |
HIGH |
6-char pairing code (charset: ABCDEFGHJKLMNPQRSTUVWXYZ23456789, ~30 bits). Embedded in QR fragment — auto-filled, zero taps. Single-session: rejects after first pair. 3 failures = 30s lockout. |
| OAuth tokens at rest |
MEDIUM |
Do NOT transfer tokens during install. Stage only account identity (email + provider). Trigger real OAuth on first boot inside user session with GNOME Keyring available. |
| Server privilege escalation |
HIGH |
Run HTTP server as unprivileged process (not root). Writes to staging dir only. fisherman (root) validates + deploys separately. Uploaded images: check magic bytes, enforce 512KB max, re-encode. |
| QR visible on screen |
LOW |
Single-use pairing. Confirmation prompt on installer. QR hidden after successful pair. |
Key Security Decisions
- OAuth refresh tokens stored in GNOME Keyring (libsecret) NOT in plaintext
.source files
- Server sandboxed:
PR_SET_NO_NEW_PRIVS, limited filesystem access
- All communication encrypted (self-signed TLS with QR-pinned fingerprint)
- Install cancellation/failure → shred staging directory
Implementation Components
1. Companion HTTP Server (Go, in fisherman)
- Ephemeral self-signed TLS on random high port
- Generates 6-char pairing code
- Accepts JSON POST with preferences
- Writes to
/run/fisherman-companion/ (0700, unprivileged)
- Single-session design
2. Phone Web App (static HTML/JS, embedded in Go binary)
- Mobile-first, works on iOS Safari + Android Chrome
- Camera access for avatar via
getUserMedia()
Intl APIs for automatic timezone/language
- Optional OAuth flows (scoped to calendar only)
- Client-side TLS fingerprint verification via
SubtleCrypto
3. QR Display Widget (Python GTK4)
- Shows QR on progress screen alongside video
- Format:
https://<ip>:<port>/s#c=<code>&fp=<sha256>
- Confirmation banner when phone connects
- Auto-hides QR after pairing
4. First-Boot Config Applier (fisherman post-install)
- Reads staging dir after install
- Writes dconf keyfiles, AccountsService, EDS sources
- Correct ownership/permissions
- Cleanup on failure
Future Extensions (separate issues)
Acceptance Criteria
Summary
Display a QR code on the progress screen during installation. User scans with phone → lightweight web app collects preferences and account info → data staged into the target system for instant first-boot personalization.
No other OS installer does this. macOS pairs iPhone via ecosystem lock-in. We do it with any phone via a web page.
Simplest Secure User Flow (2-3 taps)
Data Collected
Intl.DateTimeFormat()— automaticnavigator.languages— automatic/var/lib/AccountsService/icons/.sourcefiles (identity only)Security Architecture (reviewed by security-review agent)
Threat Model & Mitigations
ABCDEFGHJKLMNPQRSTUVWXYZ23456789, ~30 bits). Embedded in QR fragment — auto-filled, zero taps. Single-session: rejects after first pair. 3 failures = 30s lockout.Key Security Decisions
.sourcefilesPR_SET_NO_NEW_PRIVS, limited filesystem accessImplementation Components
1. Companion HTTP Server (Go, in fisherman)
/run/fisherman-companion/(0700, unprivileged)2. Phone Web App (static HTML/JS, embedded in Go binary)
getUserMedia()IntlAPIs for automatic timezone/languageSubtleCrypto3. QR Display Widget (Python GTK4)
https://<ip>:<port>/s#c=<code>&fp=<sha256>4. First-Boot Config Applier (fisherman post-install)
Future Extensions (separate issues)
Acceptance Criteria