Multi-thread + DMs: threads, user keypairs, and the full message toolkit (#17)
Turns each lchat instance from one shared group chat into a multi-thread workspace: DMs and groups are one primitive (a thread with N members), the instance is the trust boundary (open roster + admin), and every thread has its own AES key wrapped to each member's keypair (ECIES). The server still only ever sees opaque ciphertext plus key blobs it can't open. - User keypairs (ECDH P-256); per-thread keys wrapped per-member; instance key for all-members content (profile photos), server-blind. - Master-detail UI (desktop) + stacked mobile nav; new-chat / new-group, members, add-people, leave, edit-group, edit-profile. - Message toolkit: reactions, edits (15-min window), replies, typing indicators, time separators + hover times, link previews, attachments, avatars; desktop hover react/reply/edit affordances. - Per-thread push (SW decrypts with the thread key); admin console is roster-only (no group key); script-post API + tool re-keyed to the keypair model.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
22 files changed,
+3843
−2477
README.md+22 −20db.go+349 −16db_test.go+88 −12docs/threads-and-dms.md+130 −0handlers_test.go+61 −40hub.go+69 −28main.go+402 −11push.go+26 −8test/crypto.test.mjs+38 −0test/format.test.mjs+60 −1tools/lchat-post.mjs+45 −51web/pages/admin.html+7 −37web/pages/index.html+72 −36web/static/admin.js+17 −123web/static/chat.js+2074 −1894web/static/crypto.js+52 −0web/static/emoji.js+68 −0web/static/format.js+40 −0web/static/join.js+53 −171web/static/push.js+3 −7web/static/style.css+155 −15web/static/sw.js+12 −7
README.mdLoading diff…
db.goLoading diff…
db_test.goLoading diff…
docs/threads-and-dms.mdLoading diff…
handlers_test.goLoading diff…
hub.goLoading diff…
main.goLoading diff…
push.goLoading diff…
test/crypto.test.mjsLoading diff…
test/format.test.mjsLoading diff…
tools/lchat-post.mjsLoading diff…
web/pages/admin.htmlLoading diff…
web/pages/index.htmlLoading diff…
web/static/admin.jsLoading diff…
web/static/chat.jsLoading diff…
web/static/crypto.jsLoading diff…
web/static/emoji.jsLoading diff…
web/static/format.jsLoading diff…
web/static/join.jsLoading diff…
web/static/push.jsLoading diff…
web/static/style.cssLoading diff…
web/static/sw.jsLoading diff…