Files
0pt/docs/runbook/deploy.md
T
adminandClaude Sonnet 5 5a0429d46b
masterplan-lock / check (push) Successful in 4s
CI (SIAX Cloud) / security (push) Successful in 13s
CI (SIAX Cloud) / quality (push) Successful in 32s
Security Scan (SIAX Cloud) / security (push) Successful in 52s
CI (SIAX Cloud) / integration (push) Successful in 1m35s
fix(ci): add SBOM+sign release chain and gate Coolify redeploy on it
0pt had no SBOM/sign/digest chain at all (re-confirmed live before this
change: no cosign/syft/sbom hit anywhere under .gitea/workflows/, only doc
mentions in CHANGELOG.md/docs/architecture/ARCHITECTURE.md). Applies the
same two-part pattern already proven on sax3l/cl0ud and sax3l/st0re
(fix/wire-signed-digest-to-deploy, 2026-09-14):

1. .gitea/workflows/release.yml (new): builds the image, pushes it to
   git.cloud.siax.io/sax3l/0pt:sha-<12>, SBOMs it (syft), signs it (cosign,
   key-based), verifies the signature in the same run, and commits
   source-bundle/production-evidence records back to the repo.
2. .gitea/workflows/cd.yml: the `deploy` job now polls the registry for
   this exact commit's release-chain image and cosign-verifies it (against
   the committed cosign.pub) before calling Coolify's deploy endpoint --
   fails closed (10 min timeout) if release.yml never produced a verifiable
   artifact for this sha. Added AFTER the existing guard step, which still
   fails closed first on 0pt's real, pre-existing blocker: no Coolify
   Application is provisioned for 0pt yet (COOLIFY_APP_ID is a literal
   placeholder). This gate is wired ahead of that so it's already in place
   the day the app is provisioned, instead of a second follow-up change.

Honest limitation carried over from cl0ud/st0re/h0st: once provisioned,
0pt's Coolify Application is expected to use the `dockerfile` build pack
(per siax.config.json), so Coolify would still perform its own independent
docker build rather than pulling the signed image, and /api/v1/deploy has
no digest parameter to pin against -- true digest-pinning needs a
production Coolify resource-type decision, out of CI-YAML scope. This gate
proves a signed, verified build of the exact commit exists before
authorizing any deploy call; it does not prove byte-identical output from
Coolify's own rebuild.

cosign.pub is a fresh, 0pt-specific keypair generated offline for this
change. NOT YET SET on sax3l/0pt (release.yml/cd.yml's gate will fail
closed until present): REGISTRY_USER, REGISTRY_TOKEN, COSIGN_PRIVATE_KEY,
COSIGN_PASSWORD, SIAX_CI_TOKEN repo secrets.

Validated without live Coolify/registry access: YAML-parsed all 2 files,
bash -n syntax-checked all 19 embedded run: blocks, and dry-ran the actual
verification logic -- the RepoDigests filter and bounded poll-retry loop
against mocked scenarios, plus a real cosign sign+verify round-trip
(offline signing-config, local throwaway registry container) proving the
gate's cosign verify command genuinely passes on the correct key/digest and
fails closed on a wrong key and on an unsigned image.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-14 11:45:53 +02:00

3.2 KiB

Runbook: Deploy — 0pt

Mål: sätta 0pt.app på SIAX-flottan (Coolify) och avveckla Vercel i ordning. Status 2026-08-10: siax.config.json deklarerar target: coolify; ingen Coolify-app provisionerad; live-instansen körs fortfarande på Vercel.

1. Förutsättningar (blockerare för cutover)

  • Coolify-app provisionerad i cloud.siax.io (build_pack: dockerfile, repo git.siax.io/sax3l/0pt, gren main).
  • COOLIFY_APP_ID (appens UUID) ifylld i .gitea/workflows/cd.yml.
  • COOLIFY_TOKEN-secret satt på repot (Gitea → Settings → Actions → Secrets).
  • Env-secrets inlagda i Coolify-appen från Infisical-projektet 0pt (se backup/restore + incident runbooks).
  • DNS-cutover planerad: 0pt.app → Coolify-appens ingress (eller Caddy).

1b. Signerad-artefakt-grind (tillagd 2026-09-14, samma mönster som cl0ud/st0re/h0st)

.gitea/workflows/release.yml (nytt) bygger, SBOM:ar (syft), signerar (cosign) och evidensbokför varje push till main, och pushar till git.cloud.siax.io/sax3l/0pt:sha-<12>. cd.ymls deploy-jobb har nu ett extra steg (efter guard-steget i punkt 1 ovan) som kräver att exakt denna commits avbildning finns i registret och att dess cosign-signatur verifierar (mot committad cosign.pub) innan Coolify överhuvudtaget anropas.

Detta är byggt FÖRE Coolify-appen finns (se blockerarna i punkt 1) — så att grinden redan är på plats den dag appen provisioneras, i stället för att bli ett separat uppföljningsarbete. Samma ärliga begränsning som cl0ud/st0re/ h0st: när appen väl provisioneras som dockerfile build pack (så som siax.config.json föreskriver) kör Coolify sin EGEN oberoende docker build från git-källan, inte den signerade avbildningen — /api/v1/deploy saknar digest-parameter helt. Sann digest-pinning skulle kräva att appen provisioneras som Coolifys separata dockerimage-resurstyp i stället, ett beslut som inte är fattat än. Se .gitea/workflows/cd.ymls "SIGNED-ARTIFACT GATE"-kommentar för den fullständiga skrivningen.

Inte satt på sax3l/0pt ännu: repo-secrets REGISTRY_USER, REGISTRY_TOKEN, COSIGN_PRIVATE_KEY, COSIGN_PASSWORD, SIAX_CI_TOKEN — release.yml fail-closed:ar på sin första körning tills dessa finns.

2. Deploy (när appen finns)

  1. Push till main → .gitea/workflows/cd.yml POSTar till https://cloud.siax.io/api/v1/deploy?uuid=$COOLIFY_APP_ID&force=false, efter att den signerade-artefakt-grinden i punkt 1b gått grön.
  2. Följ run i Gitea Actions; verifiera att den slutar grön.
  3. Verifiera hälsa: curl https://0pt.app/api/health → ok:true och db: configured (när Postgres kopplats in).

3. Manuell redeploy (nöd)

curl -fsS -X POST "https://cloud.siax.io/api/v1/deploy?uuid=<UUID>&force=true" \
  -H "Authorization: Bearer $COOLIFY_TOKEN"

4. Vercel-avveckling (EFFER att Coolify kör stabilt)

  1. Verifiera att ingen verklig trafik/data ligger bakom (2026-08-10: /api/health visar db/queue: not_configured — marknads-/dev-läge).
  2. Peka DNS om till Coolify. 3. Behåll Vercel-projektet tills certifierad paritet (katalog, orderflöde, betalningar). 4. Avveckla först därefter.