From cd5d7138d9f6b7c4469f5bfdd2ec680e094ded0f Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 5 Aug 2026 21:28:28 +0200 Subject: [PATCH] Dashboard reads COOLIFY_READ_TOKEN, and the name is the point The owner issued a read-only Coolify token, which is the right shape: this dashboard only ever performs GETs. Renaming the variable from COOLIFY_TOKEN to COOLIFY_READ_TOKEN means the requirement is encoded where someone setting it will see it, rather than living only in a doc. A write-capable token here would mean a PocketBase vulnerability could deploy or stop every app on the PaaS. coolify.sh keeps using the full token for deploys; having both in secrets/amber.env is the separation, not redundancy. Verified the read token against both endpoints the dashboard needs (applications list, container logs) before wiring it, and validated the renamed variable on a clean local boot: 7 services enumerated, both panels configured, no errors. --- pb_hooks/status.pb.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/pb_hooks/status.pb.js b/pb_hooks/status.pb.js index e04726f..b09a390 100644 --- a/pb_hooks/status.pb.js +++ b/pb_hooks/status.pb.js @@ -28,6 +28,11 @@ // **The Coolify token never reaches the browser.** That is the entire reason this // endpoint exists rather than the page calling Coolify directly. // +// The variable is COOLIFY_**READ**_TOKEN, and the name is the point: this dashboard +// only ever performs GETs, so it must never be handed the write-capable token that +// `coolify.sh` uses for deploys. A full token here would mean a PocketBase +// vulnerability could deploy or stop every app on the PaaS. +// // NOTE: PocketBase runs each routerAdd handler in its own isolated JSVM, so // anything a handler needs must be declared INSIDE it — file-scope helpers throw // ReferenceError at request time. Nothing is hoisted here; keep it that way. @@ -134,11 +139,11 @@ routerAdd("GET", "/api/status", (e) => { // above still render on a box where these env vars were never set. const coolify = safe("coolify", () => { const base = env("COOLIFY_URL") - const token = env("COOLIFY_TOKEN") + const token = env("COOLIFY_READ_TOKEN") if (!base || !token) { return { configured: false, - why: "COOLIFY_URL / COOLIFY_TOKEN are not set on this container — add them " + + why: "COOLIFY_URL / COOLIFY_READ_TOKEN are not set on this container — add them " + "in Coolify → amber-backend → Environment Variables and redeploy.", } } @@ -211,9 +216,9 @@ routerAdd("GET", "/api/status/logs", (e) => { if (!(lines > 0) || lines > 1000) lines = 200 const base = $os.getenv("COOLIFY_URL") - const token = $os.getenv("COOLIFY_TOKEN") + const token = $os.getenv("COOLIFY_READ_TOKEN") if (!base || !token) { - return e.json(503, { error: "COOLIFY_URL / COOLIFY_TOKEN not set on this container" }) + return e.json(503, { error: "COOLIFY_URL / COOLIFY_READ_TOKEN not set on this container" }) } try { const res = $http.send({