Files
admin ea40a67940
CI (SIAX Cloud) / sonar (pull_request) Skipped
masterplan-lock / check (push) Successful in 5s
masterplan-lock / check (pull_request) Successful in 4s
CI (SIAX Cloud) / security (pull_request) Successful in 16s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 17s
CI (SIAX Cloud) / integration (pull_request) Successful in 9m49s
CI (SIAX Cloud) / quality (pull_request) Successful in 10m1s
docs(deploy): runbook and workflow headers match the live pipeline
The runbook still described Vercel and an unprovisioned Coolify app. It now
documents the verified chain (ci -> release attestation -> cd -> Coolify),
the proven manual dispatch, the verification endpoints and the rollback.
2026-11-02 18:57:45 +01:00

3.3 KiB

Runbook: Deploy — 0pt

Status 2026-11-02: live on the SIAX fleet (Coolify) at https://0pt.app. The automatic chain and the manual path are both verified: a signed image is attested, Coolify is called, the container restarts on the new commit, and /api/version reports it.

Pipeline (automatic)

git push main
  ├─ ci.yml        quality (typecheck, contrast, tests, build)
  │                integration (live OpenSearch + ClickHouse)
  │                security (Trivy, blocking)
  ├─ security.yml  push/PR/weekly scan
  ├─ release.yml   build + SBOM + blocking vuln scan + cosign attest + push
  │                → git.cloud.siax.io/sax3l/0pt:sha-<12>
  └─ cd.yml        waits for THIS commit's attestation, verifies it against
                   cosign.pub, then POSTs to Coolify
prod-smoke.yml     every 30 min: ready + db/stripe + running commit == main
  • One run per ref (concurrency); push is main-only, PRs are covered by the pull_request event. Deploys have their own non-cancelling group so two runs never race the same app.
  • Dependency caches: pnpm store (Node jobs), npm (doc0/n0d), pip (0pt-motor).

Manual deploy (proven)

Trigger the same, signed path — do not POST to Coolify directly unless the pipeline itself is broken:

curl -X POST -H "Authorization: token $GITEA_TOKEN" -H "Content-Type: application/json" \
  -d '{"ref":"main"}' \
  https://git.siax.io/api/v1/repos/admin/0pt/actions/workflows/cd.yml/dispatches

Emergency only (bypasses the attestation gate; still builds the branch head):

curl -X POST -H "Authorization: Bearer $COOLIFY_API_TOKEN" -H "Content-Type: application/json" \
  -d '{"uuid":"<COOLIFY_APP_ID>"}' https://cloud.siax.io/api/v1/deploy

Verify a deploy

curl -s https://0pt.app/api/ready     # {"ready":true,"db":"ok","stripe":"ok",...}
curl -s https://0pt.app/api/health    # liveness envelope
curl -s https://0pt.app/api/version   # {"commit":"<sha>"}

commit must equal the current main SHA. prod-smoke.yml enforces exactly that every 30 minutes; a green deploy is never confused with a current service.

Rollback

Coolify builds from the branch head, so a rollback is a normal revert:

git revert --no-edit <bad-sha> && git push origin main

That runs the same chain and deploys the reverted tree. Coolify's own "redeploy previous deployment" is the break-glass alternative.

Configuration (names only — values live in Coolify/Infisical)

  • Coolify app: dockerfile build pack, repo git.siax.io/sax3l/0pt, branch main, health check /api/health, domains 0pt.app + api.0pt.app.
  • Repository variable: COOLIFY_APP_ID.
  • Repository secrets: COOLIFY_TOKEN, REGISTRY_USER, REGISTRY_TOKEN, COSIGN_PRIVATE_KEY, COSIGN_PASSWORD, SIAX_CI_TOKEN.
  • App env: DATABASE_URL, STRIPE_SECRET_KEY, AUTH_SECRET… see .env.example for the full, documented list (DOC0, INF0/decision Ollama, OpenFGA/project grants, model policy, credit price book, OIDC).

Known limitation

Coolify rebuilds from source; /api/v1/deploy has no digest parameter, so the verified digest is recorded as provenance for the deploy rather than pinned as the running bytes. True digest pinning would require a Coolify dockerimage resource — an infrastructure decision, not a CI-YAML change.