Files
OwnCord/docs/client-architecture.md
T
jevb cec6ca6a97 docs: add public documentation for contributors and users
Created 12 public docs derived from internal vault:
- Setup guides: quick-start, server-configuration, livekit-setup, deployment
- Networking: port-forwarding, tailscale
- References: api, protocol, schema, client-architecture
- Community: contributing, security

Updated .gitignore to only exclude docs/brain/ (internal vault),
allowing docs/ to be tracked. Updated README with expanded quick
start, voice/video setup, networking ports, and doc links.
2026-03-30 22:31:06 +02:00

13 KiB

Client Architecture: Tauri v2

Comprehensive architecture reference for the OwnCord Tauri v2 desktop client. Covers project structure, data flow, component system, and subsystems.

Why Tauri v2

Tauri v2 uses the OS webview (WebView2 on Windows) so the install is ~10-15 MB and RAM usage is ~30-50 MB. The HTML/CSS mockups become the actual UI code, with CSS handling hover effects, conditional visibility, theming, and animations.


Project Layout

Client/tauri-client/
├── src-tauri/                          # Rust backend
│   ├── Cargo.toml
│   ├── tauri.conf.json                 # Window size, title, plugins, CSP, updater
│   └── src/
│       ├── main.rs                     # Windows entry point
│       ├── lib.rs                      # Tauri Builder: plugins, commands, state
│       ├── credentials.rs              # Win Credential Manager (DPAPI)
│       ├── commands.rs                 # Settings store, cert fingerprints, DevTools
│       ├── ws_proxy.rs                 # WSS proxy with TOFU cert pinning
│       ├── livekit_proxy.rs            # TCP-to-TLS tunnel for LiveKit signaling
│       ├── ptt.rs                      # Push-to-talk via GetAsyncKeyState
│       ├── tray.rs                     # System tray icon and menu
│       ├── hotkeys.rs                  # Global shortcut registration
│       └── update_commands.rs          # Auto-update check + install
│
├── src/                                # TypeScript frontend
│   ├── index.html                      # Single HTML entry point
│   ├── main.ts                         # Bootstrap, router, service wiring
│   │
│   ├── styles/
│   │   ├── tokens.css                  # CSS custom properties
│   │   ├── base.css                    # Reset, scrollbar, typography
│   │   ├── login.css                   # ConnectPage styles
│   │   ├── app.css                     # MainPage + component styles
│   │   └── theme-neon-glow.css         # Default theme overrides
│   │
│   ├── lib/                            # Core services (no UI, no DOM)
│   │   ├── api.ts                      # REST client (Tauri plugin-http)
│   │   ├── ws.ts                       # WebSocket client (Tauri IPC proxy)
│   │   ├── types.ts                    # Protocol types (WS + REST + permissions)
│   │   ├── store.ts                    # Reactive store factory
│   │   ├── dispatcher.ts              # WS message -> store action router
│   │   ├── router.ts                   # In-memory page router
│   │   ├── livekitSession.ts           # LiveKit voice/video session
│   │   ├── connectionStats.ts          # WebRTC stats poller
│   │   ├── rate-limiter.ts             # Sliding-window rate limiter
│   │   ├── permissions.ts              # Bitfield utilities
│   │   ├── profiles.ts                # Server profile CRUD
│   │   ├── credentials.ts             # Credential storage (Tauri IPC)
│   │   ├── disposable.ts              # Component lifecycle cleanup
│   │   ├── dom.ts                     # XSS-safe DOM helpers
│   │   ├── safe-render.ts             # Error boundary
│   │   ├── logger.ts                  # Structured logger
│   │   ├── notifications.ts           # Desktop notifications
│   │   ├── tenor.ts                   # Tenor GIF API v2
│   │   ├── themes.ts                  # Theme manager
│   │   ├── updater.ts                 # Auto-update
│   │   ├── reconcile.ts               # Keyed DOM list reconciliation
│   │   ├── icons.ts                   # Lucide SVG icon factory
│   │   └── ...
│   │
│   ├── stores/                         # Reactive state stores
│   │   ├── auth.store.ts
│   │   ├── channels.store.ts
│   │   ├── dm.store.ts
│   │   ├── messages.store.ts
│   │   ├── members.store.ts
│   │   ├── voice.store.ts
│   │   └── ui.store.ts
│   │
│   ├── components/                     # UI components
│   │   ├── MessageList.ts, MessageInput.ts, ...
│   │   ├── message-list/              # MessageList sub-modules
│   │   └── settings/                  # Settings tab components
│   │
│   └── pages/
│       ├── ConnectPage.ts              # Login/register page
│       ├── MainPage.ts                # Main app layout
│       └── main-page/                 # MainPage sub-controllers
│           ├── SidebarArea.ts
│           ├── ChatArea.ts
│           ├── ChannelController.ts
│           ├── MessageController.ts
│           └── ...
│
├── tests/
│   ├── unit/                          # Vitest unit tests
│   ├── integration/                   # Vitest with mocked WS
│   └── e2e/                           # Playwright E2E tests
│
├── vite.config.ts
├── tsconfig.json
├── vitest.config.ts
└── playwright.config.ts

Architecture Layers

+===================================================================+
|                         UI Components                             |
|  (HTML + CSS, vanilla TypeScript DOM manipulation)                |
|  Components are factory functions returning { mount, destroy }    |
+===================================================================+
          |                    |                    |
          |  subscribe()      |  actions           |  events
          v                    v                    v
+===================================================================+
|                      Reactive Stores                              |
|  auth | channels | dm | messages | members | voice | ui           |
|  Immutable state. Batched notifications via queueMicrotask.       |
+===================================================================+
          ^                    |
          |  WS events         |  send()
