cocottetech/@platform/codebase/@features/ai-copilot/docs/yubikey-enrollment.screen.md
natalie 1b719e1fd7 chore(bootstrap): initial V4 commit
Clean successor to V3 (forge: lilith/atlilith). Seeded from local Mac
working tree at ~/Code/@projects/@cocottetech/. node_modules and build
artifacts excluded via .gitignore.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 08:11:41 -07:00

16 KiB
Raw Blame History

yubikey-enrollment.screen

Single-screen breakdown for the YubiKey / Passkey / FIDO2 hardware-key enrollment flow — the four-step guided setup that adds a hardware-bound second factor (and Master-Key wrap source) to the account. Implements brief AF §AF5 (enrollment) and §AF8 (recovery / lost-key flows). Reached from Settings → Safety → "Add a hardware key" (per S §S6) or from the post-first-vigil hearth nudge ("You're getting traction. Lock things down — add a hardware key when you get a chance." per AF5 voice copy). Voice register: hearth on the entry / welcome moment, working on the operational steps (this is a decision flow), plain on lost-key and unrecoverable paths (per voice §V2). One-question-at-a-time pattern, same as persona-seed-interview.screen.md.

Layout (full-screen sheet — iPhone 393×852pt)

┌─────────────────────────────────────────────────┐
│ ◄ Settings                              Cancel  │ 56pt
├─────────────────────────────────────────────────┤
│                                                 │
│   ┌─────────────────────────────────────────┐  │
│   │ 1 of 4 · Tap your primary key           │  │   step header
│   │ ━━━━━━░░░░░░░░░░░░░░░░░░░░░░░░░░       │  │   progress strip
│   └─────────────────────────────────────────┘  │
│                                                 │
│ Cocotte                                         │
│ Hold your key against the top of your phone.    │   working register
│ Tap when you feel the haptic.                   │
│                                                 │
│   ┌───────────────────────────────────────┐    │
│   │              ◌                        │    │   key-tap detector
│   │       (pulse animation)               │    │   pulses while waiting
│   │                                       │    │
│   │       Waiting for key…                │    │   status line
│   └───────────────────────────────────────┘    │
│                                                 │
│   Name this key                                 │   appears after detect
│   ╭─────────────────────────────────────╮      │
│   │ e.g. yellow YubiKey 5 NFC            │      │
│   ╰─────────────────────────────────────╯      │
│                                                 │
│   [primary]                                     │   role badge
│                                                 │
│ ┌─────────────────────────────────────────────┐│
│ │             Continue                         ││   primary button, bottom-pinned
│ └─────────────────────────────────────────────┘│
│                                                 │ 34pt — home indicator
└─────────────────────────────────────────────────┘

Step 4 — Recovery code reveal (variant body):

   ┌─────────────────────────────────────────┐
   │ 4 of 4 · Save your recovery code        │
   │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   │
   └─────────────────────────────────────────┘

   Cocotte  (plain register)
   This restores your Master Key if every key
   and device is lost. Without it, your journal
   and peer history is gone.

   ┌─────────────────────────────────────────┐
   │  abandon   ribbon   crystal   palace    │   12-word BIP39 grid (3×4)
   │  velvet    summer   harvest   wander    │
   │  silver    forest   amber     stone     │
   └─────────────────────────────────────────┘

   [ Copy ]  [ Print ]  [ Show again later ]

   ☐ I've saved this somewhere offline
   ┌─────────────────────────────────────────┐
   │             Finish                       │   disabled until checkbox ticked
   └─────────────────────────────────────────┘

Components

