- db: cleanup_old_messages(days) purges messages older than N days in
batches; recipients/reads/deliveries/reactions follow via ON DELETE
CASCADE. Returns attachment file_ids no longer referenced by any
surviving message (forwarded copies keep their files) and removes
their image_uploads rows
- server: MESSAGE_RETENTION_DAYS env var (default 0 = keep forever);
hourly cleanup deletes expired messages and securely removes orphaned
attachment blobs from the upload dir
- schema: email_visible now defaults to 0 — previously any logged-in
user who knew a UUID could read another user's email via get_profile
- migrations: SQL script to apply the new default and reset the flag on
existing databases (run manually, see file header)
- docker-compose: document MESSAGE_RETENTION_DAYS
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- chat_core: defer one-time-prekey deletion until the first message
decrypts successfully; deleting it on load made the SPK grace-period
retry derive a wrong shared secret and lose the message permanently
- chat_core: fix get_deleted_since params (since -> since_ts) and
response field (message_ids -> deleted_ids) so incremental deletion
sync actually works
- chat_core: route keys_updated pushes into the notification queue
- server: notify contacts with keys_updated when a user uploads a new
SPK or logs in with a new device, so clients invalidate cached key
bundles instead of waiting for the TTL
- server: rate-limit download_stream like other heavy handlers
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>