Feat/prem keys - #529
Open
KodeSage wants to merge 2 commits into
Open
Conversation
|
@KodeSage Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Warns a device to replenish its one-time prekeys before it runs dry, instead of only discovering the problem at exhaustion.
Once a device's unconsumed one-time prekeys hit zero, every sender fetching its bundle is silently downgraded to a 3-DH session with no forward-secrecy contribution from a one-time key. This PR surfaces the shortfall early, over two surfaces:
oneTimePreKeysRemaininginGET /devices— already present onmain; this PR verifies it and documents it as the reconnect path for devices that were offline when the threshold was crossed.prekeys_lowWebSocket event, emitted after a bundle fetch drops a device below the threshold (default20, overridable via the newPREKEY_LOW_THRESHOLDenv var).Changes:
services/prekeyLowSignal.ts: owns the threshold, the unconsumed-prekey count helper, and the debounce latch.SET NX EXonprekeys:low:{deviceId}, so two gateways racing on concurrent bundle fetches still produce exactly one emit. Falls back to an in-processSetwhen Redis is unconfigured (dev/tests) and on Redis errors, degrading to local debounce rather than either spamming the device or going silent.routes/users.ts): the remaining-count query moved inside the existing claiming transaction, soremainingreflects this consumption and cannot race a concurrent fetch into a wrong value. The emit is fire-and-forget and never blocks or fails the bundle response.routes/devices.ts): the upload route recounts from the DB rather than derivingcurrentCount + batch.length, becauseON CONFLICT DO NOTHINGsilently drops duplicatekeyIds and would inflate the derived figure. The response now echoesoneTimePreKeysRemainingso clients can confirm they cleared the threshold without a follow-upGET /devices.revokeDeviceRowdeletes the device's prekeys, so a leftover latch would otherwise suppress the first warning if that identity key is later re-registered (whichPOST /devicessupports).apps/backend/docs/e2ee-onboarding.mdcovering the event payload, delivery targeting, and debounce semantics;PREKEY_LOW_THRESHOLDadded to.env.example.Event payload:
{ "deviceId": "uuid", "oneTimePreKeysRemaining": 19, "threshold": 20 }Design notes:
device:{id}room, not the user room. Only the owning device can generate replacement prekeys, and thedevice:room already routes across horizontally-scaled gateways via the Socket.IO Redis adapter.oneTimePreKeysRemainingonGET /devicesmatters as the reconnect path. Building socket-event persistence is a larger design decision than this issue calls for, so it is documented rather than implemented.Issue numbers
Closes #161
Type of change
Checklist
Testing
src/__tests__/prekeysLow.test.tscovers the acceptance criteria directly: emits below threshold, silent at/above it, at most one emit across a full 19→0 drain, per-device latch isolation, stops firing after replenishment then fires again on the next crossing, and swallows socket-layer failures.users.bundle.test.tsasserts the bundle route feeds the post-consumption count to the signal, and that an exhausted fetch does not re-signal.devices.prekeys.test.tsasserts upload re-arms the latch only once the device is back at or above the threshold.tsc --noEmitproduces an identical error count tomain(11 lines, all pre-existing inroomManager.ts/uploads.test.ts/stellarListener.ts) — no new type errors.eslintandprettier --checkclean on all changed source files.