diff --git a/AGENTS.md b/AGENTS.md index fa40d97..0a2ef08 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -148,11 +148,46 @@ Gitea API interaction from inside the container — inspecting CI runs, checking published tags, listing commits — e.g. `curl -H "Authorization: token $GITEA_ACCESS_TOKEN" "$GITEA_HOST/api/v1/repos/joakimp/opencode-devbox/actions/runs?limit=5"`. Prefer this over a short-lived PAT file when the env token is present (the -`ci-release-watcher` skill auto-detects it). Public-repo GET listings work -unauthenticated (see the `resolve-versions` mempalace-toolkit note above), so -the token matters mainly for private repos or rate-limit headroom; its -lifecycle is host-managed, so there is nothing to revoke after use. Never -echo the token value (including into logs). +`ci-release-watcher` skill auto-detects it). Never echo the token value +(including into logs). + +**Gotcha — "public repos need no token" does NOT extend to the Actions API.** +Repo metadata, tags, and commit listings on a public repo are readable +unauthenticated (measured 2026-08-15 on `gitea.jordbo.se`: `GET /repos/{o}/{r}`, +`/tags`, `/commits?limit=1&sha=main` → all `200`; this is what the +`resolve-versions` mempalace-toolkit note above relies on). But **every +`/actions/*` endpoint returns `401 {"message":"token is required"}` even when +`private=false`** — including `/actions/runs`, `/actions/runs//jobs`, and +`/actions/jobs//logs`. So the token is *mandatory* for exactly the use case +this section opens with (inspecting CI runs), not merely "nice for private repos +or rate-limit headroom". Cost a wasted detour once: `repo → 200` was read as +"public, so CI is readable", which it is not. + +**Gotcha — the env token can be silently REVOKED, and renewing the host `.env` +does not reach a running container.** The container's environment is baked at +**start**, so a host-side token renewal is invisible until the container is +recreated (or the value is re-read explicitly over SSH). Worse, a revoked token +looks identical to a valid one — the failure only shows up as `401` per request. +Verify before concluding anything about a repo's visibility: +`curl -s -o /dev/null -w '%{http_code}' -H "Authorization: token $GITEA_ACCESS_TOKEN" "$GITEA_HOST/api/v1/version"` → `200` means the token +itself is alive. To tell staleness from revocation, compare fingerprints rather +than values: `printf '%s' "$GITEA_ACCESS_TOKEN" | sha256sum` against +`shasum -a 256` of the token in the host's compose `.env` — identical +fingerprints that both `401` mean the token is revoked upstream, not stale in +the container. + +**Workaround — with no usable token, Docker Hub tags localise a CI failure.** +The job graph makes registry state a progress oracle, because `smoke` *gates* +`build-variant` and `build-variant` gates `promote-base-latest`. Read +`https://hub.docker.com/v2/repositories///tags?page_size=100` (public, +no auth) and compare timestamps: a fresh `base-` with **no** `vX.Y.Z` tag +and `latest` still on the previous release pinpoints "base built, smoke failed, +nothing shipped" without reading a single log line. Diagnosed v1.8.0 this way +before a token was available; the log later confirmed it exactly. A second free +deduction from the same graph: since `smoke`'s `if:` requires +`needs.resolve-versions.result == 'success'`, a *failed* smoke proves +`GITEA_BUILD_TOKEN` was alive in CI — a dead build token would have **skipped** +smoke (via `require_sha`), not failed it. **Gotcha — the jobs endpoint takes the internal `id`, NOT the `run_number` the UI shows as `#239`.** The two diverge widely (`id=534` was `run_number=238`),