Skip to content

Key asks are saved, and an unused key hands over (GRYT-2014) - #363

Merged
grytchat merged 1 commit into
mainfrom
claude/GRYT-2014-self-healing-keys
Nov 3, 2026
Merged

grytchat merged 1 commit into
mainfrom
claude/GRYT-2014-self-healing-keys

Conversation

@grytchat

@grytchat grytchat commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Sivert and Pingy couldn't DM on pp.sivert.io. The account key there came from an old Windows install. The phone asked to take over, but the ask lived in memory, only reached devices that were online right then, and never went anywhere on its own. This makes that heal by itself.

What changes

  • dm:key:handover:ask now also starts the 30-day reset (dm_key_reset_binding/at), with the same rule as dm:key:reset:request: asking again doesn't restart the clock. It acks { at, holder, seenAt } so the asker can say who it's waiting for.
  • dm:key:handover:pending hands out asks even after the asker has disconnected. Approving only publishes the asker's binding with a handover note, so the asker doesn't need to be online.
  • New columns dm_key_seen_at and dm_key_holder. Publishing with the current identity updates them, at most once an hour unless the name changes. The migration sets seen_at to now on existing rows, so nothing counts as unused on the day this ships.
  • A reset is due 30 days after the ask or 30 days after a device with the current key was last here, whichever comes first (keyResetDueAt). The member list's keyResetAt uses the same value, so contacts and your devices see the real date.
  • dm:key:handover:deny checks the ask exists for this account rather than that the asker is online.

Look closely at

  • The unused-key rule (keyResetDueAt in services/personKeyBinding.ts). This is the change from GRYT-1886 you agreed to: a key nobody has used for 30 days hands over as soon as someone asks. Contacts still get the key-changed pause (GRYT-1877).
  • seen_at is only ever moved forward by a client sending the current identity's binding. A binding is public, so anyone could resend it. That only keeps the key looking used, which slows a takeover and never speeds one up.
  • Asks still sit in memory for the device name. After a restart the saved reset survives but the "Is this your …?" prompt doesn't. The asker asks again on every app start.

Client half: Gryt-chat/client#1155. Server goes out first.

Tests: dmKeys.test.ts 19/19, three new ones (ask outlives the asker, unused key hands over at once, a returning device keeps the full 30 days).

🤖 Generated with Claude Code

An ask to use a device's key now starts the 30-day reset by itself and is
handed to the device with the key even after the asker disconnects. If no
device with the current key has connected for 30 days, the ask finishes at
once. The asker is told which device holds the key and when it was last here.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@grytchat
grytchat requested a review from sivert-io as a code owner November 3, 2026 15:56
@grytchat
grytchat merged commit 544b34b into main Nov 3, 2026
8 checks passed
@grytchat
grytchat deleted the claude/GRYT-2014-self-healing-keys branch November 3, 2026 16:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants