Changelog & Gap Report

Changelog & Gap Report

What shipped versus the spec: new opcodes, renumbering, PQC migration, default room, social blob, and audit items.

Changelog & Gap Report

#

Added WebSocket operations

Three authenticated WebSocket operations were added for QXP-PHANTOM:

OpcodeNameRequestResponse
36Publish prekeya prekey bundle{ "ok": true, "version": 1 }
37Fetch prekeys{ "usernames": ["..."] }{ "bundles": { "user": bundle } }
39Update blocks{ "add": ["<hex64>"], "remove": ["<hex64>"] }{ "filter": ["<hex64>"] }

All three require an identified session and follow the existing respond_error(state, sid, op, message, request_id) error pattern with static messages.

Opcode renumbering versus the specification

The design specification assigned PHANTOM the opcodes 36, 37, and 45. In the shipped code, opcode 45 was already occupied by moderation (kick), so block updates use 39. Prekey publish and fetch keep their specification opcodes.

ConceptSpecification opcodeShipped opcode
Publish prekey3636
Fetch prekeys3737
Update blocks4539

Default room

A single default room record was added so the server can offer an onboarding or announcement room. The record is stored in the default_room table with a fixed row id of 1 and contains roomId, roomKey, and title.

  • Admin endpoint: POST /api/admin/default-room with { roomId, roomKey, title } or { clear: true }.
  • The identification response (opcode 2) includes the record as defaultRoom.
  • The client persists an allowServerDefaultRoom toggle and a per-room defaultRoomLeavedRoomId flag. The client auto-joins on identification unless the user disabled the behavior or already left that specific room.

Social blob

The encrypted roster sync was added as GET /api/social/blob and PUT /api/social/blob. The blob is an AES-256-GCM ciphertext produced by the client; the server only enforces the last-write-wins version and the 64 KiB size limit.

Post-quantum challenge migration

The anti-abuse PQC challenge was migrated from a hand implemented polynomial KEM to the audited ml-kem (RustCrypto) and @noble/post-quantum libraries. The wire fields changed from { tHex, rhoHex } / { uHex, vHex } to { ekHex } / { ctHex }. The server and client were updated together.

Gap report

The following table records where the threat model or the design document describes a mechanism that differs from what is shipped. These are intentional accuracy notes, not an indication that the implementation is silently weaker without review.

AreaDesign or threat model claimShipped implementationImpact
Message encryptionMLS with X-Wing hybrid post-quantum (E8)Room-key AES-256-GCM with ECDSA P-256 signatures (QXDR-A256GCM-HKDFSHA256), no MLS, no X-WingE2EE but not post-quantum and not MLS.
Rate-limiting nullifierZero-knowledge RLN (E5)HMAC-signed anonymous quota tokens and SHA-256 nullifiersAnonymous and replay-resistant, but not zero-knowledge in the SNARK sense.
Privacy Pass redemptionImplemented (S1, E7)Route and nonce store wired, but VOPRF verification is a stub that always failsThe pass deposit gate is currently unusable.
Contextual pseudonymEd25519 derived per room (P3)ECDSA P-256 derived per roomSame domain separation property, different curve.
Inner envelope sealingMLS / X-Wing inner layer (P4)ML-KEM-768 plus AES-256-GCM, no second MLS layerEnvelopes are sealed to the recipient and opaque to the server.
Deposit gatePass, cap, or ghost tokenAnonymous quota nullifier plus one of pass or capOne additional anonymous gate is always required. The ghost gate was removed.

Audit items integrated

Several audit items are present in the implementation:

  • The VDF challenge signature covers expiresAt in addition to the other fields (S2).
  • VDF parameters are length-bounded before parsing into large integers (S3).
  • Nullifier and target-hash comparisons use constant-time checks (S4).
  • Device and contextual signatures use ECDSA P-256 rather than Ed25519 (C3).