Skip to content

feat(devices): add /devices register and revoke endpoints; emit syste…#245

Open
EDOHWARES wants to merge 1 commit into
codebestia:mainfrom
EDOHWARES:feat/devices-system-events
Open

feat(devices): add /devices register and revoke endpoints; emit syste…#245
EDOHWARES wants to merge 1 commit into
codebestia:mainfrom
EDOHWARES:feat/devices-system-events

Conversation

@EDOHWARES

Copy link
Copy Markdown

feat(devices): device registration + system events on add/revoke

Summary

  • Add device registration endpoint (POST /devices) and device revoke endpoint (DELETE /devices/:id).
  • Emit a system-style message into every conversation the user belongs to when a device is added or revoked, and broadcast it via Socket.IO.
  • Add a Zod schema for device registration and validate that identityPublicKey is base64 encoded and exactly 32 bytes.
  • Prevent duplicate (userId, deviceId) registrations by returning 409 Conflict.
  • Ensure newly registered devices start with zero prekeys by design.
  • Invalidate conversation caches for affected members after emitting device-change events.

Files changed (high level)

devices.ts

Added:

  • RegisterDeviceSchema
  • POST /devices endpoint
  • DELETE /devices/:id endpoint
  • emitDeviceChangeEvent helper

Device registration (POST /devices)

  • Validates identityPublicKey as:
    • Base64 encoded
    • Exactly 32 bytes when decoded
  • Rejects duplicate (userId, deviceId) registrations with 409 Conflict.
  • Requires authentication and binds devices to req.auth.userId.
  • Does not create prekeys during registration.

Device revoke (DELETE /devices/:id)

  • Scoped to the authenticated user.
  • Revokes only devices owned by req.auth.userId.

Device change events

emitDeviceChangeEvent:

  • Inserts a system-style message into every conversation the user belongs to.
  • Broadcasts the inserted message to conversation rooms using Socket.IO.
  • Invalidates conversation caches for affected members.

Existing prekeys functionality remains unchanged.


Acceptance Criteria Mapping

Requires JWT; device bound to req.auth.userId

  • ✅ Both endpoints use requireAuth.
  • ✅ Device ownership is derived from req.auth.userId.

Invalid or short public key returns 400

  • identityPublicKey must:
    • Be valid base64
    • Decode to exactly 32 bytes

Duplicate device returns 409

  • ✅ Registration checks for an existing (userId, deviceId) pair and returns 409 Conflict.

New device starts with zero prekeys

  • ✅ No prekeys are inserted during registration.
  • ✅ Prekeys remain empty until uploaded through the existing prekeys endpoint.

Adding or revoking a device produces a system event in shared conversations

  • emitDeviceChangeEvent inserts a message into each conversation the user belongs to.
  • ✅ Message payload is stored as JSON in messages.content.

Example payload:

{
  "userId": "user-id",
  "change": "device_added"
}

or

{
  "userId": "user-id",
  "change": "device_revoked"
}
  • ✅ Event is emitted to the corresponding Socket.IO conversation rooms.

Cache invalidation for members is triggered

  • invalidateConversationCaches() is called for conversation members after emitting events.

Implementation details & notes

System event content

System events are currently stored in:

messages.content

as a JSON stringified object:

{
  "userId": "...",
  "change": "device_added"
}

or

{
  "userId": "...",
  "change": "device_revoked"
}

The messages table does not currently contain a content_type column.

If explicit system message typing is desired, a migration will be required.

Suggested schema change:

content_type = 'system'

Socket.IO

Uses the existing server instance:

getSocketServer()

to emit inserted messages into conversation rooms.

Cache invalidation

Conversation caches are invalidated for affected members immediately after broadcasting.

Revoke behavior

Revocation is scoped to:

(userId, deviceId)

to prevent users from deleting devices belonging to others.

Error handling

  • Logs internal errors.
  • Returns standard HTTP status codes.

Testing / verification

Unit and integration tests should verify:

POST /devices

  • 201 Created on success

Response shape:

{
  "id": "...",
  "deviceId": "...",
  "createdAt": "..."
}
  • 400 Bad Request for:

    • Invalid base64
    • Incorrect public key length
  • 409 Conflict for duplicate registrations

DELETE /devices/:id

  • 200 OK when revoked
  • 404 Not Found when device does not exist
  • 403 Forbidden when attempting to revoke another user's device

Device change events

After add or revoke:

  • ✅ A message exists in every shared conversation
  • ✅ A new_message Socket.IO broadcast occurs
  • ✅ Conversation cache invalidation is triggered

Socket.IO interactions can be mocked in tests.

Manual verification

Register a device for User A and verify:

  • No prekeys exist for the device
  • Shared conversations receive a new system-style message
  • Message payload contains:
{
  "userId": "...",
  "change": "device_added"
}

Revoke the device and verify:

  • Device is removed
  • Shared conversations receive a corresponding device_revoked message

Migration / Follow-ups

If explicit system message typing is desired:

  • Add a migration to the messages table
  • Introduce a content_type column or enum
  • Set:
content_type = 'system'

inside emitDeviceChangeEvent.

Additional improvements:

  • Centralize system event payloads into a shared utility or enum.
  • Add integration tests for device registration, revocation, and event emission.
  • Add tests for Socket.IO broadcasts and cache invalidation behavior.

Closes #155
Closes #168

@drips-wave

drips-wave Bot commented Jun 26, 2026

Copy link
Copy Markdown

@EDOHWARES 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

@EDOHWARES

Copy link
Copy Markdown
Author

@codebestia , pls review PR

@codebestia

Copy link
Copy Markdown
Owner

@EDOHWARES Please fix the CI

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.

Emit a key-change/system event when a device is added or revoked POST /devices — register a new device for an existing user

2 participants