# BitRiver Live – Agent Guide ## Start here BitRiver Live is a self-hosted live-streaming stack that runs a Go control-plane API, a Next.js viewer, ingest/transcoding services, and stateful data services together. The canonical deployment shape in this repo is Docker Compose driven from `deploy/docker-compose.yml` and a root `.env`. ## Spec-driven workflow (required) For every scoped change, follow this sequence: 1. Start with **read-only analysis** and update `PLAN.md` first (scope, assumptions, risks, tests). 2. Only then implement tasks listed in `TASKS.md`, strictly top-to-bottom. 3. After each task, run the relevant test/check command(s) and update task status/results in `TASKS.md` before moving on. Working artifacts: - `SPEC.md`: user goals and success criteria. - `PLAN.md`: current technical plan, risks, and test plan. - `TASKS.md`: small reviewable tasks with acceptance criteria and status tracking. Keep these docs concise and current so a new contributor can execute `SPEC → PLAN → TASKS → Implement` without chat history. ## Canonical contract Treat these files as the deployment contract: - `deploy/docker-compose.yml` - `./.env` (repo root) - `deploy/ome/Server.generated.xml` (generated file currently present; keep in sync when OME/env settings change) If a change affects runtime behavior, confirm the contract still renders and boots. ## Golden path (single happy path) From repo root: ```bash ./scripts/quickstart.sh ``` Equivalent source command: ```bash GOTOOLCHAIN=local GOPROXY=off GOSUMDB=off go run ./cmd/bitriver quickstart --compose-file deploy/docker-compose.yml ``` ## Required checks before merging Run from repo root unless noted. Default local gate (recommended): ```bash ./scripts/verify.sh ``` Equivalent manual sequence: 1. Go tests: ```bash GOTOOLCHAIN=local GOPROXY=off GOSUMDB=off go test ./... -count=1 -timeout=120s ``` 2. Viewer checks: ```bash cd web/viewer && npm run lint cd web/viewer && npm run test ``` - Local default: run when viewer changes are in scope (or force with `./scripts/verify.sh --viewer`). - CI default (`CI=1` or `CI=true`): run only when `web/viewer` exists, `node` + `npm` are available, and the workflow is viewer-related; force with `./scripts/verify.sh --ci-viewer` for non-viewer workflows. 3. Docker Compose config validation: ```bash docker compose -f deploy/docker-compose.yml config ``` 4. Smoke test: ```bash ./scripts/test-quickstart.sh ``` - In `./scripts/verify.sh`, Docker-dependent checks run in deterministic order: Compose config validation first, then quickstart smoke. - When Docker is unavailable, both Docker-dependent steps are skipped with explicit messages. ## Repo zones ### Product - Runtime code and user-facing behavior: `cmd/`, `internal/`, `web/viewer/`, `deploy/`. - Source-of-truth docs for shipped behavior: `README.md`, `docs/quickstart.md`, `docs/contract.md`, `docs/architecture.md`, `docs/stream-lifecycle.md`, `docs/code-placement.md`. ### Ops / runbooks - Operational and release guidance: `docs/operations.md`, `docs/advanced-deployments.md`, `docs/production-release.md`, `docs/testing.md`. - Operational scripts: `scripts/` (for deploy/test/restore/backup helpers). ### Labs / planning (non-binding) - Experimental/planning docs only: `docs/labs/`. - Do not treat Labs docs as release contract. ## Code style - Keep handlers/services context-aware and explicit about dependencies. - Return wrapped errors (`%w`) with actionable context. - Keep logging structured and close to failure/success boundaries. - Preserve DI boundaries: wire concrete dependencies in `cmd/*`; pass interfaces into `internal/*` services. Go (preferred pattern): ```go func (s *StreamService) Start(ctx context.Context, id string) error { if err := s.repo.MarkStarting(ctx, id); err != nil { s.log.Error("mark starting failed", "stream_id", id, "err", err) return fmt.Errorf("mark stream starting: %w", err) } return nil } ``` - Keep React components small, typed, and testable. - Isolate fetch/API logic in client helpers/hooks, not JSX trees. - Add stable `data-testid` attributes for critical interactive paths. TS/React (preferred pattern): ```tsx export function StreamStatus({ streamId }: { streamId: string }) { const { data, isLoading } = useStream(streamId) if (isLoading) return
Loading…
return{data?.status ?? "unknown"}
} ``` ## Git workflow - Branch naming: `feat/