Component Notes
Step header "N of 4 · {step label}" + progress strip. Tappable past dots → revisit step (only for step 1↔2, not the recovery-code step).
Key-tap detector Pulse-animated circle ( / radiating rings) while waiting; green checkmark + soft haptic on read. Replaces NFC / USB / Lightning prompt iconography contextually.
Key-name input Plain text field; placeholder hints at color + model ("yellow YubiKey 5 NFC"). Stored to device_wraps.label.
Role badge [primary] or [backup] chip on the step card. Step 1 = primary; Step 2 = backup (strongly nudged).
Recovery-code display 12 BIP39 words in a 3×4 grid; monospace; high-contrast. Never screenshot-restricted from a UX standpoint (user must be able to capture it) but flagged in audit (brief I) as recovery_code_revealed.
Copy / Print buttons Copy puts on clipboard + auto-clears in 60s. Print routes through AirPrint.
Confirm checkbox "I've saved this somewhere offline" — gates the Finish button. No way to skip.
Continue button Bottom-pinned, primary filled. Disabled until step prerequisite met (key detected + named, or checkbox ticked).
Cancel (top-right) Aborts flow; prompts confirmation if any step ≥ 2 has been completed; partial enrollment is rolled back.

States

  1. Not enrolled (entry card) — Settings → Safety shows "Add a hardware key — recommended" card. Hearth register: "Lock things down. Add a hardware key when you get a chance." Tapping opens this flow. (Same card appears as a one-shot nudge after first vigil per D → AF5.)
  2. Step 1 of 4 · Tap your primary key — pulse detector active, name field hidden. Working register: "Hold your key against the top of your phone. Tap when you feel the haptic."
  3. Detecting key (animated) — pulse intensifies; status line "Reading…". Lasts < 1s typically; timeout at 30s falls back to retry prompt.
  4. Key detected + name input — checkmark replaces pulse; name field appears; Continue enabled once name is non-empty.
  5. Confirm tap (second tap) — micro-step within Step 1: "Tap once more to confirm." Same detector, no name field. Commits the primary device_wraps row.
  6. Step 2 of 4 · Enroll backup — strongly nudged. Hearth-leaning working: "Second key strongly encouraged. If your first key disappears, the second one keeps you in." Skip available but the button reads "Skip backup (not recommended)" with a low-emphasis style; tapping shows a one-shot confirm.
  7. Step 3 of 4 · Tap-test sign-in — verifies both keys produce valid FIDO2 assertions. Prompts: "Tap your primary." → "Now your backup." Failure on either shows the retry / revoke affordances inline.
  8. Step 4 of 4 · Recovery-code reveal — plain register; 12-word grid; copy/print/confirm. Finish disabled until checkbox ticked.
  9. Enrolled (success card) — full-screen receipt: "Keys are in. Two enrolled, recovery code saved." Hearth register. CTA: "Back to Settings" or "Back to chat". Audit row written (brief I).
  10. Lost primary — sign-in with backup — entry from sign-in flow, not Settings. Plain register: "Sign in with your backup key. Revoke the missing one from Settings after." Renders the detector for the backup; on success, routes to a "Revoke missing key?" panel with confirm + immediate re-enrollment CTA.
  11. All keys lost — recovery-code entry — 12-word grid input (not display). Plain register: "Recovery code restores your Master Key. Without it, your journal and peer history is gone." Word-by-word entry with BIP39 wordlist autocomplete; submit only when all 12 are valid wordlist members.
  12. Recovery succeeded — re-enroll prompt — plain: "Master Key restored. Enroll a new primary key now." Routes back to Step 1 with the role badge pre-set.
  13. Recovery failed (wrong code) — plain register, no sugar-coating: "We can't recover keys from our side. That's the whole point." No retry-count exposure (don't leak); user can retry but no hint about which word was wrong beyond BIP39 wordlist validation.
  14. Cancelled mid-flow — Cancel tapped at step ≥ 2 → confirm sheet: "Cancel enrollment? Your primary key won't be saved." Confirm rolls back any device_wraps rows written so far and returns to Settings. Step 1 cancels silently.

Interactions / gestures

  • Tap on the key-tap detector → re-prompts for tap (useful if the user fumbled and the OS-level NFC sheet dismissed).
  • Long-press on a recovery-code word → copies the full 12-word phrase to clipboard + clipboard auto-clears after 60s. (Per-word copy is intentionally not offered — too easy to share one word and think it's safe.)
  • Swipe-down on the step sheet → cancel with confirmation if mid-step ≥ 2; silent dismiss if at step 1.
  • VoiceOver swipe → reads step header, status line, action button in that order; skips decorative pulse.
  • Tap role badge → tooltip explains primary vs backup ("Backup keys can sign in if the primary is lost or damaged.").
  • Tap "Show again later" on Step 4 → schedules a Settings reminder card; does NOT unlock Finish (the checkbox is still required).

In-the-wild copy

  • (hearth, entry card from Settings or post-vigil nudge) — "You're getting traction. Lock things down — add a hardware key when you get a chance."
  • (working, Step 1) — "Hold your key against the top of your phone. Tap when you feel the haptic."
  • (working, Step 1 confirm) — "Tap once more to confirm. Same key."
  • (working, Step 2 backup) — "Second key strongly encouraged. If your first key disappears, the second one keeps you in."
  • (working, Step 3 test) — "Quick test. Tap your primary. Then your backup."
  • (plain, Step 4 recovery) — "This restores your Master Key if every key and device is lost. Without it, your journal and peer history is gone."
  • (hearth, success card) — "Keys are in. Two enrolled, recovery code saved. The kitchen's locked up well."
  • (plain, lost-primary sign-in) — "Sign in with your backup key. Revoke the missing one from Settings after."
  • (plain, recovery failed) — "We can't recover keys from our side. That's the whole point."

Edge cases

  • NFC key + iPhone alignment failure — after 3 failed reads in 15s, the detector swaps the pulse for a small alignment diagram (key against the top edge of the phone, near the camera). One-shot — don't nag.
  • USB-C YubiKey on Lightning iPhone (or vice versa) — on Step 2, if the primary was USB-C and the device is Lightning, the backup-key suggestion explicitly nudges: "Pick a backup that fits this phone — Lightning or NFC. Your primary is USB-C; that's fine but it won't sign you in here in a pinch." Linked to YBK-Q1 vendor purchase.
  • VoiceOver narration of the tap-prompt — pulse animation is decorative-only; status line "Waiting for key" is the read-aloud anchor; on detect, announces "Key detected. Name field below."
  • Reduced motion (per X) — pulse animation replaced with a static "Waiting for key" text + a single soft fade on detect. No radiating rings.
  • Cellular Watch sign-in (per AB §AB-Q5) — Watch cannot enroll hardware keys; if entered from a Watch nudge, the Watch screen shows "Enroll on phone" and hands off via Continuity. The flow itself never runs on Watch.
  • Recovery-code entry with wrong / misspelled words (state 11) — Levenshtein-tolerant prompt per word: "Did you mean: crystal?" Only suggests BIP39-list words within edit distance 2. Suggestion is per-word, never per-phrase (don't reveal phrase shape).
  • OS-level NFC sheet collision — iOS shows its own "Hold Near Top of iPhone" sheet over ours; we suppress our pulse while that sheet is up and resume on dismiss.
  • Specialist-degraded (per M §M2 / L §L4) — if the auth specialist is offline, Step 1 shows a plain-register banner: "Can't enroll keys right now. Try again in a few minutes." Cancel-only; no partial write.

Out of scope

  • Physical key acquisition / purchase flow — linked out to the vendor site (YubiKey.com / Apple Passkey docs).
  • Enrollment outside the Settings entry point (no deep-linkable enrollment URL at P0).
  • Cross-device backup-key transport — keys are enrolled per-device-context per AF5; transferring a backup key's device_wraps row to a new phone is a separate (§AF7) flow.
  • Org-level fleet enrollment for member keys — see YBK-Q3.

Open questions

  • YBK-Q1 · In-app vendor purchase link vs out-of-app — should the entry card carry a "Don't have a key? Get one" CTA? Lean: out-of-app deep link to yubico.com / Apple's passkey docs; no in-app commerce at P0. [nice-to-have]
  • YBK-Q2 · Recovery-code regeneration frequency — one-shot at first run only, or regenerate-on-demand from Settings? Lean: one-shot at first run + regenerate-on-demand from Settings → Safety, with the old code invalidated and audit row written. [blocking]
  • YBK-Q3 · Multi-tenant orgs — personal vs org-enrolled keys — in coop / org contexts (per W), does each member enroll their own key, or does the org enroll keys on members' behalf? Lean: each member personally; the org cannot wrap or hold member keys — that would break the person-first tenancy contract. [exploratory]