Releases: caaatto/rede
v2.19.14-beta
Crash diagnostics: a global unhandled-exception handler now writes the next crash to ~/.rede/crash.log instead of the app vanishing silently. Helps pin down the native audio-path crash seen during voice calls.
No protocol changes — this build is messaging/voice compatible with v2.19.13-beta clients and the current server.
v2.19.13-beta
Fixes channel-category drag-and-drop: each category header and channel row is now its own drop target, so dropping a channel onto a header files it into that category (drop on empty list space = uncategorized). Adds a right-click 'Create channel here...' on category headers to create a channel directly inside a category.
v2.19.12-beta
Channel categories are now shown in the place sidebar as collapsible headers (empty categories included). Channels can be moved between categories by drag-and-drop (owner/admin only); drop on empty channel-list space to uncategorize. Right-click a category header to delete it.
v2.19.11-beta
Legacy ratchet-state fallback in CallService.
Plain messages have always quietly fallen back to a no-deviceId ratchet state when contact.Devices is empty (legacy contact, USER_LOOKUP returned no devices, etc.) — but calls didn't, so the same contact you could chat with rejected your call with 'No secure session.' Asymmetry only: the other side could still call you, because their per-device state was intact.
v2.19.10-beta
Surface server ERROR messages in the UI instead of dropping them silently. Previously any server-side validation error (e.g. "Target device not found", "Duplicate nonce", rate-limit hits) vanished without trace because no client service registered a handler for the generic error message type.
v2.19.9-beta
Fix
After enough one-time pre-keys are consumed, `UploadPreKeysIfNeeded` generates a new signed pre-key (and PQ signed pre-key) and overwrites `Profile.SignedPreKey` / `Profile.PqSignedPreKey` in place. The X3DH responder loop already tried `Profile.PreviousSignedPreKeys`, but the list was never populated — the previous SPK secret was simply dropped, so any X3DH initial the peer built from the bundle they fetched before our rotation could not be decrypted and surfaced as "X3DH key agreement failed."
The same hole existed for the PQ signed pre-key.
Fixes
UploadPreKeysIfNeededarchives the outgoing classical + PQ signed pre-keys toPreviousSignedPreKeys/PreviousPqSignedPreKeys(new field) before overwriting them, bounded to the last 5 entries.HandleX3dhMessagenow walks every candidate PQ secret as well — current PQ SPK + archived PQ SPKs — the same way it already walked archived classical SPKs. The OTPK × SPK × PQ-secret triple is tried in a 3-deep loop; first plaintext wins, and only that combination's OTPK / PQ-OTPK is consumed.- Adds
PreviousPqSignedPreKeystoProfileso the PQ archive survives restarts.
v2.19.8-beta
Fix
When both peers ran `/resync` and sent simultaneously, each side's SendMessage path created its own initiator ratchet (call them Chain A and Chain B) while the peer's X3DH initial was already in flight. HandleX3dhMessage on each side then derived a responder state for the peer's chain and unconditionally saved it, overwriting its own initiator state. The X3DH initial messages still decrypted, but the two saved states ended up on different chains — every follow-up message landed on the wrong ratchet and silently failed, matching the user report "session established but we can't write to each other."
The fix is a session-collision tiebreaker in HandleX3dhMessage's success branch. If we already have a saved ratchet state for the peer/device when the X3DH initial arrives, both sides converge by ordinal user-id comparison:
self.UserId < from→ keep our initiator state, use the responder state only transiently to decrypt this one message, do not save it. Peer (higher id) adopts our chain.self.UserId > from→ drop our initiator state, save the responder state. Peer (lower id) keeps theirs; we converge on theirs.
First-contact / no-existing-state path is unchanged — the responder state is saved as before.
v2.19.7-beta
Fix
`HandleX3dhMessage` used to remove the matching OTPK from `Profile.OneTimePreKeys` before attempting `X3dh.Respond` + `DoubleRatchet.Decrypt`. Any failure further down (wrong SPK candidate, padding mismatch, replayed initial after a `/resync`) burned the OTPK forever — a later genuine X3DH initial citing the same OTPK then had no secret to pair with and surfaced as "X3DH key agreement failed."
The classical OTPK now follows the same lifecycle the PQ OTPK already used: remember the index, try every (SPK × OTPK) candidate, and only remove from `OneTimePreKeys` once a candidate actually returns plaintext.
Known limitation
This does not address simultaneous X3DH from both sides — i.e. both peers pressing `/resync` and sending at the same time. That case needs a deterministic session-collision tiebreaker and is tracked as a separate change.
v2.19.6-beta
Fix
Adding a contact whose profile carried a multi-KB avatar would fail the first session handshake with:
[Error] Handler error [prekey_bundle]: Message too large (260097 bytes, max 16382).
The profile broadcast was inlined into the first ratcheted message, and DoubleRatchet.Encrypt pads to fixed buckets capped at 16382 bytes — any avatar above ~12 KB base64 blew past that cap and threw, so the X3DH send never went out.
SettingsViewModel.SetAvatarFromBytes now downscales oversized images via Bitmap.CreateScaledBitmap and re-encodes them as PNG at 96 / 64 / 48 / 32 px until they fit under the on-wire cap (12 KB base64 ≈ 9216 raw bytes). Small images are left untouched.
ChatService.BuildProfilePayload now drops an oversized avatarData field instead of returning null — profiles with a legacy oversize avatar still ship their accent color and don't crash the handshake.
v2.19.5-beta
Image attachment polish
- Small images are no longer upscaled into a blurry block — the inline image now scales down only, keeping its natural size.
- The image is decoded off the UI thread, so the chat no longer freezes for a beat when a multi-MB image loads.
- Image attachments show a sized 'Loading image…' placeholder while their blob loads, instead of flashing the grey file-name chip and then jumping. The file chip only appears for non-image files or when an image genuinely fails to load.