Skip to main content

Build and deployment

GitHub Actions does not run for this project. Its jobs never execute — every one of the last 30 runs on main failed in 8–13 seconds, each annotated "The job was not started because recent account payments have failed". There are no logs and no partial results, because nothing starts. The repository is a code host.

Anything written on the assumption that a workflow gates a merge — required checks, security scanning, SBOM generation, artifact signing, generated-client drift — is inert for the same reason. A red run on this repository is not a signal about the code, and a green one would not be either.

Everything is built and deployed by Coolify, from source, on the deployment host. docs/architecture/DEPLOYMENT.md in the repository is canonical for what is true of the running system; docs/operations/DEPLOYMENT.md is canonical for the procedure. This page summarizes both and does not restate them.

What actually gates a build​

The gates live in the Dockerfiles, because that is the only place a failure can stop a deployment here. They are not the same for every image, and the difference is deliberate.

ImageWhat runs before it is built
apps/apigofmt -l ., go vet ./..., go test ./..., asyncapi-gen -check (contract drift), crypto-inventory
web, admin, pro, provider, academy, connectpnpm --filter <app> lint and typecheck
seller, shelterpnpm lint and pnpm typecheck (app-local build context — neither declares a workspace:* dependency)
the four workers (relay, notifier, reconcile, trust-consumer)nothing — go build only
apps/docspnpm build, which fails on a broken internal link (onBrokenLinks: "throw")
apps/ainothing — and it is not in the production compose at all

The only tests any deployment runs are the Go unit tests. A portal's component tests do not run in its image: they were there briefly and failed on the Coolify host while passing in an identical container locally, because that host runs some forty containers and the suite is timing-sensitive. Lint and typecheck have no timers, so they mean the same thing on a loaded host as on a quiet one.

What scripts/verify.sh gates, and who runs it​

Everything the Dockerfiles do not check is checked by scripts/verify.sh — 23 checks, run on a developer's machine before pushing. It is a real gate; it is just not a machine.

  • Go: gofmt, go vet, go build ./... ./cmd/..., go test -race ./..., AsyncAPI drift, cryptographic inventory.
  • The integration suite against real PostgreSQL + PostGIS — only with scripts/verify.sh --with-db and DATABASE_URL set. Skipped otherwise, and the script says which of the two happened rather than skipping silently.
  • OpenAPI generated-client drift across the workspace.
  • lint + typecheck + test for each of the eight portals, for apps/mobile, and for packages/{ui,api-client,auth}.
  • Supply chain: dependency exception + VEX policy, and SBOM generation checked for substance. The GitHub-action pin audit reports as a visible SKIPPED rather than quietly passing when there is no workflow directory to audit.

apps/mobile has no Dockerfile — it is an Expo app that ships through EAS — so this script is the only automated check it has, not half of a two-part gate like the portals.

Deployment is manual​

There is no webhook on the repository, so pushing to main deploys nothing, and there is no deploy script anywhere in the tree. A deployment is queued against the Coolify instance by hand. Coolify itself is not reachable from the internet: the host firewall opens 22, 80 and 443, and Coolify listens on 8000.

Somebody who pushes expecting a deployment will not get one. That is the current state, not a recommendation.

Migrations​

dbmigrate applies every file not yet recorded in schema_migrations, in lexical filename order, each in its own transaction with the row that records it. A migration numbered below an already-applied one is still picked up — that is how 0076 landed after 0085.

There is no checksum column, so a migration edited after it was applied is undetectable, and parallel branches that each claim "the next number" collide silently.

Migrations run as a one-shot migrate service on the owning role, never from the API process: runServer never calls dbmigrate, so a deployment that skips the migrate step starts the API against whatever schema is already there.

What does not exist​

No staging environment. No canary or phased rollout. No automatic rollback. No artifact registry — every image is rebuilt from source on the host, so no two deployments of the same commit are byte-identical. api migrate down exists and a rollback of any kind has never been demonstrated against this deployment. These are open items in ROADMAP.md Stage 6, not omissions from this page.

See docs/architecture/DEPLOYMENT.md for the full topology, the twenty services, the database roles and what each connection is allowed to do.