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

85 lines
3.3 KiB
Markdown

# 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:
```bash
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):
```bash
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
```bash
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:
```bash
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.