102 lines
4.2 KiB
Markdown
102 lines
4.2 KiB
Markdown
# 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
|
|
|
|
1. **Milestone 1 — Text Chat** (current): auth, identity keys, 1:1 encrypted messaging, local history, push.
|
|
2. **Milestone 2 — Voice Calls**: libsodium-secured WebRTC signaling via Supabase Realtime.
|
|
3. **Milestone 3 — Video Calls**: same stack, add video tracks + bandwidth handling.
|
|
4. **Milestone 4 — Desktop Polish**: feature parity with mobile, tray, notifications.
|
|
5. **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
|
|
|
|
```bash
|
|
# 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/shared` and 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 `profiles` table.
|
|
- **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
|