fix(ci): provision FG_CI_TOKEN alongside FORGEGRAPH_TOKEN #605

Open
gmackie wants to merge 1 commit from fix/provision-fg-ci-token into main
Owner

Bob CI run 1194 went red on a single check while every substantive job passed:

ForgeGraph CI report rejected: HTTP 401

The publisher authenticated with secrets.FG_CI_TOKEN, but provisioning only
ever wrote FORGEGRAPH_TOKEN and FORGEGRAPH_REPOSITORY_ID. The two drifted:
provisioning kept refreshing the name the workflow does not read, while the
hand-set FG_CI_TOKEN aged out. verifyBearerToken enforces expiresAt on
fg_* tokens, so an aged deploy token is refused.

Why fix it here rather than in each workflow

A scan of */.forgejo/workflows across the fleet found 27 repos whose
workflows read FG_CI_TOKEN. Sixteen had already been migrated by hand to
${{ secrets.FORGEGRAPH_TOKEN }}. Ten were still on the stale name:

appealkey   driftport   filmroom   ForgeGraph   hypemarker
latchflow   leetcode    metro-code personalWebsite  test-dojo

None are failing yet, because their hand-set copies have not expired. They will.

Fixing that one workflow at a time is ten pull requests to write the same line,
and it leaves the trap in place for the next repo scaffolded from an older
template. Writing the token under both names fixes every one of them centrally,
on their next provision, with no workflow edits at all.

One token, two names

/api/fg/ci/report accepts any deploy-scoped token, so a workflow reading
either name authenticates as the same identity. The test asserts that
explicitly: three PUTs, and FG_CI_TOKEN's body must equal
FORGEGRAPH_TOKEN's. Minting twice would leave one unused and make the audit
trail lie about which credential a run actually used.

Verification

  • provision-ci-secrets.test.ts and provision-ci-access-unit.test.ts pass
    (5 tests). The call-count assertion moved from 2 to 3 and now checks the two
    token PUTs carry identical bodies.
  • Proven end to end before this change, by hand: pointing Bob's publisher at
    FORGEGRAPH_TOKEN and provisioning a fresh token turned run 1194's failing
    report job into run 1195's passing one. This change removes the need for that
    per-repo edit.

Scope note

The endpoint that accepts a deploy-scoped token is the CI report one. Routes
calling verifyBearerToken(req) without allowMachineScopes still reject
machine tokens, so a workflow hitting those (ForgeGraph's own ai-review, which
calls /api/fg/changesets) needs FG_API_TOKEN rather than either of these.
That workflow is separately broken and dormant: it has failed since 2026-07-01
inside actions/checkout with "some refs were not updated", which is not a
credential problem and is not addressed here.

🤖 Generated with Claude Code

