# scripts/

Operational scripts for deploying the analytics stack. `deploy.sh` is
**deployment-specific** — it targets the Lilith production hosts — and intentionally lives
outside the generic product docs in [`../docs/`](../docs/).

## `deploy.sh` — build images + ship to vps-0

Builds each service's Docker image, ships it to the production VPS (`quinn-vps` / vps-0),
and brings the stack up. The VPS has only 4 GB RAM, so building **on** it OOM-kills nginx
(incident 2026-05-15) — images are always built elsewhere and shipped in.

Invoked directly or via the repo wrapper: `./run deploy` → `scripts/deploy.sh`.

### Build host (`BUILD_HOST`)

vps-0 is **amd64**; the dev laptop (plum) is **arm64**. A native arm64 image loads on the
VPS but crashes with `exec format error`, so every build targets `linux/amd64`. The old
x86 build host (apricot) is decommissioned. Choose where the build runs with `BUILD_HOST`:

| `BUILD_HOST`        | Behaviour                                                                                                              | When            |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------- | --------------- |
| `black` *(default)* | Native amd64 build on the LAN host **black**; context rsync'd over, images streamed black → vps-0 via your machine. **Fastest.** | Normal deploys  |
| `local`             | Cross-build amd64 on this host under QEMU emulation (`DOCKER_DEFAULT_PLATFORM=linux/amd64`). Slower; needs Docker Desktop running. | black is down   |
| `quinn-vps`         | Build on the VPS itself (`up -d --build`). **OOM risk** — gated behind a warning + abort window.                        | Last resort     |

### Usage

```bash
./scripts/deploy.sh                       # all services, build on black (default)
./scripts/deploy.sh processor api         # subset only (faster iteration)
BUILD_HOST=local ./scripts/deploy.sh      # emulated amd64 build on this host
TARGET_PLATFORM=linux/arm64 ./scripts/deploy.sh   # only if the VPS arch ever changes
```

Valid services: `collector processor api website-bff realtime`.

### What gets shipped

The Dockerfiles consume only the build context — `dist/` (compiled TS), `.vendor-lilith/`
(staged `@lilith/*` registry deps), and `package.json`. **Public npm deps are installed
*inside* the image build**, so Verdaccio is not needed at build time. `bun run
build:services` and the `@lilith` vendor-staging always run locally before the image build,
regardless of `BUILD_HOST`.

### Prerequisites

- **SSH** from this host to both the build host and `quinn-vps`. The image transfer routes
  `build-host → this host → vps-0`, so **no build-host ↔ vps trust is required**.
- **Docker** running on the chosen build host (`BUILD_HOST=local` needs Docker Desktop up;
  the script preflights this and fails fast with a clear message).
- **`zstd`** on the build host and vps-0 (compressed image streaming).

### Pipeline

1. `bun run build:services` — TS → `dist/` (local)
2. stage `@lilith/*` deps into each `services/<svc>/.vendor-lilith/` (local)
3. `docker compose build` on `BUILD_HOST` → `infrastructure-<svc>:latest` (amd64)
4. `docker save | zstd | ssh` — stream images to vps-0 and `docker load`
5. rsync compose + `init.sql`; `docker compose up -d` (`--no-build`, or `--build` for `quinn-vps`)
6. health smoke (`/health` on collector :4001, api :4003, website-bff :4005)
