Skip to content

Add your own new device to every DM now, and say where it went in (GRYT-1484) - #28

Merged
sivert-io merged 1 commit into
mainfrom
claude/pairing-add-own-device
Sep 28, 2026
Merged

sivert-io merged 1 commit into
mainfrom
claude/pairing-add-own-device

Conversation

@sivert-io

Copy link
Copy Markdown
Member

Part of GRYT-1484 (linking a device). It's item 8 of the build plan in crypto's docs/pairing-design.md. The approving device adds the new one to every DM group straight away, instead of waiting for somebody's next send. And it says where in each group's log the device went in, which the history transfer (item 9) needs to work out the tail.

What's new on the DM driver:

  • addOwnDevice(deviceId, { order, onProgress }). It checks the server lists the device as yours, then goes through every group this device holds. In each one it claims a KeyPackage for that device and checks the certificate names that device id under your own person key. Then it commits an Add with the usual stale_epoch retry. It only adds the device id it was given. order puts the most recently active conversations first. onProgress fires after each group with done and total, for the "40 of 112" line.
  • It returns one result per group: added, added_by_other when another member's commit put the device in first (the lazy add on their next send), or failed with a reason. A failed group doesn't stop the rest. A device under somebody else's person key throws not_own_device and stops everything, since then the server is passing off somebody else's device as yours.
  • add in each result is the seq of the commit that put the device in, and the epoch it starts in. The driver notes this in memory for every commit it applies or makes, so it knows the place when a peer's commit did the add too.
  • groupPositions() gives each group's cursor and epoch. That's what the snapshot covers when A takes it at approval.
  • MlsDecryptedMessage now carries epoch. With it, the tail is the messages with snap.seq < seq < add.seq, plus the ones after add.seq with epoch < add.epoch (sent in the old epoch, landed after the add).

Tests, in a new addOwnDevice.test.ts against the fake delivery service:

  • adds the new device to three DMs, in the order asked, with progress and one commit each. A second call commits nothing and reports the same places.
  • the positions are exact: the snapshot, two tail messages, and an old-epoch message that lands right after the add
  • a stale_epoch race, with Ola removing her tablet at the same time
  • Ola's send adding the device at the same moment, taken as the add
  • a device certified by Mallory's person key under Kari's name gets refused, and so do one the server doesn't list and this device itself

I mutation-checked three of them: dropping the person-key check, the join recording on applied commits, or the message epoch each fails its test.

Where to look:

  • The add position lives in memory only. Say the app restarts between approval and the add, and a peer's commit put the device in meanwhile. Then add comes back null for that group. Pairing sessions don't survive a restart, so I left it there.
  • noteJoins runs mlsGroupMembers twice per commit. Commits are rare, but it's on every one.
  • The new tests have their own copy of the device() helper instead of the one in dmDriver.test.ts, because GRYT-1555 (Stop for good when the server says device_removed (GRYT-1555) #27) changes that one. Merging with Stop for good when the server says device_removed (GRYT-1555) #27 is clean (checked with git merge-tree). Both add a code to MlsDriverErrorCode, in different places.

packages/core is normal review. No release here: core 0.13.0 goes out together with GRYT-1555.

🤖 Generated with Claude Code

…YT-1484)

The pairing flow's approving device needs to add the newly linked device
to every DM straight away, and the history transfer needs each group's
add position to work out the tail. addOwnDevice does the adds, refusing
a device that isn't under your own person key, and reports each group's
position as it goes. groupPositions gives the snapshot's starting point,
and decrypted messages now carry their epoch for the late old-epoch ones.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@sivert-io
sivert-io merged commit 0079350 into main Sep 28, 2026
5 checks passed
@sivert-io
sivert-io deleted the claude/pairing-add-own-device branch September 28, 2026 14:34
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.

1 participant