> ## Documentation Index
> Fetch the complete documentation index at: https://docs.shipvoice.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Deploy

> What compose gives you, and what has to change before this faces anything but your laptop.

`docker-compose.yml` is a local stack: Postgres, the backend, the voice worker
and the console, on one network, with both published ports bound to
`127.0.0.1`.

```bash theme={null}
docker compose up --build
docker compose down
```

Each service also has its own `Dockerfile` and deploys on its own. LiveKit is
external in every case: the worker connects out to `LIVEKIT_URL`.

## Read this before it faces a network

<Warning>
  The backend has no authentication. Every route is open, including the ones
  that read and write the LiveKit project and the agent's prompt. That is
  deliberate for a tool on your own machine and wrong on a public address. Put
  something in front of it, or do not expose it.
</Warning>

Three facts follow from that:

* **`CONSOLE_WRITES_ENABLED` stays off.** It defaults to `false` in code.
  Compose turns it on because compose is local. Anywhere else, leave it off and
  the write routes refuse.
* **Your LiveKit signing secret is stored unencrypted.** On first boot the
  backend copies the LiveKit project from the environment into the
  `livekit_settings` table, and the Deployment page edits it there. So the
  secret is in Postgres, in the `pgdata` volume, and in any dump you take.
  Rotating the key in your LiveKit project is the revocation path.
* **`CORS_ORIGINS_STR` unset means every origin is allowed.** Compose sets it
  to `http://localhost:5173`, but the code default is empty and
  `BACKEND_CORS_ORIGINS` returns `["*"]` for empty, so a backend started
  without it accepts browser calls from anywhere. Set it explicitly on any host
  that is not your own machine.

## The three services

`agent/fly.toml` is set up for Fly: `performance-1x`, blue/green, and a
`kill_timeout` of five minutes matching the `drain_timeout=300` on
`AgentServer`. That pairing is what makes a deploy mid-call finish the call
instead of dropping it. The worker also restarts itself when the stored LiveKit
project changes, which is why the compose service carries
`restart: unless-stopped`. Whatever you deploy onto needs the same: a process
supervisor that brings it back.

The backend runs `alembic upgrade head` on startup under a Postgres advisory
lock, so concurrent boots serialise. There is no separate migration step, and a
slow database does not stop it booting: it serves tokens from the environment
until Postgres catches up.

The console is static files. The three `VITE_` values are compiled in at build
time, so the image is environment-specific: build one per environment, and
never reuse a local image against a remote backend.

## Where secrets live

In `.env`, which is gitignored, except for the LiveKit project once the backend
has copied it into Postgres. Nothing else in this repo persists a credential.