+===================================================================+
|                       Core Services                               |
|  ws.ts       api.ts       dispatcher.ts    rate-limiter.ts        |
|  livekitSession.ts   notifications.ts   ptt.ts   tenor.ts        |
+===================================================================+
          |                    |
          |  invoke()          |  listen()
          v                    v
+===================================================================+
|                     Tauri IPC Bridge                              |
+===================================================================+
          |                    ^
          v                    |
+===================================================================+
|                      Rust Backend                                 |
|  ws_proxy (WSS + TOFU)   livekit_proxy (TCP-to-TLS tunnel)       |
|  credentials (Win32 DPAPI)   ptt (GetAsyncKeyState polling)       |
|  commands (settings store)   tray   hotkeys   update_commands     |
+===================================================================+

Data flows DOWN through layers. Events flow UP via subscriptions. No component directly calls the WebSocket or REST API; they go through stores and controllers.


Rust Backend Modules

ws_proxy.rs -- WebSocket Proxy with TOFU

WebView2 rejects self-signed TLS certificates. All WebSocket traffic routes through Rust. The Rust proxy implements TOFU certificate pinning -- on first connect, the cert fingerprint is stored; on subsequent connects, it is verified.

livekit_proxy.rs -- LiveKit TLS Tunnel

A local TCP listener proxies LiveKit SDK connections through TLS to the remote server, avoiding self-signed cert issues.

credentials.rs -- Windows Credential Manager

Uses Win32 Credential Manager APIs. Credentials are stored as DPAPI-encrypted blobs tied to the Windows user account.

ptt.rs -- Push-to-Talk

Uses GetAsyncKeyState for non-consuming key detection. 20ms polling loop on a background thread.

tray.rs -- System Tray

System tray icon with Show/Hide, Status submenu, and Quit.

update_commands.rs -- Auto-Update

Dynamic server URL updater endpoint. Update artifacts are verified via Ed25519 signature.


Store System

The store factory (createStore) provides getState, setState, subscribe, subscribeSelector, select, and flush. State is always immutable. Notifications are batched via queueMicrotask.

Store Responsibilities

Store Key State WS Events Handled
auth token, user, serverName, motd, isAuthenticated auth_ok, auth_error
channels channels (Map), activeChannelId ready, channel_create/update/delete
dm DM channel list dm_channel_open, dm_channel_close
messages per-channel messages, pending sends, hasMore chat_message, chat_edited, chat_deleted, chat_send_ok, reaction_update
members member Map, typing indicators ready, member_join/leave/update/ban, typing, presence
voice currentChannelId, voice users, local audio state voice_state, voice_leave, voice_config, voice_token
ui sidebar mode, modals, theme, connection status server_restart, error

Messages per channel are capped at 500. Typing indicators auto-clear after 5 seconds.


Component System

Components are factory functions returning { mount, destroy }. mount() appends elements to a container; destroy() removes DOM, unsubscribes listeners, and clears intervals.

DOM List Reconciliation

For efficient list updates (member list, channel list), a keyed reconciliation algorithm reuses existing DOM elements, updates in place, and removes stale elements -- preserving hover states, focus, and scroll position.


Sidebar Architecture

+----------------------------------+
| SERVER HEADER                    |
+----------------------------------+
| DIRECT MESSAGES  (3)  [+]       |
|  Top 3 DMs with unread badges   |
|  View all messages link          |
+----------------------------------+
| TEXT CHANNELS                    |
|  Category-grouped, collapsible   |
+----------------------------------+
| VOICE CHANNELS                   |
|  User avatars in channel         |
+----------------------------------+
| MEMBERS (collapsible)            |
|  Role-grouped, drag-to-resize    |
+----------------------------------+
| VOICE WIDGET                     |
|  Mute/deafen/camera/screen/leave |
+----------------------------------+
| USER BAR                         |
|  Settings + quick-switch buttons |
+----------------------------------+

Two sidebar modes: "channels" (full server view) and "dms" (full DM conversations list).


Chat Area Architecture

The chat area composes: chat header, message list, typing indicator, message input, video grid (overlays when cameras are active), pinned messages panel, and search overlay.

The ChannelController manages mounting/destroying per-channel components when the active channel changes.


Voice and Video (Client Side)

LiveKit Session

The LiveKitSession class manages the full voice/video lifecycle via LiveKit's livekit-client JS SDK.

Stream Quality Presets:

Preset Camera Resolution Camera Bitrate Screen Resolution Screen Bitrate
low 360p 600 Kbps 720p@5fps 1.5 Mbps
medium 720p 1.7 Mbps 1080p@15fps 3 Mbps
high 1080p 4 Mbps 1080p@30fps 6 Mbps
source 1080p 8 Mbps native 10 Mbps

Connection Quality

A 2-second polling interval collects WebRTC stats from both publisher and subscriber PeerConnections. Quality is color-coded: green (<100ms), yellow (100-200ms), red (>200ms).


REST API Client

Uses @tauri-apps/plugin-http fetch (not browser fetch) to bypass self-signed cert rejection. All requests include danger: { acceptInvalidCerts: true } for server URLs only. Third-party fetches use standard cert validation.


Dispatcher

wireDispatcher(ws) attaches listeners to the WsClient, routing each server message type to the appropriate store actions. Key mappings:

  • ready -> sets channels, members, voice states, DM channels
  • chat_message -> adds message, increments unread, triggers notifications
  • voice_token -> starts LiveKit session
  • presence -> updates member status
  • server_restart -> shows warning banner

See protocol.md for complete message type reference.