Skip to content

feat: fetch and consume device prekey bundle#211

Open
Andreschuks101 wants to merge 1 commit into
codebestia:mainfrom
Andreschuks101:feature/160-key-bundle
Open

feat: fetch and consume device prekey bundle#211
Andreschuks101 wants to merge 1 commit into
codebestia:mainfrom
Andreschuks101:feature/160-key-bundle

Conversation

@Andreschuks101

Copy link
Copy Markdown

Fetch + consume prekey bundle — GET /users/:userId/devices/:deviceId/key-bundle

closes #160

Serves a recipient device's public prekey bundle and consumes one one-time prekey
per fetch, so a sender can start an X3DH/Signal-style encrypted session.

Changes

  • Schema/migration: devices (identity public key, registrationId, signed
    prekey + signature, revokedAt) and one_time_pre_keys (consumed flag,
    unique (device_id, key_id), (device_id, consumed) index). Migration
    0007_device_key_bundles. Only public key material is stored.
  • Service fetchAndConsumeKeyBundle: looks up the device, returns 404 if it is
    unknown or revoked, and otherwise returns
    { identityPublicKey, registrationId, signedPreKey, oneTimePreKey | null }.
  • Route: GET /users/:userId/devices/:deviceId/key-bundle, authenticated.

Atomic, race-free consumption

The one-time prekey is claimed in a single statement:

UPDATE one_time_pre_keys SET consumed = true
WHERE id = (SELECT id FROM one_time_pre_keys
            WHERE device_id = $1 AND consumed = false
            ORDER BY key_id FOR UPDATE SKIP LOCKED LIMIT 1)
RETURNING key_id, public_key

FOR UPDATE SKIP LOCKED ensures concurrent senders skip a row another transaction
is already claiming, so the same one-time prekey can never be issued twice. When
the pool is exhausted the bundle is still returned with oneTimePreKey: null.

Acceptance criteria

  • One-time prekey consumption is atomic and race-free under concurrent fetches.
  • Returns oneTimePreKey: null gracefully when the pool is exhausted.
  • Revoked (and unknown) devices return 404.
  • No private key material is ever stored or returned.

Testing

  • New service and route tests: atomic/concurrent distinct-OTP behaviour, graceful
    null on exhaustion, 404 for unknown and revoked devices, and a "no private
    material" shape assertion.
  • Full backend suite passes (108 tests); lint and prettier clean.

@drips-wave

drips-wave Bot commented Jun 25, 2026

Copy link
Copy Markdown

@Andreschuks101 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! 🚀

Learn more about application limits

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.

GET /users/:userId/devices/:deviceId/key-bundle — fetch + consume prekey bundle

1 participant