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.
| Image | What runs before it is built |
|---|---|
apps/api | gofmt -l ., go vet ./..., go test ./..., asyncapi-gen -check (contract drift), crypto-inventory |
web, admin, pro, provider, academy, connect | pnpm --filter <app> lint and typecheck |
seller, shelter | pnpm 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/docs | pnpm build, which fails on a broken internal link (onBrokenLinks: "throw") |
apps/ai | nothing — 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-dbandDATABASE_URLset. Skipped otherwise, and the script says which of the two happened rather than skipping silently. - OpenAPI generated-client drift across the workspace.
lint + typecheck + testfor each of the eight portals, forapps/mobile, and forpackages/{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
SKIPPEDrather 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.