Slack Feature Parity — Implementation Plan #9
Labels
No labels
prio_critical
prio_low
type_bug
type_contact
type_issue
type_lead
type_question
type_story
type_task
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lhumina_code/hero_collab#9
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
Hero Collab is a markdown-centric collaboration platform built as a Hero OS service. The backend is ~95% complete (65 RPC methods, 18 DB tables, group-based permissions), but the user-facing experience has critical gaps that prevent production use as a Slack alternative.
Current State
What Works
What's Broken (before this work)
toggleReaction()always calledmessage.reactfirst; serverINSERT OR IGNOREsilently succeeded on duplicates, so unreact never firedcaller_idis client-sent and optional (bypasses all permission checks if missing)rpc_proxydrops identity headers:req.into_body()consumes the request, losing allX-Hero-User/Context/Claimsheaders before forwarding to rpc.sockWhat's Missing (Slack Feature Map)
Implementation Plan (7 Phases)
Phase 0: Critical Bug Fixes (1-2 days)
message.toggle_reactatomic RPCPhase 1: Authentication via hero_proxy (3-5 days)
COLLAB_AUTH_MODEfeature flagX-Hero-Userheader, map to collab user via newexternal_idcolumn, inject ascaller_iduser.meRPC methodusers.listRPC for invitesPhase 2: Chat UI Completion (5-7 days)
Phase 3: Missing Core Features (5-7 days)
Phase 4: Canvases & Documents (5-7 days)
Phase 5: Voice/Video Huddles (7-10 days, optional)
Phase 6: Hardening & Polish (3-4 days)
TECH_SPEC Divergences
The original
TECH_SPEC.mdpredates hero_proxy, hero_collab_app, and the CLI crate:TECH_SPEC should be updated after Phase 2. Until then,
plan/slack-feature-parity.mdsupersedes it.Post-Implementation Updates
Each phase requires updating:
openrpc.json(SDK auto-regenerates), CLI subcommands, Dioxus app types, and documentation. External repos: hero_proxy (configure roles/claims after Phase 1), hero_os (verify island after Phase 6).Phase 0 Progress — Complete ✅ (not pushed yet)
Changes are on local
developmentbranch, pending push:b541818fix: add atomic reaction toggle and fix reaction bugbc9a843feat: compose-with-files attachment flow + DB migrations1bebdeechore: update OpenRPC spec for Phase 0 changesWhat was done:
Reaction toggle fix:
message.toggle_reactRPC — atomic server-side method that checks existence and does INSERT or DELETE, returns{action: "added"|"removed"}toggleReaction()in bothchat-app.js(Dioxus island) andchat.html(standalone) to use the new methodAttachment compose-with-files flow:
attachments.message_idmade nullable via safe table recreation migration (PRAGMA check pattern)attachments/{workspace_id}/{attachment_id}/{filename}attachment.uploadaccepts optionalmessage_id+workspace_id, validates 25MB file size limitmessage.sendaccepts optionalattachment_idsarray, associates pending attachments (syncs both DB column and data blob)message.get/message.listnow returnattachments[]array (same JOIN pattern as reactions)external_idcolumn to users table (for Phase 1 hero_proxy mapping)OpenRPC spec updated:
message.toggle_reactmethodattachment.upload(message_id optional, +workspace_id)message.send(+attachment_ids)Blocker for Phase 1+
WebSocket real-time sync is broken when accessed via hero_router. The
proxy_to_socketfunction in hero_router:resp.into_body().collect()) — kills streamingUpgraderesponse header (line 556 in routes.rs) — kills WebSocket handshakeThis affects ALL Hero services with WebSocket, not just hero_collab. hero_whiteboard has the identical pattern (
/ws/{board_id}on ui.sock) and would have the same issue.The WebSocket architecture (UI socket serves WS relay, browser connects via hero_router) is the standard Hero pattern — hero_collab's TECH_SPEC explicitly says it was "learned from hero_whiteboard's proven dual-channel pattern." The issue is in hero_router's proxy layer.
See hero_router issue for proposed fix.
WebSocket Blocker Resolved ✅
The WebSocket issue was not a hero_router bug. hero_router already has WebSocket passthrough support (commit
d1632cd— "feat: add WebSocket tunnel support to hero_router", April 13). The locally installed binary was simply built before that commit.After rebuilding hero_router from latest
developmentbranch, WebSocket works correctly:The hero_router issue (#35) has been closed. WebSocket real-time sync is no longer a blocker for Phase 1 and Phase 2.
Note: hero_collab's
chat.htmlhad a missing base-path prefix in the WebSocket URL (was/ws/{id}, fixed to{basePath}/ws/{id}). This has been fixed locally (not pushed yet).Phase 0 pushed to development ✅
4 commits pushed:
b541818fix: add atomic reaction toggle and fix reaction bugbc9a843feat: compose-with-files attachment flow + DB migrations1bebdeechore: update OpenRPC spec for Phase 0 changesb77429dfix: WebSocket base-path prefix and dual-codebase syncWorking: Reaction toggle, attachment upload/download, OpenRPC spec (65 methods).
Known issue: WebSocket connects (101 Switching Protocols via curl) but the browser sees the connection close immediately ("Finished" status, empty response). The WS tunnel in hero_router (
proxy_ws_tunnel) may be dropping the connection after handshake. RPC-based features work fine — real-time sync via WebSocket needs further debugging. This does not block Phase 1 (auth) or Phase 2 UI work.Proceeding to Phase 1 (authentication via hero_proxy).
Phase 1 (Auth) — Core Complete, pushed to development
Commits:
9077493feat: Phase 1 auth — hero_proxy header integration, user.me, rpc_proxy forwarding395cb70feat: Phase 1E+1F — user_id validation, channel member permissions, auth-aware UIWhat was done:
Auth header reading (1A):
http_rpc()readsX-Hero-User,X-Hero-Context,X-Hero-Claims.handle_rpc()looks up user byexternal_id/email/alias, injects ascaller_id. Verified e2e: hero_proxy → hero_router → hero_collab_server.Feature flag (1-PRE-2):
COLLAB_AUTH_MODE=proxy|devenv var. Defaultdev.RPC proxy header forwarding (1B):
rpc_proxy()extracts identity headers BEFORE consuming request body, forwards them to rpc.sock. Previously all headers were silently dropped.user.me RPC (1D): Returns authenticated user or auto-creates from
X-Hero-User. Returns{authenticated: false}in dev mode.user_id validation (1E): 12 self-operation handlers use
resolve_self_user_id()— blocks impersonation when authenticated, backward compatible in dev mode. Channel member add/remove got new permission checks (was zero before).Chat UI auth (1F):
init()callsuser.mefirst. If authenticated, skips user picker. Falls back to dev mode picker otherwise.Remaining Phase 1 items (deferred):
Next: Phase 2 (Chat UI Completion)
Post-Phase 1 Fixes — Pushed to development
Commits since last update:
eb93d62fix: switch hero_collab_ui to axum::serve() for WebSocket supporta513aedfix: consolidate dual JS codebase, image previews, message ordering, soft-delete filtering3c9645ffix: include attachments in message.send response for immediate preview5dc9417fix: export missing global functions (selectChannel, startDm, pickUser, etc.)WebSocket Fixed
Root cause: hero_collab_ui used manual
hyper::server::conn::http1::Builderwhich didn't properly propagate WebSocket upgrades through hero_router's tunnel. Switched toaxum::serve()(same as hero_proc_ui which has working WebSocket). Connection now stays open — "Connected" status in sidebar.Dual Codebase Consolidated
chat.htmlchat.htmlis now a pure HTML template that loadschat-app.jsvia<script src>window.*for HTML handlersuser.mecall) added tochat-app.js(was only in the deleted inline copy)Bug Fixes
message.listresults reversed from DESC to chronological order. Messages now appear in same order before and after refresh.message.listresults (was showing deleted messages after refresh).message.sendnow includesattachments[]in response so image previews appear immediately, not just after refresh.Current state
All Phase 0 + Phase 1 core features working:
Ready for Phase 2 (Chat UI Completion): unread counts, typing indicators, presence, inline editing.
Comprehensive Review Complete — Fixes Pushed
Ran a full code review of all Phase 0+1 work. Found 12 issues, fixed the critical ones:
Fixed (pushed):
cb781ef—user.merace condition: concurrent calls for new user could create duplicates. Fixed with existence check inside lock + INSERT OR IGNORE.cb781ef— Send button stayed disabled when files attached but no text typed. Fixed button logic to checkstate.pendingAttachments.length.17059e4— DM functions (pickDmUser,dmSearchKey,filterDmUsers) not exported towindowafter IIFE consolidation. Caused ReferenceError when clicking DM autocomplete entries.Accepted (not blocking):
COLLAB_AUTH_MODE=proxy)Review verified working:
Starting Phase 2 (Chat UI Completion).
Phase 2 Complete + Security Fixes — Pushed
Commits since Phase 1 update:
Phase 2 features:
e76b5cafeat: Phase 2A-D — unread counts, typing indicators, presence, WebSocket events1c5f7f6fix: Phase 2 review — WS broadcast, sendBeacon, typing cleanup, heartbeat leakReview fixes:
edd697ffix: gate delete button to message owner, filter deleted messages in get17059e4fix: export missing DM functions (pickDmUser, dmSearchKey, filterDmUsers)cb781effix: review fixes — user.me race condition, send button with attachmentsSecurity fixes:
9897044fix: path traversal protection in storage, channel membership check on attachments88ec970feat: WebSocket auth — extract X-Hero-User on WS upgrade (Phase 1C)b58cfe0docs: add known deferred issues section to planPhase 2 features working:
Security hardening done:
Ecosystem research on WebSocket auth:
Checked hero_whiteboard and hero_proc — neither checks auth on WebSocket connections. The Hero pattern is: WebSocket relays are unauthenticated broadcast pipes, auth happens at hero_proxy edge. Our implementation (extracting X-Hero-User + logging) is ahead of the ecosystem standard.
Remaining deferred (Phase 6):
Ready for Phase 3 (search, notifications, pins, channel browse).
Deferred Items Resolved — Only 3 remain
Previously deferred 5 items from Phase 1+2 reviews. Resolved 2 more:
Now complete:
ws_handlerextractsX-Hero-User, callsuser.me+channel.member.listvia rpc.sock to verify channel membership. Non-members get 403 Forbidden. Dev mode (no auth header) still allows connections for backward compat. Goes beyond hero_whiteboard and hero_proc which have zero WS auth. (88ec970,43dd7f9)LocalBackend.full_path()now canonicalizes paths and rejects traversal attempts. (9897044)attachment.get/deletecheck channel membership viacheck_attachment_access(). (9897044)edd697f)edd697f)Still deferred (Phase 6):
uploaded_bycolumn + schema change)Starting Phase 3 (search, notifications, pins, channel browse).
Phase 3 Complete — Pushed to development
Initial implementation:
9f774befeat: Phase 3 — search, notifications, pins, channel browseProduction hardening (13 issues fixed from code review):
b6e3948fix: Phase 3 production hardening — 13 issues from code reviewOpenRPC spec updated:
33e00b6chore: update OpenRPC spec — 70 methods (was 65)Phase 3 features:
3A. Full-text search:
3B. Mention notifications:
3C. Pinned messages:
3D. Channel browse/discovery:
Review hardening (13 fixes):
FTS sync on edit/delete, FTS query sanitization, FTS backfill, notification interval leak, sender_name in mentions, mention ID from PK not blob, list_pinned SQL filter, pin permission check, search access control, mention LIMIT, json_extract for name lookup, ID type safety
OpenRPC spec: 70 methods. Ready for Phase 4 (Canvases & Documents).
UX Polish Round — Pushed
Multiple UX fixes after hands-on testing:
e0dc5fcPin button in hover bar, editor 404 fix, thread composer buttonse2ce56dThread hover actions, emoji/attachment context-awareness56d2b73Thread-aware emoji, attachments, pin, delete, reactions979c3c3Soft-delete in thread.replies, pending attachment targeting613cd88Proper separation of main and thread composer state (no workaround)70bbd11Emoji picker positioned near triggering button5fd5d68Thread reactions, inline editing, scroll to new messaged318235Emoji picker viewport-aware positioningKey improvements:
OpenRPC: 70 methods. Starting Phase 4 (Canvases).