b44a785d203ebaa0de076a45921916fe1be98261
Rust side:
- New src-tauri/src/screen_sources.rs with an enumerate_screen_sources
command. Uses the xcap crate for cross-platform screen + window
enumeration and capture; PNG thumbnails are letterbox-scaled to fit
320x180 and returned as base64.
- Source ids emit Chromium's internal desktopCapturer format
("screen:<id>:0", "window:<hwnd>:0") so JS can try passing them
straight into chromeMediaSourceId.
- Registered in both invoke_handler branches in lib.rs.
Frontend:
- New lib/screenSources.ts — Tauri command wrapper + thumbnailDataUrl
helper for the picker UI.
- New components/ScreenSourcePicker.tsx — Discord-style grid: sources
grouped under "Bildschirme" / "Fenster", large thumbnail cards with
selection state, quality preset + system-audio toggle in the footer.
"Teilen" button is enabled either way; without a selection it says
"Ohne Auswahl weiter" and falls through to the OS picker.
- Replaces the old form-style ScreenShareDialog entirely (removed).
CallContext wiring:
- startScreenShare now accepts an optional sourceId. When set, it
captures that exact source via getUserMedia's legacy
chromeMediaSourceId constraint and publishes the resulting tracks
manually (video as ScreenShare, audio as ScreenShareAudio). Falls
back to setScreenShareEnabled if WebView2 rejects the constraint,
so users always get a working share even if the direct path fails.
- Track 'ended' listeners unpublish the pub when the OS revokes
capture (close of shared window, OS "stop sharing" banner).
InCallPanel:
- Left-click on the share button now opens the picker instead of
starting with last-saved settings; right-click opens it too. The
picker itself is the 1-click UX.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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%