Local development
Set up the monorepo, database, API, portal, admin console, Expo clients, and test suite.
Prerequisites
- Bun 1.3.14 or newer
- Node.js 24 or newer
- Docker with Compose
- A Twitch application for Twitch login
- A Kick application when testing Kick login, chat, or metadata
- A Google OAuth web client with YouTube Data API v3 enabled when testing YouTube Direct
- Xcode or Android Studio when working on either native Expo app
Install every workspace dependency from the repository root:
bun installConfigure the environment
The local launcher creates and validates the server, web, and OBS Remote env files. Copy the broadcaster example manually because its address depends on your device:
cp apps/native/.env.example apps/native/.env.localThe launcher generates missing local secrets and reports blank Twitch, Kick, or Google credentials. Those provider flows require real credentials; unrelated local features still start.
AI_GATEWAY_API_KEY enables Seppo through Vercel AI Gateway. Optional
ELEVENLABS_API_KEY enables account-gated hosted captions and audio isolation;
add ELEVENLABS_VOICE_ID for hosted chat text-to-speech. Without those optional
values, the native app uses on-device fallbacks or hides the hosted option.
Register the callbacks for the providers you exercise locally:
Twitch: https://api.visp.localhost/api/auth/callback/twitch
Kick: https://api.visp.localhost/api/auth/oauth2/callback/kick
Google: http://localhost:3000/api/auth/google-local-callbackFor Google, create a Web application OAuth client. Add
http://localhost:3000 as the authorized origin, add the Google callback above
as the authorized redirect URI, and store its
client ID and secret as GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET in the
server environment. Enable YouTube Data API v3 and add the
youtube.force-ssl scope to the consent screen. See the repository
DEVELOPMENT.md for the complete console and verification walkthrough.
Relay and S3 values point to the local Compose services. Never commit .env
files.
Start the local stack
One command starts PostgreSQL 18, MinIO, two MediaMTX relays, the relay gateway, API, portal, admin console, OBS Remote web app, and docs, then applies migrations:
bun run dev:localRun bun run dev:local:down to stop the containers without deleting data.
To exercise multi-relay assignment, register the second Compose relay in Admin
with host 127.0.0.2, Control API http://127.0.0.2:9997, and ping URL
http://relay2.visp.localhost:8082/ping.
When changing packages/db/src/schema, generate and inspect a migration before
applying it:
bun run db:generate
bun run db:migrateUse bun run db:push only with a disposable development database. Open Drizzle
Studio with bun run db:studio.
Run the project
For normal API and portal work:
| Process | Command | Address |
|---|---|---|
| Complete local stack | bun run dev:local | https://visp.localhost |
| API | Managed by the launcher | https://api.visp.localhost |
| Admin console | Managed by the launcher | https://admin.visp.localhost |
| OBS Remote web | Managed by the launcher | http://localhost:8083 |
| Relay | Managed by the launcher | https://relay.visp.localhost |
| MinIO console | Managed by the launcher | https://minio.visp.localhost |
| PostgreSQL | Managed by the launcher | 127.0.0.1:54320 |
| Documentation | bun run --cwd apps/fumadocs dev | https://docs.visp.localhost |
| Expo dev server | bun run --cwd apps/native dev | Shown by Expo |
bun run dev starts every workspace through Turborepo. Use the focused commands
unless you need the API, portal, admin console, Expo, and documentation site
simultaneously.
OBS Remote
Run only the dedicated control surface with bun run dev:obs-remote. Web uses
port 8083. For a physical phone, set EXPO_PUBLIC_SERVER_URL in
apps/obs-remote/.env.local to http://LAN_IP:3000, then run its ios or
android script. Browser authentication requires OBS_REMOTE_WEB_ORIGIN to
exactly match the served origin.
Native app
Expo Go cannot load the local SRT module. Build a development client instead:
bun run --cwd apps/native ios
bun run --cwd apps/native androidUse a physical device for real camera, microphone, and network testing. A phone
cannot reach the API through 127.0.0.1; set EXPO_PUBLIC_SERVER_URL to an
address reachable from the phone.
Documentation
Pages live in apps/fumadocs/content/docs. Add an MDX file and include its slug
in meta.json to place it in the sidebar.
Tests and checks
Run the smallest relevant check while working, then the full checks before submitting a change:
bun test
bun run test:integration
bun run check-types
bun run build
bun run checkThe integration suite manages compose.test.yml, binds PostgreSQL to
127.0.0.1:55432, and removes its volumes on exit.
Code map
| Change | Primary location |
|---|---|
| HTTP routes, hooks, server lifecycle | apps/server/src |
| tRPC procedures and relay rules | packages/api/src |
| Authentication and providers | packages/auth/src |
| Database schema and migrations | packages/db/src |
| Browser portal | apps/web/src |
| Admin support console | apps/admin/src |
| Shared UI primitives | packages/ui/src |
| Native client and SRT bridge | apps/native |
| OBS Remote control surface | apps/obs-remote |
| OBS-side control transport | apps/obs-plugin |
| Operator and broadcaster docs | apps/fumadocs/content/docs |
Add environment variables to packages/env and the relevant .env.example in
the same change. Extend server log redaction when introducing a new
credential-shaped field.
Troubleshooting
- The server exits during import: a value in
apps/server/.envis blank or invalid; the error names it. - The portal cannot authenticate: verify the API URL and make
CORS_ORIGINexactly match the portal origin. - The admin console denies access: sign in through the portal and confirm
the account has role
adminor is listed inADMIN_USER_IDS. - A phone cannot connect: use a LAN or tunnel address instead of localhost and allow API port 3000 through the host firewall.
- OBS Remote cannot authenticate: check
OBS_REMOTE_WEB_ORIGIN, port 8083, and WebSocket upgrade support for/api/obs/live. - Relay or snapshot actions fail: check
docker compose psand the MediaMTX and gateway logs. - Integration tests cannot bind port 55432: stop the process using that port and rerun the suite.
The repository's DEVELOPMENT.md and component READMEs remain the canonical
reference for implementation-specific details.