Commit Graph

4 Commits

Author SHA1 Message Date
Julius Joska 8b452a5593 fix(relay): message loss on large queues, call wake-up throttling, identity reclaim, prekey upload
- stop counting ACK and Pong frames toward the per-connection frame window;
  draining a large queue used to trip the 429 limit, disconnect the client
  and, after eight rounds, drop the remaining envelopes
- deliver to online recipients with try_send: a recipient that stops reading
  its socket no longer pins the sender's task and memory
- throttle call wake-ups per recipient (1 per 10 s, 6 per 10 min) and
  ordinary wake-ups (1 per 5 s); collapse ids for APNs and FCM
- POST /v1/register accepts X-Sepot-Identity-Proof and lets a new device take
  over an existing identity (restore from the recovery phrase)
- POST /v1/keys uploads one-time prekeys and rotates signed/Kyber prekeys;
  PrekeysLow is sent after AUTH when fewer than 20 remain
- GET /v1/keys hands out at most 30 one-time prekeys per address per hour
- reqwest errors are logged without the URL; UnifiedPush POSTs pin the
  addresses the SSRF guard resolved; NAT64/6to4 embedded addresses are checked
- absolute AUTH deadline, at most 3 AUTH attempts, length limits on push
  endpoints and VoIP tokens
- TURN credentials live 12 h and are reissued per connection every 10 min
- global attachment storage cap (SEPOT_ATTACHMENTS_TOTAL_BYTES), nosniff and
  attachment disposition on downloads
- SEPOT_MAX_CONNS_PER_IP, /v1/ready backed by the database, graceful shutdown,
  RLIMIT_NOFILE raised to the hard limit, server-info reports source and revision
- Dockerfile: rust 1.99, GIT_SHA build arg for the image label and
  SEPOT_REVISION; Tor image on alpine 3.24
2026-10-09 14:02:33 +02:00
Julius Joska 69687d0acf feat(relay): ephemeral delivery, call wake-ups and TURN credentials
Protocol and relay groundwork for typing indicators and 1:1 calls.

- ContentHint.EPHEMERAL: delivered only to an online recipient, never
  queued or pushed; the sender gets Receipt.STATUS_DROPPED otherwise.
- ContentHint.CALL: a call offer is queued like a normal message, but an
  offline recipient is woken immediately — a PushKit VoIP push on iOS
  (priority 10, 45 s expiry) or a short-lived FCM data message on
  Android. The relay learns that a device is being called, not by whom.
- RegisterPush.voip_token stores the iOS VoIP token next to the regular
  APNs token (migration 0004).
- TurnRequest/TurnCredentials: logged-in devices receive time-limited
  coturn credentials (use-auth-secret REST scheme, HMAC-SHA1), so the
  TURN server is never an open relay. Configured with SEPOT_TURN_URLS and
  SEPOT_TURN_SECRET or SEPOT_TURN_SECRET_FILE; without them calls fall
  back to direct connections and STUN.
2026-10-03 22:43:53 +02:00
Julius Joska b82f50f888 fix(relay): keep-alive on WebSocket, cap delivery attempts, honest receipts
Messages arrived late or not at all, for two independent reasons.

Dead connections: neither side sent a ping, and after AUTH the server
waited for a frame without a timeout. Mobile NAT drops idle TCP silently,
so the client wrote into a void while the relay still held the device in
its online map — and therefore never sent a push wake-up either. The ping
is a protocol-level one rather than v1::Ping, because dart:io answers
those on its own; the fix works with clients already in the wild.

Poisoned queue: a client decrypts an envelope (ratchet advances), stores
it and acknowledges it. If that ACK is lost, a second decryption attempt
can only fail, so the envelope is never acknowledged — and was redelivered
on every connect until its 30-day TTL, without ever reaching the recipient.
attempt_count existed from the first migration but was never counted; it
now bounds redelivery.

Receipts reported DELIVERED_ONLINE whenever a write into the bounded mpsc
channel succeeded, which it does even for a long-dead socket. Online is now
claimed only for devices that recently sent something.

Push send failures were logged at debug while the relay runs at info, so a
rejected wake-up looked exactly like a working one.
2026-08-13 12:23:17 +02:00
Julius Joska 8081d98c67 Šepot — end-to-end encrypted messenger
Initial public release. Anonymous on-device identity (no phone number,
e-mail, or password), Signal-protocol cryptography (X3DH + PQXDH, Double
Ratchet, sealed sender) via libsignal, a metadata-minimizing relay,
group chats, disappearing messages, encrypted local storage, optional
Tor transport, and a UI localized into Czech, English, German, Russian,
and Italian.

Monorepo: Flutter client (app/), Rust relay + wire types (rust/),
protobuf definitions (proto/), self-host deploy (deploy/).

Licensed under AGPL-3.0 with an app-store distribution exception (§7).
2026-07-11 17:21:47 +02:00