byGalax a04ecf7a19 feat: voice messages, offline queue, delivery ticks, volume slider, admin + scaling
- Voice messages: MediaRecorder → encrypted attachment, custom waveform
  player via OfflineAudioContext, 60s limit + live mic-level meter
- Offline message queue: localStorage outbox, exponential backoff retries,
  optimistic pending bubble with retry/discard
- Delivery indicator: message_deliveries table + RLS (reciprocal receipts),
  ✓ / ✓✓ / ✓✓-blue tick states, group-aware (all members must ack)
- Per-participant volume slider in calls via right-click tile menu,
  persisted to localStorage, applied to attached audio elements
- Group call scaling: grid up to 12 tiles with pagination,
  active-speaker auto-promotion in fullscreen
- Push notifications scaffolding: service worker, VAPID subscription
  registration, notify-push edge function skeleton
- Backup recovery code: 24-char base32 code (~120 bits entropy) as
  alternative decrypt path, restore UI with mode toggle
- Admin panel: conversations list, audit log (admin_audit_log table +
  admin_log_action RPC), audit entry on user flag toggle
- Search v2: sender filter, attachment-only toggle, date range
- Reactions pop animation (scale 0.4→1.15→1 on count change)
- Message list windowing (150 default, expand via IntersectionObserver)
- Stub cleanup: removed dead ScreenshareStub from CallParticipantTile

Fixes:
- Focus-triggered flicker: dropped window.focus listeners in three spots,
  throttled visibilitychange/online wake-refreshes to 30s, keep existing
  data visible during background re-syncs (no more spinner on every click)
- Voice attachment audio element collapsed to 0px on peer side — now
  forces 280px min-width on bubble

Migrations (push required):
  20260421000001_message_deliveries.sql
  20260421000002_admin_audit_log.sql

Server TODO:
  VAPID keys + notify-push edge function deploy
2026-04-21 01:14:16 +02:00
2026-04-18 23:11:35 +02:00
2026-04-18 23:11:35 +02:00
2026-04-18 23:11:35 +02:00
2026-04-18 23:11:35 +02:00
2026-04-18 23:11:35 +02:00
2026-04-18 23:11:35 +02:00
2026-04-18 23:11:35 +02:00
2026-04-18 23:11:35 +02:00
2026-04-18 23:11:35 +02:00
2026-04-18 23:11:35 +02:00
2026-04-18 23:11:35 +02:00
2026-04-18 23:11:35 +02:00
2026-04-18 23:11:35 +02:00
2026-04-18 23:11:35 +02:00
2026-04-18 23:11:35 +02:00

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

# 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
S
Description
No description provided
Readme 64 MiB
Languages
TypeScript 76.3%
Makefile 9.5%
JavaScript 4.3%
PLpgSQL 4%
CSS 3.3%
Other 2.6%