Add end-to-end encrypted voice messages; release v5.4.5
CodeQL Analysis / Analyze CodeQL (push) Canceled after 0s
Deploy Application / deploy (push) Canceled after 0s
Mirror to Codeberg / mirror (push) Canceled after 0s
Mirror to PrivacyGuides / mirror (push) Canceled after 0s

- Record voice notes in-browser, sent over the chunked AES-GCM file-transfer
  channel (per-file session key + signed SHA-256 integrity).
- Captured as PCM and encoded to WAV for universal playback (incl. iOS/Safari);
  auto-accepted and played inline from an in-memory blob, never written to disk.
- Composer mic button with live waveform + timer; desktop shows mic + send side
  by side, mobile swaps mic to send when typing.
- CSP media-src now allows blob: so recorded/received audio can play.
- Roadmap: Desktop Edition -> 5.0, new 5.5 'Secure Voice & Calls', later
  milestones shifted; version bumped to 5.4.5.
- Update README, docs (security/API/cryptography), and CHANGELOG.
This commit is contained in:
lockbitchat
2026-07-22 00:41:58 -04:00
parent 0e3e3a2974
commit c98a01b1d5
87 changed files with 1678 additions and 164 deletions
+23
View File
@@ -54,6 +54,29 @@ function withTwoSessions() {
assert.equal(state.sessions.b.keyFingerprint, '', 'sibling fingerprint untouched');
}
// Peer presence is cleared when the session leaves the connected state, so a reconnect
// never re-shows the peer's stale status before they re-broadcast it.
{
let state = withTwoSessions();
state = sessionsReducer(state, { type: A.SET_STATUS, id: 'a', status: 'connected' });
state = sessionsReducer(state, { type: A.SET_PEER_PRESENCE, id: 'a', presence: 'busy' });
assert.equal(state.sessions.a.peerPresence, 'busy');
// connected -> verified keeps presence (still connected).
state = sessionsReducer(state, { type: A.SET_STATUS, id: 'a', status: 'verified' });
assert.equal(state.sessions.a.peerPresence, 'busy', 'presence kept while still connected');
// verified -> peer_disconnected clears it.
state = sessionsReducer(state, { type: A.SET_STATUS, id: 'a', status: 'peer_disconnected' });
assert.equal(state.sessions.a.peerPresence, null, 'presence cleared on disconnect');
// Reconnecting does not resurrect the old presence; it stays null until re-broadcast.
state = sessionsReducer(state, { type: A.SET_STATUS, id: 'a', status: 'connected' });
assert.equal(state.sessions.a.peerPresence, null, 'no stale presence after reconnect');
state = sessionsReducer(state, { type: A.SET_PEER_PRESENCE, id: 'a', presence: 'available' });
assert.equal(state.sessions.a.peerPresence, 'available', 'fresh presence applies after reconnect');
}
// UPDATE_MESSAGE_STATUS and DELETE_MESSAGE only touch the named session/message.
{
let state = withTwoSessions();