# Local development (/docs/development)



## Prerequisites [#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
* Xcode or Android Studio when working on the native app

Install every workspace dependency from the repository root:

```bash
bun install
```

## Configure the environment [#configure-the-environment]

The local launcher creates and validates the server and web env files. Copy only
the native example manually because its address depends on your device:

```bash
cp apps/native/.env.example apps/native/.env.local
```

The launcher generates missing local secrets and reports blank Twitch or Kick
credentials. Those provider flows require real credentials; unrelated local
features still start.

Register the callbacks for the providers you exercise locally:

```text
Twitch: https://api.visp.localhost/api/auth/callback/twitch
Kick:   https://api.visp.localhost/api/auth/oauth2/callback/kick
```

Relay and S3 values point to the local Compose services. Never commit `.env`
files.

## Start the local stack [#start-the-local-stack]

One command starts PostgreSQL 18, MinIO, MediaMTX, the relay gateway, API, and
portal, then applies migrations:

```bash
bun run dev:local
```

Run `bun run dev:local:down` to stop the containers without deleting data.

When changing `packages/db/src/schema`, generate and inspect a migration before
applying it:

```bash
bun run db:generate
bun run db:migrate
```

Use `bun run db:push` only with a disposable development database. Open Drizzle
Studio with `bun run db:studio`.

## Run the project [#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`   |
| 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` | `http://localhost:4000`        |
| 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, Expo, and documentation site simultaneously.

### Native app [#native-app]

Expo Go cannot load the local SRT module. Build a development client instead:

```bash
bun run --cwd apps/native ios
bun run --cwd apps/native android
```

Use 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 [#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 [#tests-and-checks]

Run the smallest relevant check while working, then the full checks before
submitting a change:

```bash
bun test
bun run test:integration
bun run check-types
bun run build
bun run check
```

The integration suite manages `compose.test.yml`, binds PostgreSQL to
`127.0.0.1:55432`, and removes its volumes on exit.

## Code map [#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`               |
| Shared UI primitives                 | `packages/ui/src`            |
| Native client and SRT bridge         | `apps/native`                |
| OBS remote control                   | `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 [#troubleshooting]

* **The server exits during import:** a value in `apps/server/.env` is blank or
  invalid; the error names it.
* **The portal cannot authenticate:** verify the API URL and make `CORS_ORIGIN`
  exactly match the portal origin.
* **A phone cannot connect:** use a LAN or tunnel address instead of localhost
  and allow the API port through the host firewall.
* **Relay or snapshot actions fail:** check `docker compose ps` and 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.
