From e4bb54405b71c3ec3c27e50372609bfa1b7dc073 Mon Sep 17 00:00:00 2001 From: jevb Date: Mon, 30 Mar 2026 16:47:19 +0200 Subject: [PATCH] docs: regenerate codemaps from current codebase 6 codemaps updated with accurate line counts, routes, WS message types, schema, and test infrastructure from 203 scanned source files. --- docs/CODEMAPS/architecture.md | 80 ++++++++--------- docs/CODEMAPS/backend.md | 165 ++++++++++++++++++++-------------- docs/CODEMAPS/data.md | 136 ++++++++++++++++++++-------- docs/CODEMAPS/frontend.md | 159 ++++++++++++++++++-------------- docs/CODEMAPS/rust-backend.md | 74 +++++++++++++++ docs/CODEMAPS/testing.md | 85 ++++++++++++++++++ 6 files changed, 490 insertions(+), 209 deletions(-) create mode 100644 docs/CODEMAPS/rust-backend.md create mode 100644 docs/CODEMAPS/testing.md diff --git a/docs/CODEMAPS/architecture.md b/docs/CODEMAPS/architecture.md index e683e080..b1442aa7 100644 --- a/docs/CODEMAPS/architecture.md +++ b/docs/CODEMAPS/architecture.md @@ -1,57 +1,55 @@ - + -# OwnCord Architecture +# Architecture Overview -## System Overview +## System Components ``` -+-------------------+ +-------------------+ -| Tauri Client | WSS | Go Server | -| (Rust + TS) |--------->| (chatserver.exe) | -| | HTTPS | | -| livekit-client |---. | LiveKit SDK | -+-------------------+ | +-------------------+ - | | - v v - +-------------------+ - | LiveKit Server | - | (companion proc) | - +-------------------+ +Tauri Client (Rust + TypeScript) + | + |-- WS Proxy (Rust) ----> Go Server :8443 (WS) + |-- HTTP (Tauri plugin) -> Go Server :8443 (REST /api/v1/*) + |-- LK Proxy (Rust) ----> LiveKit :7880 (via /livekit/* reverse proxy) + | + v +Go Server (chatserver.exe) + |-- chi router: REST API + WS upgrade + admin panel + |-- ws.Hub: real-time message dispatch, voice orchestration + |-- SQLite (db.DB): all persistent state + |-- LiveKit: companion process or external, voice/video media + |-- storage/: file uploads on disk + |-- updater/: client auto-update (GitHub releases) ``` ## Data Flow -``` -Client Server Storage ------- ------ ------- -ConnectPage api/auth_handler.go SQLite (WAL) - login/register ─HTTP──> POST /api/v1/auth/* ──> users, sessions - <─token─ +### REST (request/response) +`Client -> Tauri HTTP plugin -> Go chi router -> AuthMiddleware -> handler -> db.DB -> JSON response` -MainPage ws/serve.go - ws.connect() ─WSS──> ServeWS() → Hub.register - dispatcher.ts <─ready─ handlers.go dispatcher - ├─ chat_send ──> messages, attachments - ├─ voice_join ──> voice_states + LiveKit token - └─ presence ──> users.status +### WebSocket (bidirectional) +`Client -> Rust ws_proxy (TLS + TOFU) -> /api/v1/ws -> ws.ServeWS -> ws.Hub` +- Client sends: auth, chat_send, voice_join, etc. (17 client msg types) +- Server broadcasts: chat_message, presence, voice_state, etc. (27 server msg types) +- Reconnection: client sends last_seq, server replays from 1000-event ring buffer -livekitSession.ts ws/livekit.go - Room.connect() ─WebRTC─> GenerateToken(JWT) - <─media─> LiveKit SFU (companion) -``` +### Voice/Video (LiveKit) +`Client -> Rust livekit_proxy -> /livekit/* reverse proxy -> LiveKit server :7880` +- Token issued via WS voice_join flow (24h TTL, 23h refresh) +- Webhook: LiveKit -> POST /api/v1/livekit/webhook -> ghost cleanup ## Key Boundaries -| Boundary | Protocol | Auth | -|----------|----------|------| -| Client ↔ Server REST | HTTPS | Bearer token | -| Client ↔ Server WS | WSS (via Rust proxy) | In-band `auth` message | -| Client ↔ LiveKit | WebRTC (via wss proxy) | JWT access token | -| Server ↔ LiveKit | gRPC/HTTP | API key + secret | -| Server ↔ SQLite | In-process | Single-writer WAL | +| Boundary | Mechanism | +|----------|-----------| +| Auth | Session tokens (SHA-256 hash in DB), 30-day TTL | +| 2FA | TOTP with partial_token (10min), 5-attempt limit | +| Rate limiting | Per-IP token bucket (auth.RateLimiter) | +| DM authz | IsDMParticipant check (not role-based) | +| Admin | IP CIDR restriction (AdminIPRestrict middleware) | +| Uploads | 10 MiB max, MIME allowlist, 1 MiB default body | +| TLS | Rust-side TOFU cert pinning (certs.json store) | ## Entry Points - -- **Server:** `main.go` → config → TLS → DB → migrate → router → HTTP server -- **Client:** `main.ts` → router → ConnectPage (auth) → MainPage (app) +- **Server:** `main.go` -> config -> TLS -> DB -> migrate -> router -> HTTP server +- **Client:** `main.ts` -> router -> ConnectPage (auth) -> MainPage (app) - **LiveKit:** Auto-started by `livekit_process.go` alongside chatserver diff --git a/docs/CODEMAPS/backend.md b/docs/CODEMAPS/backend.md index 32991d8c..978c030d 100644 --- a/docs/CODEMAPS/backend.md +++ b/docs/CODEMAPS/backend.md @@ -1,79 +1,114 @@ - + -# Backend Codemap (Go Server) +# Go Server Codemap -## HTTP Routes +## Source Stats +- 74 source files, ~14,528 lines (excluding tests) +- 58 test files -### Auth (rate-limited) -``` -POST /api/v1/auth/register → handleRegister [3/min] -POST /api/v1/auth/login → handleLogin [5/min] -POST /api/v1/auth/logout → handleLogout [AUTH] -GET /api/v1/auth/me → handleMe [AUTH] -``` +## REST Routes (api/) -### Channels & Messages -``` -GET /api/v1/channels/ → handleListChannels [AUTH] -GET /api/v1/channels/{id}/messages → handleGetMessages [AUTH, paginated] -GET /api/v1/search?q= → handleSearch [AUTH, FTS5] -``` +### Auth -- /api/v1/auth +| Method | Path | Handler | Rate Limit | +|--------|------|---------|------------| +| POST | /register | handleRegister | 3/min | +| POST | /login | handleLogin | 60/min | +| POST | /verify-totp | handleVerifyTOTP | 10/min | +| POST | /logout | handleLogout | auth | +| GET | /me | handleMe | auth | +| DELETE | /account | handleDeleteAccount | 5/min | -### Invites, Uploads -``` -POST /api/v1/invites/ → handleCreateInvite [AUTH, MANAGE_INVITES] -GET /api/v1/invites/ → handleListInvites [AUTH, MANAGE_INVITES] -DELETE /api/v1/invites/{code} → handleRevokeInvite [AUTH, MANAGE_INVITES] -POST /api/v1/uploads → handleUpload [AUTH, max 100MB] -GET /api/v1/uploads/{id} → handleDownload [AUTH] -``` +### TOTP -- /api/v1/users/me/totp +| Method | Path | Handler | Rate Limit | +|--------|------|---------|------------| +| POST | /enable | handleEnableTOTP | 5/min | +| POST | /confirm | handleConfirmTOTP | 5/min | +| DELETE | / | handleDisableTOTP | 5/min | -### WebSocket & LiveKit -``` -GET /api/v1/ws → ServeWS() [upgrade, in-band auth] -POST /api/v1/livekit/webhook → LiveKit webhook [JWT verify] -WS /livekit/* → reverse proxy → :7880 [mixed-content fix] -``` +### Channels -- /api/v1/channels (all auth-required) +| Method | Path | Handler | +|--------|------|---------| +| GET | / | handleListChannels | +| GET | /{id}/messages | handleGetMessages | +| GET | /{id}/pins | handleGetPins | +| POST | /{id}/pins/{msgId} | handleSetPinned(true) | +| DELETE | /{id}/pins/{msgId} | handleSetPinned(false) | +| GET | /api/v1/search | handleSearch (30/min) | -### Admin (/admin, IP-restricted) -``` -GET /admin/stats, /users, /channels, /audit-log, /settings, /backups -POST /admin/channels, /backup, /updates/apply -GET /admin/logs/stream [WebSocket log viewer] -``` +### DMs -- /api/v1/dms (auth) +| Method | Path | Handler | +|--------|------|---------| +| POST | / | handleCreateDM | +| GET | / | handleListDMs | +| DELETE | /{channelId} | handleCloseDM | + +### Invites -- /api/v1/invites (auth + MANAGE_INVITES) +| Method | Path | Handler | +|--------|------|---------| +| POST | / | handleCreateInvite | +| GET | / | handleListInvites | +| DELETE | /{code} | handleRevokeInvite | + +### Other Endpoints +| Method | Path | Handler | Access | +|--------|------|---------|--------| +| GET | /health, /api/v1/health | handleHealth | public | +| GET | /api/v1/info | handleInfo | public | +| GET | /api/v1/metrics | handleMetrics | admin IP | +| GET | /api/v1/diagnostics/connectivity | handleDiagnosticsConnectivity | auth | +| POST | /api/v1/uploads | MountUploadRoutes | auth | +| GET | /api/v1/files/{id} | handleServeFile | auth | +| POST | /api/v1/livekit/webhook | MountWebhookRoute | admin IP | +| GET | /api/v1/livekit/health | handleLiveKitHealth | admin IP | +| Handle | /livekit/* | NewLiveKitProxy | 30/min | +| GET | /api/v1/ws | ws.ServeWS | WS upgrade | ## Middleware Chain -``` -RequestID → Recoverer → requestLogger → SecurityHeaders → MaxBodySize(1MB) - Per-route: AuthMiddleware, RequirePermission(bit), RateLimitMiddleware - Admin: AdminIPRestrict(allowedCIDRs) -``` +`RequestID -> Recoverer -> requestLogger -> SecurityHeaders -> MaxBodySize(1MiB) -> [per-route: Auth, RateLimit, AdminIP]` -## WS Message Handlers (ws/handlers.go) +## WS Message Types (ws/message_types.go) +**Client -> Server (17):** auth, chat_send, chat_edit, chat_delete, reaction_add, reaction_remove, typing_start, channel_focus, presence_update, voice_join, voice_leave, voice_mute, voice_deafen, voice_camera, voice_screenshare, voice_token_refresh, ping -| Type | Handler | Rate | DB | Broadcast | -|------|---------|------|-----|-----------| -| chat_send | handleChatSend | 10/s | CreateMessage | channel | -| chat_edit | handleChatEdit | 10/s | EditMessage | channel | -| chat_delete | handleChatDelete | 10/s | DeleteMessage | channel | -| reaction_add/remove | handleReaction | 5/s | Add/RemoveReaction | channel | -| typing_start | handleTyping | 1/3s | — | channel (excl sender) | -| presence_update | handlePresence | 1/10s | UpdateUserStatus | all | -| voice_join | handleVoiceJoin | — | JoinVoice + GenToken | all | -| voice_leave | handleVoiceLeave | — | LeaveVoice | all | -| voice_mute/deafen | handleVoiceMute/Deafen | — | UpdateVoice* | all | -| voice_camera | handleVoiceCamera | 2/s | UpdateVoiceCamera | all | - -## Key Files +**Server -> Client (27):** auth_ok, auth_error, ready, chat_message, chat_send_ok, chat_edited, chat_deleted, reaction_update, typing, presence, channel_create, channel_update, channel_delete, voice_state, voice_config, voice_token, voice_speakers, voice_leave, member_join, member_leave, member_update, member_ban, server_restart, error, pong, dm_channel_open, dm_channel_close +## Key Source Files | File | Lines | Purpose | |------|-------|---------| -| main.go | 291 | Entry, init, graceful shutdown | -| api/router.go | 198 | Route mounting, Hub + LiveKit init | -| api/middleware.go | 325 | Auth, permissions, rate limit, security headers | -| ws/hub.go | 303 | Client registry, broadcast, settings cache | -| ws/handlers.go | 522 | WS message dispatcher | -| ws/voice_handlers.go | 332 | Voice join/leave/mute/camera | -| ws/livekit.go | 170 | Token generation, room management | -| ws/livekit_process.go | 189 | LiveKit binary lifecycle | -| ws/livekit_webhook.go | 178 | LiveKit event processing | +| api/router.go | 312 | chi router, middleware stack, Hub/LiveKit init | +| api/auth_handler.go | 829 | register, login, 2FA, delete account | +| api/channel_handler.go | 576 | messages, pins, search | +| api/middleware.go | 348 | auth, rate limit, CORS, security headers | +| api/dm_handler.go | 225 | DM CRUD with real-time close broadcast | +| api/invite_handler.go | 173 | invite management | +| api/upload_handler.go | 195 | file upload/serve with MIME validation | +| api/livekit_proxy.go | 198 | reverse proxy for LiveKit signaling | +| ws/hub.go | 499 | client registry, broadcast, heartbeat sweep | +| ws/handlers.go | 185 | message dispatch switch | +| ws/handlers_chat.go | 355 | chat_send, chat_edit, chat_delete | +| ws/handlers_presence.go | 139 | presence_update, channel_focus | +| ws/handlers_reaction.go | 103 | reaction_add, reaction_remove | +| ws/serve.go | 385 | WS upgrade, in-band auth, ready, reconnect replay | +| ws/voice_join.go | 235 | LiveKit token generation, voice state | +| ws/voice_controls.go | 179 | mute, deafen, camera, screenshare | +| ws/voice_leave.go | 87 | voice disconnect cleanup | +| ws/livekit.go | 202 | LiveKit SDK wrapper | +| ws/livekit_process.go | 309 | companion process lifecycle | +| ws/livekit_webhook.go | 195 | participant_joined/left events | +| ws/ringbuffer.go | 78 | 1000-event replay buffer | +| ws/messages.go | 480 | all payload struct definitions | +| config/config.go | 296 | TOML config with env overrides | +| main.go | 336 | startup, TLS, graceful shutdown | + +## DB Layer (db/) +| File | Lines | Tables | +|------|-------|--------| +| auth_queries.go | 434 | users, sessions, login_attempts | +| message_queries.go | 522 | messages, messages_fts, reactions | +| channel_queries.go | 243 | channels, channel_overrides, read_states | +| dm_queries.go | 286 | dm_participants, dm_open_state | +| voice_queries.go | 278 | voice_states | +| admin_queries.go | 374 | audit_log, settings, roles | +| attachment_queries.go | 156 | attachments | +| models.go | 198 | all Go struct definitions | +| migrate.go | 203 | 8 migrations, schema_versions tracking | +| errors.go | 33 | sentinel errors: ErrNotFound, ErrDuplicate, ErrForbidden | diff --git a/docs/CODEMAPS/data.md b/docs/CODEMAPS/data.md index 3a9bc7bb..3a07725e 100644 --- a/docs/CODEMAPS/data.md +++ b/docs/CODEMAPS/data.md @@ -1,52 +1,114 @@ - + -# Data Codemap (SQLite) +# Data Layer Codemap -## Tables +## SQLite Schema (8 migrations) -| Table | PK | Key Columns | Indexes | -|-------|----|----|---------| -| roles | id | name, permissions (bitfield), position, is_default | — | -| users | id | username, password (bcrypt), role_id FK, status, banned, totp_secret | username UNIQUE | -| sessions | id | user_id FK, token, ip_address, expires_at | token UNIQUE | -| channels | id | name, type (text/voice), category, position, voice_max_users | — | -| channel_overrides | id | channel_id FK, role_id FK, allow/deny (bitfields) | (channel_id, role_id) | -| messages | id | channel_id FK, user_id FK, content, reply_to, deleted, pinned | (channel_id, id DESC) | -| messages_fts | rowid | FTS5 virtual table (content, channel_id) | — | -| attachments | id (UUID) | message_id FK, filename, stored_as, mime_type, size | — | -| reactions | id | message_id FK, user_id FK, emoji | (message_id, emoji) UNIQUE w/ user | -| voice_states | user_id | channel_id, muted, deafened, camera, screenshare, joined_at | — | -| invites | id | code UNIQUE, created_by FK, max_uses, use_count, expires_at | — | -| read_states | (user_id, channel_id) | last_message_id, mention_count | — | -| audit_log | id | actor_id, action, target_type, target_id, detail, created_at | (actor_id), (created_at DESC) | -| login_attempts | id | ip_address, username, success, timestamp | (ip_address, timestamp) | -| settings | key | value (JSON text) | — | -| emoji, sounds | id | Custom emoji/soundboard storage | — | +### Core Tables + +**users** -- User accounts +| Column | Type | Notes | +|--------|------|-------| +| id | INTEGER PK | auto-increment | +| username | TEXT UNIQUE | COLLATE NOCASE, 2-32 runes | +| password | TEXT | bcrypt hash | +| avatar | TEXT | nullable, file ID | +| role_id | INTEGER FK | -> roles(id), default 4 (Member) | +| totp_secret | TEXT | nullable, TOTP seed | +| status | TEXT | online/idle/dnd/offline | +| banned, ban_reason, ban_expires | | ban system | + +**roles** -- Permission roles (4 defaults) +| ID | Name | Permissions | Position | +|----|------|-------------|----------| +| 1 | Owner | 0x7FFFFFFF | 100 | +| 2 | Admin | 0x3FFFFFFF | 80 | +| 3 | Moderator | 0x000FFFFF | 60 | +| 4 | Member | 0x00000663 | 40 (default) | + +**channels** -- Text + voice + DM channels +- type: "text" | "voice" | "dm" +- voice fields: voice_max_users, voice_quality, mixing_threshold, voice_max_video + +**channel_overrides** -- Per-role allow/deny bitfields per channel +- UNIQUE(channel_id, role_id) + +### Messaging Tables + +**messages** -- Chat messages +- FK: channel_id -> channels, user_id -> users, reply_to -> messages +- Indexes: (channel_id, id DESC), (user_id) + +**messages_fts** -- FTS5 virtual table for full-text search +- Triggers: messages_ai (insert), messages_ad (delete), messages_au (update) + +**attachments** -- File uploads linked to messages +- id: TEXT PK (UUID), mime_type, size, width/height (images) + +**reactions** -- Emoji reactions +- UNIQUE(message_id, user_id, emoji) + +### Auth Tables + +**sessions** -- Login sessions +- token (SHA-256 hash), device, ip, 30-day TTL +- Indexes: (token), (user_id) + +**login_attempts** -- Brute-force tracking +- Index: (ip_address, timestamp) + +### DM Tables (migration 008) + +**dm_participants** -- DM channel membership +- PK: (channel_id, user_id) +- Index: (user_id) + +**dm_open_state** -- Per-user DM visibility +- PK: (user_id, channel_id) + +### Voice Table (migration 002) + +**voice_states** -- Active voice connections +- PK: user_id, FK: channel_id +- muted, deafened, speaking, joined_at +- Index: (channel_id) + +### Other Tables + +| Table | Purpose | +|-------|---------| +| invites | Invite codes with max_uses, expiry, revocation | +| read_states | Per-user per-channel last_message_id + mention_count | +| audit_log | Admin action log (user_id, action, target, details) | +| settings | Key-value server config (schema_version, require_2fa, etc.) | +| emoji | Custom emoji (shortcode, filename) | +| sounds | Custom sound effects | ## Migration History | # | File | Change | |---|------|--------| -| 001 | initial_schema.sql | All base tables + FTS5 | +| 001 | initial_schema.sql | All base tables, FTS5, triggers, default roles | | 002 | voice_states.sql | voice_states table | | 003a | audit_log.sql | Canonicalize audit columns | | 003b | voice_optimization.sql | camera/screenshare fields, voice channel config | | 004 | fix_member_permissions.sql | Member role perms = 0x663 | | 005 | channel_overrides_index.sql | Composite index for permission lookups | | 006 | member_video_permissions.sql | Add USE_VIDEO + SHARE_SCREEN bits | +| 007 | attachment_dimensions.sql | width/height columns on attachments | +| 008 | dm_tables.sql | dm_participants + dm_open_state tables | -## Query Files (db/) - -| File | Tables | Methods | -|------|--------|---------| -| auth_queries.go | users, sessions, invites | CreateUser, GetUserBy*, BanUser, Session CRUD, Invite CRUD | -| channel_queries.go | channels, channel_overrides | List/Get/Create/Delete Channel, permissions | -| message_queries.go | messages, reactions, read_states | CRUD, Search (FTS5), pagination, reactions | -| voice_queries.go | voice_states | Join/Leave, GetState, Update mute/camera/etc | -| attachment_queries.go | attachments | Create, Link to message, Get by message IDs | -| admin_queries.go | audit_log, settings, users | Stats, audit, settings, backup | - -## DB Config -- Driver: `modernc.org/sqlite` (pure Go, no CGO) -- WAL mode, busy timeout 5s, single-writer -- Foreign keys enforced +## DB Access Layer (db/) +| File | Lines | Operations | +|------|-------|-----------| +| auth_queries.go | 434 | CreateUser, AuthenticateUser, CreateSession, ValidateSession, SetTOTPSecret | +| message_queries.go | 522 | InsertMessage, GetMessages, EditMessage, DeleteMessage, SearchMessages (FTS5) | +| channel_queries.go | 243 | ListChannels, CreateChannel, UpdateChannel, DeleteChannel, GetChannelPermissions | +| dm_queries.go | 286 | GetOrCreateDMChannel, GetUserDMChannels, OpenDM, CloseDM, IsDMParticipant | +| voice_queries.go | 278 | JoinVoice, LeaveVoice, GetVoiceStates, UpdateVoiceState | +| admin_queries.go | 374 | GetStats, ListUsers, BanUser, AuditLog, GetSettings | +| attachment_queries.go | 156 | InsertAttachment, GetAttachment, GetMessageAttachments | +| account.go | 101 | ChangePassword, ChangeAvatar, DeleteAccount | +| errors.go | 33 | Sentinel errors: ErrNotFound, ErrDuplicate, ErrForbidden | +| db.go | 136 | Open, WAL mode, busy timeout, FK enforcement | +| models.go | 198 | All Go struct definitions (User, Message, Channel, VoiceState, etc.) | diff --git a/docs/CODEMAPS/frontend.md b/docs/CODEMAPS/frontend.md index e437fd80..23693161 100644 --- a/docs/CODEMAPS/frontend.md +++ b/docs/CODEMAPS/frontend.md @@ -1,75 +1,102 @@ - + -# Frontend Codemap (Tauri v2 Client) +# TypeScript Frontend Codemap -## Page Flow +## Source Stats +- 110 source files, ~25,451 lines +- Vanilla TypeScript (no framework), imperative DOM + +## Page Tree ``` -main.ts → router("connect") - ConnectPage → login/register → wirePostAuth() → ws.connect() - → dispatcher wires events → "ready" received - → router.navigate("main") - MainPage → compose sidebar + chat + voice + modals - → logout → router.navigate("connect") +main.ts (463 lines) -- entry point, router setup + ConnectPage.ts (290) + connect-page/LoginForm.ts (642) + connect-page/ServerPanel.ts (345) + MainPage.ts (468) + main-page/SidebarArea.ts (919) -- unified 240px sidebar + main-page/SidebarDmSection.ts (151) + main-page/SidebarDmHelpers.ts (138) + main-page/SidebarMemberSection.ts (176) + main-page/ChatArea.ts (142) + main-page/ChatHeader.ts (80) + main-page/ChannelController.ts (298) + main-page/MessageController.ts (132) + main-page/ReactionController.ts (134) + main-page/VoiceCallbacks.ts (140) + main-page/VideoModeController.ts (189) + main-page/OverlayManagers.ts (329) + main-page/MemberPickerModal.ts (105) ``` -## Component Tree & Store Subscriptions -``` -MainPage - ├─ ChannelSidebar ── channels.store, voice.store, auth.store, ui.store - ├─ ChatHeader ────── channels.store - ├─ MessageList ───── messages.store, members.store - ├─ TypingIndicator ─ members.store - ├─ MessageInput ──── messages.store, rate-limiter - ├─ VoiceWidget ───── voice.store, channels.store - ├─ VideoGrid ─────── voice.store (camera-filtered subscription) - ├─ MemberList ────── members.store - ├─ UserBar ───────── auth.store - └─ SettingsOverlay ─ auth.store, ui.store, voice.store -``` +## Stores (stores/) +| File | Lines | Key Exports | +|------|-------|-------------| +| messages.store.ts | 358 | addMessage, editMessage, deleteMessage, updateReaction, confirmSend | +| voice.store.ts | 312 | joinVoiceChannel, leaveVoiceChannel, setVoiceStates, updateVoiceState, setSpeakers | +| channels.store.ts | 216 | setChannels, addChannel, updateChannel, removeChannel, setActiveChannel, incrementUnread | +| ui.store.ts | 199 | setTransientError, UI flags and layout state | +| members.store.ts | 193 | setMembers, addMember, removeMember, updatePresence, setTyping | +| dm.store.ts | 102 | setDmChannels, addDmChannel, removeDmChannel, updateDmLastMessage | +| auth.store.ts | 70 | setAuth, clearAuth | +| roles.store.ts | 29 | role list state | + +## Core Services (lib/) +| File | Lines | Purpose | +|------|-------|---------| +| livekitSession.ts | 1171 | LiveKit facade: connect, disconnect, device switch, token refresh | +| api.ts | 573 | REST client (fetch wrapper, all /api/v1/* calls) | +| ws.ts | 498 | WS client: connect, send, reconnect with last_seq | +| audioPipeline.ts | 398 | AudioWorklet VAD, gain, noise gate | +| dispatcher.ts | 396 | WS message -> store action mapping (all 27 server msg types) | +| profiles.ts | 448 | Server profile CRUD, auto-login, credential storage | +| types.ts | 632 | Shared TypeScript interfaces and type guards | +| media-visibility.ts | 324 | Intersection Observer for lazy media loading | +| connectionStats.ts | 234 | WebRTC stats polling (2s), quality indicator | +| audioElements.ts | 221 | Audio element pool for participant playback | +| noise-suppression.ts | 274 | RNNoise WASM integration | +| icons.ts | 277 | SVG icon library | +| deviceManager.ts | ~200 | Mic/speaker enumeration, hot-swap fallback | +| themes.ts | ~180 | Theme manager: built-in + custom JSON import/export | +| notifications.ts | ~170 | Desktop notifications + taskbar flash | +| ptt.ts | ~150 | Push-to-talk wiring (delegates to Rust PTT) | +| credentials.ts | ~120 | IPC bridge to Rust Windows Credential Manager | +| tenor.ts | ~100 | GIF picker (Tenor API v2) | +| toast.ts | ~80 | Toast notification system | +| store.ts | ~60 | Generic reactive store pattern (subscribe/getState) | +| disposable.ts | ~50 | Resource cleanup pattern | +| rate-limiter.ts | ~50 | Client-side rate limiting | +| router.ts | ~40 | Simple page router | + +## Components (components/) +| File | Lines | Purpose | +|------|-------|---------| +| ChannelSidebar.ts | 836 | Legacy sidebar (superseded by SidebarArea) | +| MessageList.ts | 629 | Virtual scroll message list | +| MessageInput.ts | 517 | Rich input: replies, attachments, GIF/emoji picker | +| VoiceWidget.ts | 386 | Voice controls, timer, connection quality | +| EmojiPicker.ts | 326 | Emoji grid with search | +| SettingsOverlay.ts | 322 | Settings panel shell + tab routing | +| VideoGrid.ts | 292 | Video tile layout | +| VoiceChannel.ts | 243 | Voice channel user list with states | +| SearchOverlay.ts | 229 | Full-text search UI | +| settings/AccountTab.ts | 662 | Account, 2FA, avatar, delete | +| settings/VoiceAudioTab.ts | 447 | Audio device config, noise suppression | +| settings/AdvancedTab.ts | 282 | Cache clear, devtools, diagnostics | +| settings/LogsTab.ts | 281 | Log viewer + export | +| settings/AppearanceTab.ts | 227 | Theme, accent color, compact mode | +| message-list/renderers.ts | 284 | Message DOM rendering | +| message-list/attachments.ts | 387 | File attachment display | +| message-list/embeds.ts | 345 | URL preview embeds | +| message-list/media.ts | 479 | Image/video inline display | ## WS Dispatch Flow (dispatcher.ts) ``` -ws.on("ready") → channels/members/voice bulk load -ws.on("chat_message") → messages.addMessage() + notifications.ts -ws.on("voice_state") → voice.updateVoiceState() -ws.on("voice_token") → livekitSession.handleVoiceToken() -ws.on("voice_leave") → voice.removeVoiceUser() -ws.on("presence") → members.updatePresence() -ws.on("channel_*") → channels.add/update/remove -ws.on("member_*") → members.add/update/remove +ws.on("ready") -> channels/members/voice/dm bulk load +ws.on("chat_message") -> messages.addMessage() + notifications.ts +ws.on("voice_state") -> voice.updateVoiceState() +ws.on("voice_token") -> livekitSession.handleVoiceToken() +ws.on("presence") -> members.updatePresence() +ws.on("channel_*") -> channels.add/update/remove +ws.on("member_*") -> members.add/update/remove +ws.on("dm_channel_*") -> dm.add/remove ``` - -## LiveKit Voice Flow (livekitSession.ts) -``` -handleVoiceToken(token, url, channelId) - → Room.connect(wss://host/livekit, token) - → publishMic (optional RNNoise WASM) - → startSpeakingPoll (100ms, Web Audio AnalyserNode) - → onTrackSubscribed →