Bob CI run 1194 went red on a single check while every substantive job passed: ``` ForgeGraph CI report rejected: HTTP 401 ``` The publisher authenticated with `secrets.FG_CI_TOKEN`, but provisioning only ever wrote `FORGEGRAPH_TOKEN` and `FORGEGRAPH_REPOSITORY_ID`. The two drifted: provisioning kept refreshing the name the workflow does not read, while the hand-set `FG_CI_TOKEN` aged out. `verifyBearerToken` enforces `expiresAt` on `fg_*` tokens, so an aged deploy token is refused. ## Why fix it here rather than in each workflow A scan of `*/.forgejo/workflows` across the fleet found **27 repos** whose workflows read `FG_CI_TOKEN`. Sixteen had already been migrated by hand to `${{ secrets.FORGEGRAPH_TOKEN }}`. Ten were still on the stale name: ``` appealkey driftport filmroom ForgeGraph hypemarker latchflow leetcode metro-code personalWebsite test-dojo ``` None are failing yet, because their hand-set copies have not expired. They will. Fixing that one workflow at a time is ten pull requests to write the same line, and it leaves the trap in place for the next repo scaffolded from an older template. Writing the token under both names fixes every one of them centrally, on their next provision, with **no workflow edits at all**. ## One token, two names `/api/fg/ci/report` accepts any deploy-scoped token, so a workflow reading either name authenticates as the same identity. The test asserts that explicitly: three PUTs, and `FG_CI_TOKEN`'s body must equal `FORGEGRAPH_TOKEN`'s. Minting twice would leave one unused and make the audit trail lie about which credential a run actually used. ## Verification - `provision-ci-secrets.test.ts` and `provision-ci-access-unit.test.ts` pass (5 tests). The call-count assertion moved from 2 to 3 and now checks the two token PUTs carry identical bodies. - Proven end to end before this change, by hand: pointing Bob's publisher at `FORGEGRAPH_TOKEN` and provisioning a fresh token turned run 1194's failing report job into run 1195's passing one. This change removes the need for that per-repo edit. ## Scope note The endpoint that accepts a deploy-scoped token is the CI report one. Routes calling `verifyBearerToken(req)` **without** `allowMachineScopes` still reject machine tokens, so a workflow hitting those (ForgeGraph's own `ai-review`, which calls `/api/fg/changesets`) needs `FG_API_TOKEN` rather than either of these. That workflow is separately broken and dormant: it has failed since 2026-07-01 inside `actions/checkout` with "some refs were not updated", which is not a credential problem and is not addressed here. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix(ci): provision FG_CI_TOKEN alongside FORGEGRAPH_TOKEN
All checks were successful
CI / gitleaks (pull_request) Successful in 7s
CI / storybook (pull_request) Successful in 1m26s
CI / web-build (pull_request) Successful in 3m24s
forgegraph/ci CI passed
CI / ci (pull_request) Successful in 11m46s
058f3cb052
Bob CI run 1194 went red on one check while every real job passed:

    ForgeGraph CI report rejected: HTTP 401

The publisher authenticated with secrets.FG_CI_TOKEN, but provisioning only
ever wrote FORGEGRAPH_TOKEN and FORGEGRAPH_REPOSITORY_ID. The two drifted:
provisioning kept refreshing the name the workflow does not read, while the
hand-set FG_CI_TOKEN aged out. verifyBearerToken enforces expiresAt on fg_*
tokens, so an aged deploy token is refused.

A scan of the fleet found 27 repos whose workflows read FG_CI_TOKEN. Sixteen
had already been migrated by hand to ${{ secrets.FORGEGRAPH_TOKEN }}; ten were
still on the stale name -- appealkey, driftport, filmroom, ForgeGraph,
hypemarker, latchflow, leetcode, metro-code, personalWebsite, test-dojo. None
were failing yet, because their hand-set copies have not expired. They will.

Fixing that one workflow at a time is ten pull requests to write the same line,
and it leaves the trap in place for the next repo scaffolded from an older
template. Writing the token under both names fixes every one of them centrally,
on their next provision, with no workflow edits at all.

It is one token, not two. /api/fg/ci/report accepts any deploy-scoped token, so
a workflow reading either name authenticates as the same identity. The test
asserts that explicitly: three PUTs, and FG_CI_TOKEN's body must equal
FORGEGRAPH_TOKEN's. Minting twice would leave one unused and make the audit
trail lie about which credential a run used.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Author
Owner

Preview environment is live: https://pr-605-forgegraph.forgegraf.com

Deployed 058f3cb0 with the beta stage's environment. It redeploys on every push and is destroyed when this PR closes.

Preview environment is live: https://pr-605-forgegraph.forgegraf.com Deployed `058f3cb0` with the beta stage's environment. It redeploys on every push and is destroyed when this PR closes.
All checks were successful
CI / gitleaks (pull_request) Successful in 7s
Required
Details
CI / storybook (pull_request) Successful in 1m26s
CI / web-build (pull_request) Successful in 3m24s
forgegraph/ci CI passed
CI / ci (pull_request) Successful in 11m46s
Required
Details
This pull request can be merged automatically.
This branch is out-of-date with the base branch
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin fix/provision-fg-ci-token:fix/provision-fg-ci-token
git switch fix/provision-fg-ci-token
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
gmackie/ForgeGraph!605
No description provided.