Create, test, and upgrade a Wahoo application.

> **Work in progress:** Do not use Wahoo in production yet.

## Requirements

Install Go 1.25 or later, Node.js 22 or later, npm, Docker, and GPG when you install release binaries.

## Create An Application

Run this from a Wahoo checkout:

```bash
go run ./cmd/wahoo new --local ./acme
```

The command creates a core application, writes `.wahoo/project.json`, runs `go mod tidy`, and asks about the three browser-facing modules.

```text
Add authentication route stubs? [y/N]:
Add an SSE endpoint? [y/N]:
Add a WebSocket endpoint? [y/N]:
```

Use `--modules` for all module selection in automation:

```bash
go run ./cmd/wahoo new --local --modules auth,openapi,jobs ./acme
go run ./cmd/wahoo new --local --yes ./acme
```

`--local` vendors the current framework checkout so the generated Docker build does not depend on a host path.

## Modules

```bash
wahoo modules
wahoo add ./acme openapi jobs
```

| Module | Adds |
| --- | --- |
| `auth` | `501` account route stubs. |
| `sse` | A bounded event hub and a `501` stream route. |
| `websocket` | A `501` WebSocket route. |
| `openapi` | An application-owned OpenAPI 3.1 document at `/openapi.json`. |
| `jobs` | A separate `cmd/worker` and queue integration seam. |
| `uploads` | A `501` upload route. |
| `mail` | A `501` mail route. |
| `audit` | A `501` audit-event route. |
| `webhooks` | A `501` webhook route. |
| `entitlements` | A `501` entitlement route. |
| `billing` | A `501` billing route. |

The modules do not select a provider or expose a live integration. Replace an HTTP stub only after authorization, limits, storage, and operational policy exist.

## Start And Test

```bash
cd acme
npm ci --prefix web
npm run dev --prefix web
```

Open `http://localhost:8080`. The Vite and SSR worker listen on loopback port `4173` during local development.

```bash
npm test --prefix web
npm run typecheck --prefix web
npm run build --prefix web
npm run test:ssr --prefix web
go test ./...
go vet ./...
```

## Docker

```bash
docker build -f Dockerfile.example -t acme .
docker run --rm -p 8080:8080 acme
```

The image uses pinned base digests, a non-root runtime user, and `tini`. The Node supervisor stops the container if either the Go process or private SSR worker exits.

## Upgrade

Commit the application first. Check an upgrade without writes, then apply it to the target release tag.

```bash
wahoo upgrade ./acme --check --to vX.Y.Z
wahoo upgrade ./acme --apply --to vX.Y.Z
go mod tidy
go test ./...
```

`upgrade --apply` changes only the Wahoo dependency and `.wahoo/project.json`. It never rewrites `app/`, `web/`, migrations, or the lockfile.