Download Den · Open-source, privacy-focused voice and text chat for communities.
Den is a self-hostable chat platform built for gaming groups and online communities. It gives you real-time text messaging, voice channels with screen sharing, direct messages, and server organization — with full control over your data and no vendor lock-in.
- Open source — audit the code, fork it, run your own instance
- Privacy first — voice traffic relayed through your own server so participant IPs stay private; no biometric data, no behavioral tracking
- Self-hostable — entire stack runs on free-tier services for small communities; each service has a clear paid upgrade path
- Desktop native — built with Tauri 2.0 for a lean native app (~5 MB bundle); same codebase targets iOS and Android via Tauri 2
- Servers & channels — organize your community into servers with multiple text channels
- Real-time messaging — instant delivery via Supabase Realtime (WebSocket-backed PostgreSQL subscriptions)
- Voice channels — group voice with mute, deafen, and screen sharing via LiveKit WebRTC
- Direct messages — private one-on-one conversations with file attachments
- File attachments — images and files in chat, stored on Cloudflare R2 with zero egress cost
- Message reactions — emoji reactions on any message
- Reply threads — quote-reply to any message with context preview
- Pinned messages — pin important messages per channel
- User presence — online / idle / do not disturb / offline status
- Roles & permissions — bitfield permission system per server
- Invite links — shareable invite codes with optional expiry and use limits
- Quick switcher —
Ctrl+Kto jump between channels and DMs instantly - Desktop notifications — native OS notifications for mentions and DMs when the window is unfocused
- Server settings — guild icon upload, name editing, role management
| Layer | Technology | Why |
|---|---|---|
| Desktop | Tauri 2.0 + React 18 + TypeScript | 25× lighter than Electron; same codebase targets iOS/Android |
| UI | TailwindCSS v4 | CSS-native @theme tokens, no config file |
| State | Zustand | Flat store, minimal boilerplate |
| Backend | Supabase | DB + Auth + Realtime + Storage in one free-tier platform |
| Voice/Video | LiveKit Cloud (self-hostable SFU) | Open-source WebRTC; swap cloud → self-hosted with zero code changes |
| File storage | Cloudflare R2 | 10 GB/month free, zero egress fees |
| Build | Vite 7 | Fast HMR, Tauri-native |
| Tests | Vitest + React Testing Library | Vite-native, 130+ tests |
| Service | Purpose | Free tier |
|---|---|---|
| Supabase | Database, Auth, Realtime, Storage, Edge Functions | 500 MB DB, 50 K MAU |
| LiveKit Cloud | Voice / video / screen share | 10 K participant-minutes/month |
| Cloudflare R2 | File attachments CDN | 10 GB storage, zero egress |
| Tauri desktop app | Client | Self-distributed |
| Total | $0/month |
Scale path: Supabase Pro ($25/mo) when hitting free limits. LiveKit can be self-hosted on a ~$4/mo VPS at any time with no code changes.
- Node.js 18+
- Rust (for Tauri desktop builds)
- A Supabase project (free tier is fine)
- A LiveKit Cloud project (free tier) — required for voice channels
- A Cloudflare R2 bucket — required for file attachments
git clone https://github.com/Delimiters/den.git
cd den
npm install- Create a new Supabase project at supabase.com
- Open the SQL Editor and run the migrations in order:
supabase/migrations/001_initial_schema.sql
supabase/fixes/001_fix_guild_members_rls.sql
supabase/fixes/002_message_reactions_rls.sql
supabase/fixes/003_avatars_storage_bucket.sql
supabase/fixes/004_guild_invites_table.sql
supabase/fixes/005_rls_hardening.sql
supabase/fixes/006_dm_rls_and_functions.sql
supabase/fixes/006b_dm_tables_and_rls.sql
supabase/fixes/007_attachments_storage.sql
supabase/fixes/009_roles_and_permissions.sql
supabase/fixes/010_voice_sessions.sql
supabase/fixes/011_dm_attachments.sql
supabase/fixes/012_message_replies.sql
supabase/fixes/013_pinned_messages.sql
supabase/fixes/014_voice_sessions_replica_identity.sql
supabase/fixes/015_custom_emojis.sql
- In Storage, create a public bucket named
avatars - In Authentication → URL Configuration, add
http://localhost:1420to Allowed Redirect URLs
cp .env.example .env.localVITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your-anon-keyYour Supabase URL and anon key are in Project Settings → API. The anon key is safe to expose to the client — Row Level Security enforces access at the database layer.
Install the Supabase CLI:
npm install -g supabase
supabase login
supabase link --project-ref your-project-refSet required secrets:
# LiveKit (from cloud.livekit.io → your project)
supabase secrets set LIVEKIT_API_KEY=your-key
supabase secrets set LIVEKIT_API_SECRET=your-secret
supabase secrets set LIVEKIT_URL=wss://your-project.livekit.cloud
# Cloudflare R2 (from Cloudflare dashboard → R2 → Manage API tokens)
supabase secrets set R2_ACCESS_KEY_ID=your-key-id
supabase secrets set R2_SECRET_ACCESS_KEY=your-secret
supabase secrets set R2_BUCKET_NAME=your-bucket-name
supabase secrets set R2_ACCOUNT_ID=your-account-id
supabase secrets set R2_PUBLIC_URL=https://your-r2-public-urlDeploy:
supabase functions deploy upload-url --no-verify-jwt
supabase functions deploy livekit-token --no-verify-jwt
supabase functions deploy link-preview --no-verify-jwtBrowser preview (no Rust needed):
npm run dev
# Open http://localhost:1420Full Tauri desktop app:
. "$HOME/.cargo/env"
npm run tauri devnpm run dev # Vite dev server at http://localhost:1420
npm run tauri dev # Full desktop app with hot reload
npm run build # TypeScript check + Vite production build
npm test # Vitest in watch mode
npm run test:once # Single test run
npm run test:coverage # Coverage report (60% threshold enforced)src/
├── components/
│ ├── auth/ # Sign in / sign up
│ ├── chat/ # Message, MessageList, MessageInput, EmojiPicker
│ ├── layout/ # AppLayout, GuildSidebar, ChannelSidebar, MemberList, DmSidebar
│ └── ui/ # Avatar, StatusIndicator, QuickSwitcher
├── hooks/
│ ├── useAuth.ts # Session management
│ ├── usePresence.ts # Online/offline presence tracking
│ ├── useRealtimeMessages.ts # Message subscription + send
│ ├── useDirectMessages.ts # DM subscription + send
│ └── useVoiceChannel.ts # LiveKit join/leave
├── lib/supabase.ts # Supabase client singleton
├── stores/appStore.ts # Zustand global store
├── types/index.ts # Shared TypeScript interfaces
└── utils/
├── message.ts # formatTimestamp, shouldCompact, date helpers
├── upload.ts # R2 pre-signed upload helper
└── desktopNotification.ts # Web Notifications API wrapper
supabase/
├── migrations/ # Initial schema
├── fixes/ # Incremental migrations (apply in order)
└── functions/
├── upload-url/ # Generates R2 pre-signed upload URLs
└── livekit-token/ # Generates LiveKit room tokens
GitHub Actions automatically deploys Supabase Edge Functions on push to main.
Required repository secrets (Settings → Secrets and variables → Actions → Repository secrets):
| Secret | Value |
|---|---|
SUPABASE_ACCESS_TOKEN |
From supabase.com → Account → Access Tokens |
SUPABASE_PROJECT_REF |
Your project reference ID |
- Servers, channels, real-time text messaging
- User presence (online / idle / DND / offline)
- Direct messages
- File attachments (images + files)
- Emoji reactions
- Message replies with context preview
- Pinned messages
- Roles and permissions
- Voice channels with screen sharing (LiveKit)
- Desktop notifications
- Quick channel switcher (Ctrl+K)
- @mentions and mention highlighting
- Message search
- Markdown rendering (bold, italic, strikethrough, code blocks, lists, blockquotes, headings)
- Link previews / embeds
- Custom per-server emojis
- System tray + minimize to tray
- Auto-update (Tauri updater)
- Screen sharing / game streaming in desktop app (broken in Tauri — needs native screen capture permissions)
- Video chat — camera in voice channels
- Direct calling via DMs (voice + video, no shared server required)
- Master settings menu — microphone/camera device selection, notification controls, app version, manual update check
- Voice channel encryption (E2EE via LiveKit SFrame)
- Text message encryption (E2EE — client-side keys, server stores ciphertext)
- Mobile (iOS / Android via Tauri 2.0)
- Permanent web hosting — the app already runs in any browser; needs a production Vercel deployment with a custom domain and stable env vars
MIT — see LICENSE