phase 9 chat save 2 — transport + privacy boundary (v1.56.81): SSE+POST chat delivery (NOT WebSocket — reuses M3's proven cookie-auth/participant SSE pattern; no new upgrade-auth surface). db: insertChatMessage (one opaque ciphertext blob per recipient), listChatHistory (room + recipient_fingerprint + id>after filter), freeChatHistory. Endpoints (operator+team via requireTeamApi): POST /admin/api/chat/send (participant-gated, sender=session, per-recipient blobs, size caps, kind whitelist), GET /admin/api/chat/history (participant-gated + ONLY blobs addressed to the session's own server-known fingerprint), GET /admin/api/chat/stream (SSE; auth+participant+self-key checked BEFORE the stream opens; loop locks DB per-tick never across sleeps; emit-failure returns). PRIVACY: operator cannot read others' DMs — not a member of their room, and even shared-room reads are filtered to its own fingerprint. Verified against real Postgres: A→B blob is readable by B, NOT by A's own view, NOT by outsider C (participant gate false), server stores ciphertext only; the one live-PG failure is the known pre-existing team-tier test

dev · 16 hours ago · 2026-06-16 · 14.4 MB

session: admin rebuild · agent: claude-code

$ koh steal kepr.uk/kepr@c75890f0a9b3
·
← 1cb094535d9b ca0d030d820f →
⇓ download .face