Migrations And Release

Use migrations and generated-contract checks as release gates. Do not depend on runtime AutoMigrate outside local development.

Image Build Matrix

The backend Dockerfile ships one Go build stage and exposes several runtime targets. CI publishes each as a separate image; the default runtime target remains a combined image for local compatibility.

ImageDocker targetEntrypoint
wipe-apiapi-runtime/app/api
wipe-proof-workerproof-worker-runtime/app/worker-proof
wipe-workerworker-runtime/app/worker
wipe-verifier-cliverifier-cli-runtime/app/verifier
wipe-migratormigrator-runtime/app/migrator

Build an individual image locally from the repository root:

  docker build --target api-runtime          -t wipe-api:local          backend
docker build --target proof-worker-runtime -t wipe-proof-worker:local backend
docker build --target worker-runtime       -t wipe-worker:local       backend
docker build --target verifier-cli-runtime -t wipe-verifier-cli:local backend
docker build --target migrator-runtime     -t wipe-migrator:local     backend
  

The wipe-worker image is shared by queue worker kinds. Set WORKER_KIND or pass the kind as the first argument — anchor, notifications, reports, retry, or maintenance. wipe-proof-worker is a dedicated binary and does not take a kind argument.

Verifier CLI Smoke

The verifier CLI can call the public /verify endpoint:

  docker run --rm wipe-verifier-cli:local \
  -url http://host.docker.internal:8080/verify -code 4A7F2C90AB18DE55
  

It can also hash canonical JSON offline using the same RFC 8785/JCS transform as the certificate service:

  docker run --rm -i wipe-verifier-cli:local \
  -canonical-json - -expect-hash sha256:<expected> < certificate.json
  

Build Images

Reference build for the full stack:

  docker compose build api proof-worker anchor-worker notification-worker report-worker retry-worker maintenance-worker
  

Run Migrations

The root compose file exposes a migrator profile:

  docker compose --profile migration build db-migrate
docker compose --profile migration run --rm db-migrate version
  

For a reviewed migration run, set:

  MIGRATIONS_DATABASE_URL='postgres://...'
MIGRATIONS_ACTION=up
  

Use the migrator up -dry-run command where supported before applying changes to a shared environment.

Reviewed Migration Flow

The migrator image ships the Go cmd/migrator binary with backend/db/migrations embedded. It supports up (with -dry-run), version, and force VERSION, and keeps the schema_migrations table compatible with the previous golang-migrate image.

Before any production migration run:

  1. Review the SQL diff and rollback script for every new *.up.sql/*.down.sql pair.
  2. Run make -C backend migrations-check.
  3. Build the reviewed image with docker build --target migrator-runtime -t wipe-migrator:<tag> backend.
  4. Run a non-mutating version check against the target database.
  5. Run up only after the change ticket names the image digest, target database, expected migration versions, backup point, and rollback owner.

Example commands:

  docker run --rm wipe-migrator:local help

docker run --rm --network <deployment-network> \
  -e MIGRATIONS_DATABASE_URL="<reviewed-postgres-url>" \
  wipe-migrator:local version

docker run --rm --network <deployment-network> \
  -e MIGRATIONS_DATABASE_URL="<reviewed-postgres-url>" \
  wipe-migrator:local up -dry-run

docker run --rm --network <deployment-network> \
  -e MIGRATIONS_DATABASE_URL="<reviewed-postgres-url>" \
  wipe-migrator:local up
  

The migrator only migrates forward; rollbacks apply the reviewed *.down.sql scripts manually. Do not disable DATABASE_AUTOMIGRATE in an environment until the reviewed SQL baseline is confirmed to cover the full current GORM schema.

Refresh API Contract

From backend/:

  make api-contract
  

This refreshes Swagger and generated TypeScript clients. For route comparison only:

  go run ./cmd/route-contract -write-docs
  

Expected outputs:

FilePurpose
backend/docs/swagger/swagger.jsonGenerated OpenAPI artifact.
backend/docs/api-contract-status.mdHuman-readable route backlog comparison.
backend/docs/api-contract-status.jsonMachine-readable route status.
frontend/src/lib/api/client and dashboard/src/lib/api/clientGenerated HeyAPI clients.

The current contract status reports 54 expected backlog operations and 54 implemented in Swagger.

Validate Backend

Run:

  cd backend
go test ./...
make api-contract
  

Docker-heavy checks:

  make -C backend compose-e2e-config
docker compose -f compose.e2e.yml up --build -d
  

Use unique E2E ports when running in parallel:

  COMPOSE_PROJECT_NAME=wipe-e2e-local \
WIPE_E2E_API_PORT=18080 \
WIPE_E2E_KEYCLOAK_PORT=18081 \
WIPE_E2E_PUBLIC_API_URL=http://127.0.0.1:18080 \
WIPE_E2E_PUBLIC_KEYCLOAK_URL=http://127.0.0.1:18081 \
docker compose -f compose.e2e.yml up --build -d
  

Validate Frontends

For the new frontend:

  cd frontend
npm install
npm run check
npm run build
  

For the dashboard:

  cd dashboard
pnpm check
pnpm build
  

Release Checklist

CheckExpected result
Git status reviewedOnly intended source and generated files changed.
Migrations reviewedUp/down SQL exists and migrator image can run version.
API contract refreshedNo missing cahier backlog endpoints.
Backend tests passgo test ./... succeeds.
Frontend type/build checks passSelected portal builds with generated client.
Compose smoke passesAPI, Keycloak, MinIO, workers, and portals start.
Observability heartbeats presentAPI and worker signals visible in Grafana/Prometheus.
Secrets reviewedNo local defaults or dev bypasses in non-local environment.

Optional Cosign Policy

Buildx SBOM and provenance attestations are mandatory for pushed images. If the release policy adds cosign:

  • Sign only immutable digest or commit-SHA tags after the Harbor push.
  • Prefer keyless signing with CI OIDC when the Gitea runner and trust policy support it; otherwise use a key-backed cosign secret owned by the release environment.
  • Deployment admission must verify either the expected OIDC issuer/identity or the configured public key before pulling promoted images.