Six documents on main say some version of "no Coolify app has been provisioned for this repo; cd.yml's COOLIFY_APP_ID is a literal placeholder that fails the job on purpose."
That is no longer true, and it is not a cosmetic staleness problem: a merge into main ships to
production today, while the repo's own README, ADR and runbook promise that it cannot. I found
this the only way you can find it — by merging something and then reading the deploy job's log.
Evidence (measured, not assumed)
What
How
Repo Actions secretCOOLIFY_TOKEN exists, created 2026-08-31T11:34:34Z
GET /api/v1/repos/sax3l/0pt/actions/secrets
Repo Actions variableCOOLIFY_APP_ID exists with a real 24-character value — not the TODO-no-coolify-app-provisioned-yet literal
GET /api/v1/repos/sax3l/0pt/actions/variables
Both guards in .gitea/workflows/cd.yml therefore pass, and the deploy actually fired
push to main @ 1ff34410, run 11695, job 26295
From the job log:
::group::Run if [ -z "***" ]; then <- COOLIFY_TOKEN is set, so -z is false
...
::group::Run curl -fsS -X POST \
"https://cloud.siax.io/api/v1/deploy?uuid=${COOLIFY_APP_ID}&force=false" \
...
coolify deploy HTTP 200
🏁 Job succeeded
Deploy to Coolify / deploy (push) = success.
What this PR deliberately does NOT claim
That the Coolify app builds, starts, or serves anything, or that 0pt.app DNS points at it rather
than at Vercel. Nobody probed the running site. All that is proven is that the webhook is
accepted with HTTP 200. Every corrected passage says exactly that, so this does not swap one
unverified claim for another — which is why GOLDEN-STANDARD-REPORT.md D4 moves from ❌ to ⚠️ and
not to ✅.
Files corrected
README.md — "Deploy" section. Original bullet struck, not deleted; a new bullet states the
measured state and the operational consequence ("know what you are shipping before you merge").
docs/02-architecture.md — the hosting SUPERSEDED callout.
docs/architecture/decisions/0001-deploy-target-coolify-not-vercel.md — the consequence is
annotated with an UPDATE block; the decision text is untouched. An ADR is a historical record,
so the original reads as "true when written 2026-08-09", not as current state.
docs/runbook/incident.md — the symptom row is now inverted. The old entry told an operator to
expect a red deploy job; the real hazard today is the opposite, an unintended deploy.
docs/compliance/INVENTORY.md — the CI row's "deploy.yml röd men avsiktligt" claim.
Verification
Docs only — no workflow files, no .gitignore, no source. Falsifiable check over all six files
(a stale claim present without its correction marker is red):
origin/main -> FAIL (12 findings)
this branch -> PASS
mutation: drop only the README bullet -> FAIL
mutation: drop only the runbook correction -> FAIL
One honest note about the check itself: it first passed vacuously on docs/02-architecture.md,
because PR #32 had already placed the string CORRECTED 2026-09-06 in that file for an unrelated
reason. The marker was made specific so the check can actually fail there too — a gate that cannot
fall is worse than no gate.
node scripts/siax-doctor.mjs: 39/40, identical to main (the one failure, P1-15 CODEOWNERS, is
pre-existing and is what PR #19 supplies).
## The finding
Six documents on `main` say some version of *"no Coolify app has been provisioned for this repo;
`cd.yml`'s `COOLIFY_APP_ID` is a literal placeholder that fails the job on purpose."*
That is no longer true, and it is not a cosmetic staleness problem: **a merge into `main` ships to
production today, while the repo's own README, ADR and runbook promise that it cannot.** I found
this the only way you can find it — by merging something and then reading the deploy job's log.
## Evidence (measured, not assumed)
| What | How |
|---|---|
| Repo Actions **secret** `COOLIFY_TOKEN` exists, created `2026-08-31T11:34:34Z` | `GET /api/v1/repos/sax3l/0pt/actions/secrets` |
| Repo Actions **variable** `COOLIFY_APP_ID` exists with a real 24-character value — **not** the `TODO-no-coolify-app-provisioned-yet` literal | `GET /api/v1/repos/sax3l/0pt/actions/variables` |
| Both guards in `.gitea/workflows/cd.yml` therefore pass, and the deploy actually fired | push to `main` @ `1ff34410`, run **11695**, job **26295** |
From the job log:
```
::group::Run if [ -z "***" ]; then <- COOLIFY_TOKEN is set, so -z is false
...
::group::Run curl -fsS -X POST \
"https://cloud.siax.io/api/v1/deploy?uuid=${COOLIFY_APP_ID}&force=false" \
...
coolify deploy HTTP 200
🏁 Job succeeded
```
`Deploy to Coolify / deploy (push)` = **success**.
## What this PR deliberately does NOT claim
That the Coolify app builds, starts, or serves anything, or that `0pt.app` DNS points at it rather
than at Vercel. **Nobody probed the running site.** All that is proven is that the webhook is
accepted with HTTP 200. Every corrected passage says exactly that, so this does not swap one
unverified claim for another — which is why `GOLDEN-STANDARD-REPORT.md` D4 moves from ❌ to ⚠️ and
not to ✅.
## Files corrected
- `README.md` — "Deploy" section. Original bullet struck, not deleted; a new bullet states the
measured state and the operational consequence ("know what you are shipping before you merge").
- `docs/02-architecture.md` — the hosting `SUPERSEDED` callout.
- `docs/architecture/decisions/0001-deploy-target-coolify-not-vercel.md` — the *consequence* is
annotated with an UPDATE block; the decision text is untouched. An ADR is a historical record,
so the original reads as "true when written 2026-08-09", not as current state.
- `docs/runbook/incident.md` — the symptom row is now inverted. The old entry told an operator to
expect a red deploy job; the real hazard today is the opposite, an *unintended* deploy.
- `docs/compliance/GOLDEN-STANDARD-REPORT.md` — D4 ❌ → ⚠️.
- `docs/compliance/INVENTORY.md` — the CI row's "deploy.yml röd men avsiktligt" claim.
## Verification
Docs only — no workflow files, no `.gitignore`, no source. Falsifiable check over all six files
(a stale claim present without its correction marker is red):
```
origin/main -> FAIL (12 findings)
this branch -> PASS
mutation: drop only the README bullet -> FAIL
mutation: drop only the runbook correction -> FAIL
```
One honest note about the check itself: it first passed **vacuously** on `docs/02-architecture.md`,
because PR #32 had already placed the string `CORRECTED 2026-09-06` in that file for an unrelated
reason. The marker was made specific so the check can actually fail there too — a gate that cannot
fall is worse than no gate.
`node scripts/siax-doctor.mjs`: 39/40, identical to `main` (the one failure, `P1-15 CODEOWNERS`, is
pre-existing and is what PR #19 supplies).
Generated with [Claude Code](https://claude.com/claude-code)
Six documents on main assert that no Coolify app has been provisioned for 0pt and
that cd.yml's COOLIFY_APP_ID is a literal placeholder whose guard fails the job on
purpose. That is no longer true, and the gap is not cosmetic: it means a merge to
main silently ships to production while the repo's own docs promise it cannot.
Measured, not assumed:
* repo Actions secret COOLIFY_TOKEN exists, created 2026-08-31T11:34:34Z
(GET /api/v1/repos/sax3l/0pt/actions/secrets)
* repo Actions variable COOLIFY_APP_ID exists with a real 24-character value,
not the "TODO-no-coolify-app-provisioned-yet" literal
(GET /api/v1/repos/sax3l/0pt/actions/variables)
* both guards in .gitea/workflows/cd.yml therefore pass; on the push to main at
1ff34410 (2026-09-06) the job "Deploy to Coolify / deploy" (run 11695, job
26295) finished SUCCESS with "coolify deploy HTTP 200" in its log
Deliberately NOT claimed: that the Coolify app builds, starts or serves, or that
0pt.app DNS points at it rather than Vercel. Nobody probed the running site, and
every corrected passage says so explicitly rather than replacing one unverified
claim with another.
Corrected: README.md "Deploy", docs/02-architecture.md hosting callout,
docs/architecture/decisions/0001 (consequence annotated, decision untouched --
an ADR is a historical record), docs/runbook/incident.md (the symptom row is now
inverted: the risk is an unintended deploy, not a red job),
docs/compliance/GOLDEN-STANDARD-REPORT.md D4 (❌ -> ⚠️, not ✅, because "webhook
accepted" is not "app serves"), docs/compliance/INVENTORY.md CI row.
Falsifiable check over all six files (stale claim present without a correction
marker = red):
origin/main -> FAIL (12 findings)
this branch -> PASS
mutation: drop only the README bullet -> FAIL
mutation: drop only the runbook correction -> FAIL
The check initially passed vacuously on docs/02-architecture.md because PR #32 had
already put the string "CORRECTED 2026-09-06" in that file for an unrelated reason;
the marker was made specific so the check could actually fail there too.
Docs only. No workflow files, no .gitignore, no source.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The previous commit said "nobody has probed the running site". I then probed it,
so the docs should not keep hedging.
curl -sI https://0pt.app/
HTTP/2 402
server: Vercel
x-vercel-error: DEPLOYMENT_DISABLED
strict-transport-security: max-age=63072000
https://0pt.app/api/health -> HTTP 402, body "Payment required / DEPLOYMENT_DISABLED"
Two facts hold simultaneously and the docs now state both: the Coolify deploy hook
fires for real on every push to main, AND the public product domain is dark behind a
disabled Vercel deployment with DNS never cut over. Neither cancels the other.
Still not established, and still marked as such: whether the Coolify app builds and
starts. A deploy endpoint returning 200 means the request was accepted, not that a
container came up healthy.
Side note worth recording for PR #19: the HSTS header on that 402 response is
Vercel's platform default, which is exactly the disappearing-on-cutover risk
next.config.mjs in PR #19 was written to remove.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Probed 2026-09-06 08:00 UTC. So two things are true at the same time, and the docs now say both:
The Coolify deploy hook fires for real on every push to main (coolify deploy HTTP 200).
The public product domain 0pt.app is dark — still pointed at Vercel, whose deployment is
disabled for non-payment. DNS was never cut over, so the Coolify app serves no 0pt.app traffic.
Neither fact cancels the other, and the earlier "unverified" wording would have let a reader assume
whichever one suited them.
Still not established, and still labelled as such in the docs: whether the Coolify app builds and
starts at all. A deploy endpoint returning 200 means the request was accepted, not that a container
came up healthy.
One detail worth carrying to #19: the strict-transport-security header on that 402 is Vercel's
platform default, not the app's. That is precisely the "HSTS silently disappears on cutover to
Coolify/Traefik" risk that next.config.mjs in #19 was written to remove — and it is unfixed on main today.
**Addendum — I then probed the running site, so the PR no longer hedges.**
```
curl -sI https://0pt.app/
HTTP/2 402
server: Vercel
x-vercel-error: DEPLOYMENT_DISABLED
strict-transport-security: max-age=63072000
https://0pt.app/api/health -> HTTP 402, body: "Payment required / DEPLOYMENT_DISABLED"
```
Probed 2026-09-06 08:00 UTC. So two things are true at the same time, and the docs now say both:
1. The Coolify deploy hook **fires for real** on every push to `main` (`coolify deploy HTTP 200`).
2. The public product domain `0pt.app` is **dark** — still pointed at Vercel, whose deployment is
disabled for non-payment. DNS was never cut over, so the Coolify app serves no `0pt.app` traffic.
Neither fact cancels the other, and the earlier "unverified" wording would have let a reader assume
whichever one suited them.
Still not established, and still labelled as such in the docs: whether the Coolify app builds and
starts at all. A deploy endpoint returning 200 means the request was accepted, not that a container
came up healthy.
One detail worth carrying to #19: the `strict-transport-security` header on that 402 is **Vercel's
platform default**, not the app's. That is precisely the "HSTS silently disappears on cutover to
Coolify/Traefik" risk that `next.config.mjs` in #19 was written to remove — and it is unfixed on
`main` today.
Head is now `853f093`; CI re-running.
Generated with [Claude Code](https://claude.com/claude-code)
admin
merged commit into main2026-09-06 08:05:08 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The finding
Six documents on
mainsay some version of "no Coolify app has been provisioned for this repo;cd.yml'sCOOLIFY_APP_IDis a literal placeholder that fails the job on purpose."That is no longer true, and it is not a cosmetic staleness problem: a merge into
mainships toproduction today, while the repo's own README, ADR and runbook promise that it cannot. I found
this the only way you can find it — by merging something and then reading the deploy job's log.
Evidence (measured, not assumed)
COOLIFY_TOKENexists, created2026-08-31T11:34:34ZGET /api/v1/repos/sax3l/0pt/actions/secretsCOOLIFY_APP_IDexists with a real 24-character value — not theTODO-no-coolify-app-provisioned-yetliteralGET /api/v1/repos/sax3l/0pt/actions/variables.gitea/workflows/cd.ymltherefore pass, and the deploy actually firedmain@1ff34410, run 11695, job 26295From the job log:
Deploy to Coolify / deploy (push)= success.What this PR deliberately does NOT claim
That the Coolify app builds, starts, or serves anything, or that
0pt.appDNS points at it ratherthan at Vercel. Nobody probed the running site. All that is proven is that the webhook is
accepted with HTTP 200. Every corrected passage says exactly that, so this does not swap one
unverified claim for another — which is why
GOLDEN-STANDARD-REPORT.mdD4 moves from ❌ to ⚠️ andnot to ✅.
Files corrected
README.md— "Deploy" section. Original bullet struck, not deleted; a new bullet states themeasured state and the operational consequence ("know what you are shipping before you merge").
docs/02-architecture.md— the hostingSUPERSEDEDcallout.docs/architecture/decisions/0001-deploy-target-coolify-not-vercel.md— the consequence isannotated with an UPDATE block; the decision text is untouched. An ADR is a historical record,
so the original reads as "true when written 2026-08-09", not as current state.
docs/runbook/incident.md— the symptom row is now inverted. The old entry told an operator toexpect a red deploy job; the real hazard today is the opposite, an unintended deploy.
docs/compliance/GOLDEN-STANDARD-REPORT.md— D4 ❌ → ⚠️.docs/compliance/INVENTORY.md— the CI row's "deploy.yml röd men avsiktligt" claim.Verification
Docs only — no workflow files, no
.gitignore, no source. Falsifiable check over all six files(a stale claim present without its correction marker is red):
One honest note about the check itself: it first passed vacuously on
docs/02-architecture.md,because PR #32 had already placed the string
CORRECTED 2026-09-06in that file for an unrelatedreason. The marker was made specific so the check can actually fail there too — a gate that cannot
fall is worse than no gate.
node scripts/siax-doctor.mjs: 39/40, identical tomain(the one failure,P1-15 CODEOWNERS, ispre-existing and is what PR #19 supplies).
Generated with Claude Code
Six documents on main assert that no Coolify app has been provisioned for 0pt and that cd.yml's COOLIFY_APP_ID is a literal placeholder whose guard fails the job on purpose. That is no longer true, and the gap is not cosmetic: it means a merge to main silently ships to production while the repo's own docs promise it cannot. Measured, not assumed: * repo Actions secret COOLIFY_TOKEN exists, created 2026-08-31T11:34:34Z (GET /api/v1/repos/sax3l/0pt/actions/secrets) * repo Actions variable COOLIFY_APP_ID exists with a real 24-character value, not the "TODO-no-coolify-app-provisioned-yet" literal (GET /api/v1/repos/sax3l/0pt/actions/variables) * both guards in .gitea/workflows/cd.yml therefore pass; on the push to main at1ff34410(2026-09-06) the job "Deploy to Coolify / deploy" (run 11695, job 26295) finished SUCCESS with "coolify deploy HTTP 200" in its log Deliberately NOT claimed: that the Coolify app builds, starts or serves, or that 0pt.app DNS points at it rather than Vercel. Nobody probed the running site, and every corrected passage says so explicitly rather than replacing one unverified claim with another. Corrected: README.md "Deploy", docs/02-architecture.md hosting callout, docs/architecture/decisions/0001 (consequence annotated, decision untouched -- an ADR is a historical record), docs/runbook/incident.md (the symptom row is now inverted: the risk is an unintended deploy, not a red job), docs/compliance/GOLDEN-STANDARD-REPORT.md D4 (❌ -> ⚠️, not ✅, because "webhook accepted" is not "app serves"), docs/compliance/INVENTORY.md CI row. Falsifiable check over all six files (stale claim present without a correction marker = red): origin/main -> FAIL (12 findings) this branch -> PASS mutation: drop only the README bullet -> FAIL mutation: drop only the runbook correction -> FAIL The check initially passed vacuously on docs/02-architecture.md because PR #32 had already put the string "CORRECTED 2026-09-06" in that file for an unrelated reason; the marker was made specific so the check could actually fail there too. Docs only. No workflow files, no .gitignore, no source. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>Addendum — I then probed the running site, so the PR no longer hedges.
Probed 2026-09-06 08:00 UTC. So two things are true at the same time, and the docs now say both:
main(coolify deploy HTTP 200).0pt.appis dark — still pointed at Vercel, whose deployment isdisabled for non-payment. DNS was never cut over, so the Coolify app serves no
0pt.apptraffic.Neither fact cancels the other, and the earlier "unverified" wording would have let a reader assume
whichever one suited them.
Still not established, and still labelled as such in the docs: whether the Coolify app builds and
starts at all. A deploy endpoint returning 200 means the request was accepted, not that a container
came up healthy.
One detail worth carrying to #19: the
strict-transport-securityheader on that 402 is Vercel'splatform default, not the app's. That is precisely the "HSTS silently disappears on cutover to
Coolify/Traefik" risk that
next.config.mjsin #19 was written to remove — and it is unfixed onmaintoday.Head is now
853f093; CI re-running.Generated with Claude Code