Live updates: SSE multiplexer + read watermark + cross-tab sync #10

Open
opened 2026-05-14 23:17:16 +02:00 by arne · 0 comments
Owner

Parent

#2

What to build

Real time. internal/sse opens one SSE-in per logged-in user to their posta-server, refcounted by active browser tabs with a 10s grace on disconnect. chat.posta.no exposes /events to browsers using htmx's sse extension. posta-server SSE events translate to small opaque trigger events; htmx fires hx-gets for the affected fragments (one render path for first-paint and live updates). Read watermark POSTs on thread open; while a thread is open, the backend tracks that peer as "active" for the session and auto-advances the watermark on inbound for that peer before fanning out the trigger.

Acceptance criteria

  • Logged-in browser tabs open GET /events (cookie-authenticated)
  • First subscriber for a user opens the SSE-in to posta-server; subsequent tabs share it
  • Last unsubscribe + 10s grace closes the SSE-in
  • posta-server inbound and outbound-state events fan out as thread-updated trigger (peer URL in data); sidebar gets contact-changed
  • read-watermark-changed event forwards to sidebar for unread re-render
  • resync event from posta-server fans out a "re-render now" trigger to all subscribed tabs
  • Opening a thread POSTs /api/v1/contacts/read with the highest visible rowId
  • While a thread is open in a tab, the session tracks active-peer; inbound for that peer auto-advances watermark before fanning out
  • Two tabs of the same user stay in sync (send in one ↔ update in other within 1s)
  • posta-server restart triggers SSE-in reconnect with backoff + resync cascade
  • Last-Event-ID resumption works after transient disconnects
  • Unit tests in internal/sse: refcount lifecycle, 10s grace period, reconnect with backoff, Last-Event-ID resumption, resync forwarding, concurrent subscribe/unsubscribe races

Blocked by

## Parent #2 ## What to build Real time. `internal/sse` opens one SSE-in per logged-in user to their posta-server, refcounted by active browser tabs with a 10s grace on disconnect. chat.posta.no exposes `/events` to browsers using htmx's `sse` extension. posta-server SSE events translate to small opaque trigger events; htmx fires `hx-get`s for the affected fragments (one render path for first-paint and live updates). Read watermark POSTs on thread open; while a thread is open, the backend tracks that peer as "active" for the session and auto-advances the watermark on inbound for that peer before fanning out the trigger. ## Acceptance criteria - [ ] Logged-in browser tabs open GET `/events` (cookie-authenticated) - [ ] First subscriber for a user opens the SSE-in to posta-server; subsequent tabs share it - [ ] Last unsubscribe + 10s grace closes the SSE-in - [ ] posta-server `inbound` and `outbound-state` events fan out as `thread-updated` trigger (peer URL in data); sidebar gets `contact-changed` - [ ] `read-watermark-changed` event forwards to sidebar for unread re-render - [ ] `resync` event from posta-server fans out a "re-render now" trigger to all subscribed tabs - [ ] Opening a thread POSTs `/api/v1/contacts/read` with the highest visible `rowId` - [ ] While a thread is open in a tab, the session tracks active-peer; inbound for that peer auto-advances watermark before fanning out - [ ] Two tabs of the same user stay in sync (send in one ↔ update in other within 1s) - [ ] posta-server restart triggers SSE-in reconnect with backoff + `resync` cascade - [ ] `Last-Event-ID` resumption works after transient disconnects - [ ] Unit tests in `internal/sse`: refcount lifecycle, 10s grace period, reconnect with backoff, `Last-Event-ID` resumption, `resync` forwarding, concurrent subscribe/unsubscribe races ## Blocked by - #9
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
posta/chat#10
No description provided.