48ac9d2922268346c16d358db349ffdcb167e924
Phase B.1 of the native-livekit migration. Ships the command surface and event bridge behind a cargo feature so the default build stays unaffected while the JS-SDK path keeps running in production. Rust side - livekit 0.7 (default tokio runtime, rustls-tls-native-roots) pulled as an optional dependency; tokio also optional under the same feature - Feature `rust-livekit` gates everything — off by default; on via `cargo build --features rust-livekit` - src-tauri/src/livekit_bridge.rs: LivekitState mutex, connect / disconnect / send_data commands, event pump for room_state, participant_joined, participant_left, data_received - Mic / camera / screen share commands stubbed with explicit "not implemented" errors so JS callers fail loudly rather than silently no-op JS side - src/lib/nativeLiveKit.ts exposes a NativeRoom class with the same event / method shape the CallContext will need, plus a VITE_USE_RUST_LIVEKIT flag so the adapter can be swapped once the bridge reaches parity - isRustLivekitAvailable() gates access at both env + runtime layers Build impact - Baseline build unchanged (1s incremental, no new deps pulled) - Feature build initial: ~10min (libwebrtc download + link) - Feature build incremental: ~1s - Binary size with feature: +12-20MB vs baseline Open questions (documented for the next phase) - Video-frame rendering bridge remains an upstream gap; livekit-rust exposes NativeVideoFrame but no stable path to expose that as a MediaStreamTrack inside the WebView - Audio-only rust path is realistic near-term; full-rust needs either upstream video-bridge or a native-overlay render window
chat-app
Private, end-to-end encrypted chat app for a small circle. Self-hosted Supabase backend, clients on iOS, Android, macOS, Windows, and Linux.
Server is zero-knowledge: it only stores ciphertexts and metadata needed to route them.
Milestone Roadmap
- Milestone 1 — Text Chat (current): auth, identity keys, 1:1 encrypted messaging, local history, push.
- Milestone 2 — Voice Calls: libsodium-secured WebRTC signaling via Supabase Realtime.
- Milestone 3 — Video Calls: same stack, add video tracks + bandwidth handling.
- Milestone 4 — Desktop Polish: feature parity with mobile, tray, notifications.
- Milestone 5 — Groups & Channels: Discord-like group channels with shared ratchet keys.
Repository Layout
apps/
mobile/ Expo + React Native (iOS / Android)
desktop/ Tauri v2 + React + Vite + Tailwind (macOS / Windows / Linux)
packages/
shared/ Business logic: Supabase client, libsodium crypto, auth, chat
db-types/ Generated Supabase database types
ui-web/ React web components shared by desktop (not by React Native)
infra/
supabase/ Self-hosting docs, client env, SQL migrations
.github/workflows/ CI + build placeholders
Tech Stack
- Mobile: Expo SDK 52, Expo Router, expo-secure-store, expo-sqlite, expo-notifications, react-native-libsodium.
- Desktop: Tauri v2, React 18, Vite, Tailwind, tauri-plugin-stronghold, tauri-plugin-sql, tauri-plugin-notification, Zustand.
- Backend: Supabase self-hosted (Postgres + GoTrue + PostgREST + Realtime + Storage + Edge Functions) on Hetzner, Caddy reverse proxy.
- Crypto: X25519 identity keys, XChaCha20-Poly1305 envelopes (libsodium / NaCl).
- Auth: email magic-link, invite-only.
Prerequisites
- Node 22+ (see
.nvmrc) - pnpm 9+
- Rust + Cargo (Tauri): https://rustup.rs
- Docker + Docker Compose (for running Supabase locally or on the VPS)
- Platform toolchain per target:
- iOS: Xcode
- Android: Android Studio + SDK
- macOS/Linux/Windows Tauri: see
apps/desktop/README.md
Setup
# 1. Node + pnpm (macOS)
brew install pnpm
corepack enable
# 2. Clone + install
git clone <this repo>
cd chat-app
pnpm install
# 3. Env
cp infra/supabase/.env.example apps/mobile/.env
cp infra/supabase/.env.example apps/desktop/.env
# Fill in SUPABASE_URL and SUPABASE_ANON_KEY from your self-hosted stack.
See infra/supabase/README.md for spinning up the backend.
Common Scripts (run from repo root)
| Script | Purpose |
|---|---|
pnpm dev |
Start every workspace's dev task (Turborepo). |
pnpm build |
Build every workspace. |
pnpm lint |
ESLint across workspaces. |
pnpm typecheck |
TypeScript project-wide type check. |
pnpm test |
Vitest across workspaces. |
pnpm format |
Prettier write. |
pnpm format:check |
Prettier check. |
pnpm mobile:dev |
expo start for the mobile app. |
pnpm mobile:ios |
Native iOS run. |
pnpm mobile:android |
Native Android run. |
pnpm desktop:dev |
tauri dev for the desktop app. |
pnpm desktop:build |
Platform-specific Tauri bundle. |
pnpm db:types |
Regenerate @chat-app/db-types from the running Supabase. |
Architecture Overview
- Shared-first: Anything that can run in both React Native and the Tauri WebView lives in
packages/sharedand is imported via@chat-app/shared/*. Both hosts pass in adapters for platform-only concerns (secure storage, SQLite, libsodium backend). - Zero-knowledge server: Messages are encrypted client-side before insert. The server sees ciphertexts, a conversation id, a sender id, and a timestamp — nothing else.
- Key custody: X25519 private keys live in platform secure stores only (Keychain/Keystore on mobile, Stronghold on desktop). Public keys live in the
profilestable. - Realtime: Supabase Realtime delivers new ciphertext rows to subscribed clients. Push notifications are silent (data-only) — the client decrypts and composes the visible notification locally.
Security Checklist
- RLS enabled on every user-facing table
- Invite-only enforced in SQL (invites table + policy)
- No plaintext in push payloads
- Service role key never shipped to a client
- JWT secret rotated on first boot
- TLS via Caddy at the edge
Description
Languages
TypeScript
76.3%
Makefile
9.5%
JavaScript
4.3%
PLpgSQL
4%
CSS
3.3%
Other
2.6%