Real-time voice rooms for web, desktop & Android.
One Next.js client · Tauri native shells · LiveKit media · Self-hostable
Open Chatovo · Report a bug · Request a feature
Chatovo is a Discord-inspired voice messenger built around rooms — not endless channel lists. Spin up a room, share the link, start talking. Public, private, or password-protected.
|
Rooms first Each room is its own space. No noisy server hierarchies — just the people you invited. |
One UI everywhere The same experience in the browser, on Windows/macOS/Linux, and on Android. |
Yours to host Docker Compose, Caddy, LiveKit SFU, PostgreSQL — run it on your own VPS. |
|
Voice & video — low-latency WebRTC via LiveKit SFU Room chat — messages, markdown, file attachments Private rooms — optional password on join Auth — email + password (better-auth) |
Desktop app — tray, global shortcuts, PTT, screen share, auto-update Android — Tauri 2 APK (manual install) i18n — English & Russian ( Open API — NestJS + OpenAPI / Swagger UI |
flowchart LR
subgraph clients["Clients"]
WEB["Web · Next.js"]
DESK["Desktop · Tauri"]
AND["Android · Tauri"]
end
subgraph backend["Backend"]
API["NestJS API · Bun"]
DB[("PostgreSQL")]
LK["LiveKit SFU"]
end
WEB --> API
DESK --> API
AND --> API
API --> DB
clients --> LK
API --> LK
Frontend follows Feature-Sliced Design — the pages/ layer is named views/ to avoid clashing with the Next.js router.
| Layer | Stack |
|---|---|
| Client | React 19 · SCSS modules · Base UI · Motion · TanStack Query · React Hook Form · Zod |
| Native | Tauri 2 · Rust · deep-link · updater · global-shortcut |
| Server | NestJS on Bun · Prisma · better-auth · React Email |
| Shared | @chatovo/schemas — Zod types for client & server |
| Tooling | ESLint · Prettier · Stylelint · TypeScript · React Compiler · Bun workspaces |
chatovo/
├── apps/
│ ├── client/ Next.js · FSD (app / views / widgets / features / entities / shared)
│ ├── server/ NestJS API · Prisma · modules/
│ └── tauri/ Rust shell · desktop + Android
├── packages/schemas/ Shared Zod schemas
├── infra/ Caddy · LiveKit configs
├── docs/ Architecture & style guides
├── docker-compose.yml
└── docker-compose.dev.yml
| Tool | Required for |
|---|---|
| Bun ≥ 1.1 | Everything |
| Docker + Compose | Local LiveKit / Postgres |
| Rust + SDKs | Tauri builds only → docs |
git clone https://github.com/zilero232/chatovo.git
cd chatovo
bun installOne .env at the repo root serves both the client and the server; the client
reads its NEXT_PUBLIC_* out of it via loadRootEnv() in next.config.ts.
cp .env.example .env| Variable | Purpose |
|---|---|
BETTER_AUTH_SECRET |
Auth signing |
DATABASE_URL / DIRECT_URL |
Postgres (DIRECT_URL is required for Prisma) |
LIVEKIT_* |
SFU credentials (server) |
NEXT_PUBLIC_API_URL |
Client → API |
NEXT_PUBLIC_LIVEKIT_URL |
Client → media |
CORS_ORIGINS |
Allowed web origins |
bun --filter @chatovo/server db:pushAll-in-one (recommended)
bun dev:fullOpens LiveKit + Caddy in Docker, then client (:3000) and server (:4000).
Browse https://chatovo.localhost — accept the local certificate once.
Split terminals
bun dev:livekit # terminal 1
bun dev # terminal 2bun tauri:dev # desktop dev
bun tauri:build # desktop release
bun android:init # first-time Android setup
bun android:dev # Android on device / emulator
bun android:build # APK + AAB
bun tauri:icon # icons from apps/client/app/icon.svgVersion lives in root
package.jsonand must be bumped in every workspace manifest,apps/tauri/tauri.conf.jsonandapps/tauri/Cargo.tomlto match — thereleaseworkflow refuses to build when they disagree with each other or with the pushed tag.
bun verify # typecheck + lint + format:check + lint:css — run before committing
bun fix # lint:fix + format + lint:css:fix
bun lint:rust # clippy on apps/tauriBoth run in GitHub Actions; there are no local release scripts.
| Workflow | Trigger | Result |
|---|---|---|
deploy.yml |
Actions → deploy → Run workflow | Builds the web + server images, pushes to GHCR, restarts the VPS stack |
release.yml |
Push a v* tag |
Windows, macOS (arm64 + x64), Linux and Android bundles → GitHub Release |
Cutting a release — bump the version in every manifest (root, the three apps,
packages/schemas, apps/tauri/tauri.conf.json, apps/tauri/Cargo.toml), then:
git tag v1.2.8 && git push --tagsThe checks job refuses to build if any of them disagree with the tag.
Deploying by hand on the VPS instead:
docker compose pull && docker compose up -d| Item | |
|---|---|
| ✅ | Voice & video rooms |
| ✅ | In-room text chat |
| ✅ | Web · desktop · Android |
| ✅ | Tray · shortcuts · PTT · screen share · auto-update |
| ⬜ | iOS client |
| ⬜ | Google Play listing |
Bug reports and PRs are welcome — the licence allows forking for exactly that. For larger changes, open an issue first.
bun run fix # eslint --fix + prettier + stylelint --fix
bun run verify # typecheck + lint + format + stylelint — must pass
bun run test # vitest across all workspacesCommit messages follow Conventional Commits
and are checked by commitlint on commit. Tests live in _tests/ next to the
code they cover; conventions are in .claude/rules/testing.md.
Source-available — all rights reserved. The source is published so you can read it and contribute to it: forking to open a pull request is welcome. Redistributing it, publishing a fork as its own project, or reusing it in another product needs written permission. See LICENSE.
Built by Alexandr Artemev