Skip to content

feat: fetch and consume device prekey bundle - #211

Closed
Andreschuks101 wants to merge 136 commits into
codebestia:mainfrom
Andreschuks101:feature/160-key-bundle
Closed

feat: fetch and consume device prekey bundle#211
Andreschuks101 wants to merge 136 commits 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.

codebestia and others added 30 commits May 12, 2026 14:16
feat(backend): PostgreSQL connection with Drizzle ORM + base schema
feat(backend): SIWS wallet auth with JWT + Socket.IO middleware
feat(backend): real-time messaging via Socket.IO with persistent storage
feat(contracts): TokenTransfer Soroban contract with testnet deploy script
feat(web): landing page for Clicked
ci(backend): format check, lint, and test pipeline
Adds GET /users/:id — requires a valid JWT, returns id/username/avatarUrl
plus wallet address/isPrimary pairs; explicit serialization prevents
leaking internal fields. Returns 404 for unknown or malformed IDs.

Fixes codebestia#10
Adds proposals Soroban contract with cast_vote/execute_proposal:
double-vote prevention, expiry enforcement, yes>no pass threshold,
and cross-contract treasury withdraw on passed proposals. Also adds
group_treasury contract gating withdraw behind the proposals contract.
20 tests cover all acceptance criteria.

Fixes codebestia#39
Adds contracts/scripts/deploy_group_treasury.sh mirroring deploy_token_transfer.sh.
Validates DEPLOYER_SECRET, ADMIN_ADDRESS, TOKEN_CONTRACT_ID, and INITIAL_MEMBERS
(comma-separated) before building, uploading WASM, deploying, and calling initialize.
Outputs contract ID and .env setup instructions. Adds GROUP_TREASURY_CONTRACT_ID
to .env.example.

Fixes codebestia#42
- Add useSocket hook connecting to backend via socket.io-client
- Add /app/conversations/[id]/page.tsx with scrollable message thread
- Messages grouped by date with day separators
- Self messages right-aligned (accent), others left-aligned
- Auto-scroll to bottom only when user is already at the bottom
- Shows avatar, sender name, content, and timestamp per message
- Add reusable validate(schema) Express middleware returning structured 400s
- Add ChallengeSchema and VerifySchema for auth routes
- Replace manual if(!field) guards in auth.ts with validate middleware
- Structured error format: { error, issues: [{ field, message }] }
- Add supertest + 6 tests covering valid input, missing fields, wrong types
feat: group treasury contract, Makefile, and AI agent chat/fraud endpoints
…oint-10

feat(api): implement GET user profile endpoint
- Add last_read_message_id nullable FK column to conversation_members schema
- Generate migration 0002_greedy_hellion.sql (ALTER TABLE + FK constraint)
- Add message_read Socket.IO event handler with membership + message validation
- Persist lastReadMessageId per userId/conversationId in conversation_members
- Broadcast read_receipt { userId, lastReadMessageId } to conversation room
- Prevent spoofed reads: validates message exists in target conversation
- Add 4 tests covering success, non-member error, invalid message, DB persistence
- Extract Express app into src/app.ts for testability (server start stays in index.ts)
- Add supertest dev dependency for HTTP assertion
- Add 10 integration test cases for POST /auth/challenge and POST /auth/verify:
  challenge: valid walletAddress, missing walletAddress, empty body
  verify: new user JWT, existing user JWT, expired nonce, invalid sig,
          missing fields, empty body, malformed wallet address (Keypair throws)
- All mocks (DB, nonce, Stellar SDK) are offline — no real network or DB required
- 21 tests pass across 3 test files
- Add ioredis dependency and src/lib/redis.ts with graceful null fallback
- Cache GET /conversations per userId with 30-second TTL (CONV_CACHE_TTL)
- Cache key format: conversations:<userId>
- Invalidate cache for all conversation members after send_message
- Invalidate cache for all new members after create_conversation
- Redis errors silently degrade: requests fall through to DB
- Add 5 tests: cache hit skips DB, cache miss writes to Redis, null Redis fallback,
  Redis error fallback, per-user key format verified
JACOB STANLEY and others added 24 commits June 23, 2026 12:07
[CONTRACT] Add member management to Group Treasury
created apps/web/src/components/ui/Modal.tsx:1
…mponent

add WalletAddress component with copy and explorer link
…ndicator

feat: add online presence indicator to DM avatars and member count to…
feat(ui): implement CopyButton with clipboard API and framer-motion t…
Centralize admin check and update backend configuration
@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

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to create a new devices schema.
The device schema has already been created in a different pr.
Merge main to get it and work with it.

Also, please resolve the conflicts.

@codebestia

Copy link
Copy Markdown
Owner

Hello @Andreschuks101
Unfortunately I will be closing this PR.
There was an issue with the main branch which lead to a fix that affected this PR.
Please raise another PR.
I apologize for the inconvenience this will be causing you.

@codebestia codebestia closed this Jun 26, 2026
@codebestia

Copy link
Copy Markdown
Owner

Please update your main branch before raising another PR.

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