Skip to content

epic: QR Code Phone Companion — personalize system during install #25

Description

@castrojo

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.. │
└─────────────────────────────────────────────────┘
  1. Scan QR → browser opens (self-signed TLS, cert fingerprint pinned in QR)
  2. Page auto-authenticates via pairing code in URL fragment (zero extra taps)
  3. Enter display name, snap avatar (camera), pick dark/light theme
  4. Optional: "Connect Calendar?" → OAuth → scoped CalDAV token
  5. 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)

  • GSConnect/Valent auto-pair via QR
  • Tailscale mesh invite via QR
  • GNOME Remote Desktop pre-pairing

Acceptance Criteria

  • QR displays during progress phase
  • Phone loads page in <2s over LAN
  • Self-signed TLS active (no plaintext)
  • Pairing code validates, lockout on failure
  • Display name + avatar staged correctly
  • Dark mode preference applies on first boot
  • Timezone/language auto-detected
  • Calendar identity staged (OAuth on first boot)
  • Cancelled installs wipe staging directory
  • Works on iOS Safari + Android Chrome

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind/epicMulti-issue tracker. File child issues instead of implementing here.priority/p2Normal backlog. Pick last when no hive/ or priority/p0-p1 exist.source:agentFiled by an AI agentstatus/queuedReady to claim. Contributor: comment /claim to take this.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions