Group sends built the recipient list from the full group membership, so
members already known to be unregistered were retried on every send via
the legacy 1:1 fan-out. On large groups this made a single send take
tens of seconds and could time out, leaving the message undelivered.
Filter out recipients whose unregistered timestamp is set before
sending, returning an unregisteredFailure result for each (so callers
and CLI output are unchanged) without the network attempt. The flag is
maintained independently by profile/CDS discovery and is cleared when a
recipient registers again, so skipped recipients are re-included
automatically.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Keep the unidentified socket alive while a JSON-RPC connection is open (including stdio mode) and while a D-Bus object is exported, instead of for the lifetime of the receive loop. The receive loop does not use the unauthenticated socket, so keeping it alive there was semantically wrong.
This also covers --receive-mode=manual, where no receive loop runs butclients still send messages.
The unauthenticated (sealed sender) socket had no keep-alive token
registered, causing SignalWebSocket's DelayedDisconnectThread to tear
down the connection ~10s after each send. Every subsequent group message
then had to re-establish a fresh TLS connection (~6s delay).
The authenticated socket avoids this by registering a "receive" keep-alive
token for the lifetime of the receive loop. Apply the same pattern to the
unauthenticated socket: register the token alongside the authenticated one
and remove it in the same finally block.
This keeps the unidentified connection alive in daemon mode, matching the
behaviour of Signal mobile clients.
Without an initial flush(), the JVM HttpServer buffers all
output until the first flush() in the 15-second keep-alive loop.
Clients with shorter timeouts (e.g. 10 s) abort before receiving
any data.
Add a flush() call directly after creating ServerSentEventSender,
before the wait loop, so the HTTP 200 response and headers reach the
client immediately upon connection.
Adds regression test SseInitialFlushTest that verifies at least one
byte arrives within 2 seconds of connecting to GET /api/v1/events.
When filling or updating a V2 group, profile keys were copied from
DecryptedGroup.members into the local profile store but not from
requestingMembers. Admins who never had a prior session with a user in
the join queue then lacked profile keys and could not decrypt profiles
(e.g. for listContacts).
Also process DecryptedRequestingMember entries the same way as full
members, using DecryptedMember / DecryptedRequestingMember types so the
lib module does not require a direct protobuf dependency.
Made-with: Cursor