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.
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/versionreports 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);pushis main-only, PRs are covered by thepull_requestevent. 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:
dockerfilebuild pack, repogit.siax.io/sax3l/0pt, branchmain, health check/api/health, domains0pt.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.examplefor 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.