Device-security nudge: verify this device / key backup (#110, #123) #258

Merged
jared merged 1 commits from security-nudge into lotus 2026-09-30 21:03:23 -04:00
Owner

Implements #110 and #123 together as one device-security strip. The design was approved on #123, where the real-client screenshots are.

States, in priority order

  1. Verify this device: cross-signing exists, but this device isn't verified. This comes first, because verifying with the recovery key also connects the backup.
  2. Connect this device to your key backup: a backup exists, but this device isn't using it.
  3. Set up key backup: there's no backup at all.

The button opens Settings → Devices (the existing flows), and Not now snoozes the nudge.

Rules

  • Grace period: nothing in a device's first 24 h.
  • Snooze and limit: Not now snoozes that nudge for 7 days; it stops after 3 dismissals.
  • Never shown on a healthy device.
  • One strip at a time: it waits for sync to settle, and the outage, maintenance and connection strips from #124 take priority.
  • Local only: a per-device record in localStorage, wiped on logout.
  • Can't break the app: it's mounted inside the Matrix client context and an error boundary. An early version outside the context crashed the app on load; that was caught before pushing.

Also changed

On phones, the status strip now wraps its text instead of cutting it off when there's no Details button. That helps #124's strips too.

Tests

  • Unit: 4 (the #123 state table, loading, timing, the stored record).
  • End-to-end (e2e/security-nudge.spec.ts, local Synapse):
    • nothing during the grace period;
    • Set up key backup opens Settings → Devices;
    • Not now holds across a reload.
  • Full suites: unit 1,319 pass; Playwright 29 passed.
  • Checked by hand: Verify this device, on a second device of an account set up through the real flow (screenshots on #123).

🤖 Generated with Claude Code

https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA

Implements **#110** and **#123** together as one device-security strip. The design was approved on #123, where the real-client screenshots are. ## States, in priority order 1. **Verify this device:** cross-signing exists, but this device isn't verified. This comes first, because verifying with the recovery key also connects the backup. 2. **Connect this device to your key backup:** a backup exists, but this device isn't using it. 3. **Set up key backup:** there's no backup at all. The button opens **Settings → Devices** (the existing flows), and **Not now** snoozes the nudge. ## Rules - **Grace period:** nothing in a device's first **24 h**. - **Snooze and limit:** **Not now** snoozes that nudge for **7 days**; it stops after **3** dismissals. - **Never shown** on a healthy device. - **One strip at a time:** it waits for sync to settle, and the outage, maintenance and connection strips from #124 take priority. - **Local only:** a per-device record in localStorage, wiped on logout. - **Can't break the app:** it's mounted inside the Matrix client context **and an error boundary**. An early version outside the context crashed the app on load; that was caught before pushing. ## Also changed On phones, the status strip now wraps its text instead of cutting it off when there's no **Details** button. That helps #124's strips too. ## Tests - **Unit:** 4 (the #123 state table, loading, timing, the stored record). - **End-to-end** (`e2e/security-nudge.spec.ts`, local Synapse): - nothing during the grace period; - *Set up key backup* opens Settings → Devices; - **Not now** holds across a reload. - **Full suites:** unit 1,319 pass; Playwright 29 passed. - **Checked by hand:** *Verify this device*, on a second device of an account set up through the real flow (screenshots on #123). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
jared added 1 commit 2026-09-30 20:28:49 -04:00
feat: device-security nudge — verify this device / key backup (#110, #123)
CI / Build & Quality Checks (pull_request) Successful in 1m48s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 12s
CI / Playwright smoke (e2e) (pull_request) Successful in 11m38s
6a7627fa26
One "security" strip, in the same top slot and style as the status banner
(#124), for this device, in priority order:
- "Verify this device" — cross-signing exists but this device isn't
  verified (can't unlock backed-up history, untrusted to others). First,
  because verifying with the recovery key also connects the backup.
- "Connect this device to your key backup" — a backup exists, this device
  isn't using it.
- "Set up key backup" — no backup at all (includes accounts without
  cross-signing; the setup flow does both).
The button opens Settings → Devices (existing flows); "Not now" snoozes.

Rules: nothing in a device's first 24 h; "Not now" snoozes that nudge 7
days; 3 dismissals stop it; never on a healthy device; waits until sync
has settled (never under "Connecting…"); outage/maintenance/connection
strips win. Per-device record in localStorage, wiped on logout. Mounted
inside the Matrix client context and an error boundary (an early version
outside the context crashed the app on load — caught before pushing).

Also: the status strip wraps its text on phones instead of truncating it
when there's no Details button (benefits #124's strips too).

Design approved on #123 (real-client screenshots there). Tests: 4 unit
(the #123 state table, loading, timing, stored record); e2e: nothing during
the grace period, "Set up key backup" → Settings → Devices, "Not now"
holds across a reload. Unit 1319, Playwright 29 passed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
jared merged commit d1993f2084 into lotus 2026-09-30 21:03:23 -04:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: LotusGuild/cinny#258