From 2c40b0905960a567949f779b4dca372cc038576b Mon Sep 17 00:00:00 2001 From: Matt Miller Date: Tue, 28 Jul 2026 14:43:54 -0700 Subject: [PATCH 1/3] ci: dispatch to cloud with an App token instead of a PAT The PAT behind CLOUD_REPO_DISPATCH_TOKEN was regenerated without the Actions secret being updated, so the stored copy went stale. Every tag from v0.25.0 through v0.29.0 returned 401 and opened no cloud bump PR, which went unnoticed for six weeks because the bumps were filed by hand. Mint an installation token per run instead, scoped to Comfy-Org/cloud, so there is no stored credential left to drift or expire. --- .github/workflows/tag-dispatch-cloud.yml | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tag-dispatch-cloud.yml b/.github/workflows/tag-dispatch-cloud.yml index 53a0e91d6..84c529441 100644 --- a/.github/workflows/tag-dispatch-cloud.yml +++ b/.github/workflows/tag-dispatch-cloud.yml @@ -8,16 +8,36 @@ on: jobs: dispatch-cloud: runs-on: ubuntu-latest + # No checkout and no use of GITHUB_TOKEN; the dispatch goes out on the App + # token generated below, which is scoped to Comfy-Org/cloud only. + permissions: + contents: read steps: + # A PAT was used here previously. It was silently regenerated upstream + # without the Actions secret being updated, so every tag from v0.25.0 + # (2026-06-16) through v0.29.0 dispatched a 401 and no cloud bump PR was + # opened for six weeks. App installation tokens are minted per run and + # cannot drift out of sync with a stored copy. + - name: Generate GitHub App token + id: app-token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 + with: + app-id: ${{ secrets.FEN_RELEASE_APP_ID }} + private-key: ${{ secrets.FEN_RELEASE_PRIVATE_KEY }} + # Cross-repo dispatch: without these the token is scoped to this + # repository and the POST to cloud would 403. + owner: Comfy-Org + repositories: cloud + - name: Send repository dispatch to cloud env: - DISPATCH_TOKEN: ${{ secrets.CLOUD_REPO_DISPATCH_TOKEN }} + DISPATCH_TOKEN: ${{ steps.app-token.outputs.token }} RELEASE_TAG: ${{ github.ref_name }} run: | set -euo pipefail if [ -z "${DISPATCH_TOKEN:-}" ]; then - echo "::error::CLOUD_REPO_DISPATCH_TOKEN is required but not set." + echo "::error::App token generation produced an empty token." exit 1 fi From fefe6420a7f6774fdeb739ed3d2638f230f1439b Mon Sep 17 00:00:00 2001 From: Matt Miller Date: Tue, 28 Jul 2026 14:47:08 -0700 Subject: [PATCH 2/3] ci: use cloud-code-bot for the dispatch token FEN_RELEASE_* are environment secrets on the 'backport release' environment, which is gated on required reviewers. A tag dispatch must not wait on a human approval, so that environment is not usable here. cloud-code-bot already authors the bump PRs on the cloud side, so its write access to Comfy-Org/cloud is established. --- .github/workflows/tag-dispatch-cloud.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tag-dispatch-cloud.yml b/.github/workflows/tag-dispatch-cloud.yml index 84c529441..e446ee4cf 100644 --- a/.github/workflows/tag-dispatch-cloud.yml +++ b/.github/workflows/tag-dispatch-cloud.yml @@ -22,10 +22,14 @@ jobs: id: app-token uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 with: - app-id: ${{ secrets.FEN_RELEASE_APP_ID }} - private-key: ${{ secrets.FEN_RELEASE_PRIVATE_KEY }} - # Cross-repo dispatch: without these the token is scoped to this - # repository and the POST to cloud would 403. + # cloud-code-bot is the same App the cloud receiver already runs on: + # it authored the bump PRs, so its write access there is established + # rather than assumed. + app-id: ${{ vars.CLOUD_CODE_BOT_APP_ID }} + private-key: ${{ secrets.CLOUD_CODE_BOT_PRIVATE_KEY }} + # Cross-repo dispatch: create-github-app-token scopes to the current + # repository by default, so the org installation must be named + # explicitly or the POST to cloud would 403. owner: Comfy-Org repositories: cloud From c7e4ddfb592ccbf8df96ffff9a6a5a1bc00258be Mon Sep 17 00:00:00 2001 From: Matt Miller Date: Tue, 28 Jul 2026 14:53:22 -0700 Subject: [PATCH 3/3] ci: scope the dispatch App to cloud only An App private key is not scoped: it can mint tokens for every repo its App is installed on, with the App's full entitlement. The owner and repositories inputs are step arguments, not a boundary. cloud-code-bot is entitled to cloud's production release and migration workflows, so storing its key in this public repo would join the two repos' trust boundaries at the weaker one. Use a single-purpose App holding only contents:write on Comfy-Org/cloud instead -- the same minimal scope the outgoing PAT had, without the silent expiry. --- .github/workflows/tag-dispatch-cloud.yml | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/.github/workflows/tag-dispatch-cloud.yml b/.github/workflows/tag-dispatch-cloud.yml index e446ee4cf..7b0f6719e 100644 --- a/.github/workflows/tag-dispatch-cloud.yml +++ b/.github/workflows/tag-dispatch-cloud.yml @@ -22,14 +22,18 @@ jobs: id: app-token uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 with: - # cloud-code-bot is the same App the cloud receiver already runs on: - # it authored the bump PRs, so its write access there is established - # rather than assumed. - app-id: ${{ vars.CLOUD_CODE_BOT_APP_ID }} - private-key: ${{ secrets.CLOUD_CODE_BOT_PRIVATE_KEY }} - # Cross-repo dispatch: create-github-app-token scopes to the current - # repository by default, so the org installation must be named - # explicitly or the POST to cloud would 403. + # A single-purpose App: installed only on Comfy-Org/cloud, holding + # only contents:write. Deliberately NOT cloud-code-bot, which is also + # entitled to cloud's production release and migration workflows -- + # an App private key is not scoped, so any key stored here can mint a + # token for everything its App is entitled to, on every repo it is + # installed on. The owner/repositories inputs below are arguments, + # not a security boundary. Keeping this App minimal is what makes it + # safe to hold in a public repository. + app-id: ${{ vars.CORE_TO_CLOUD_DISPATCH_APP_ID }} + private-key: ${{ secrets.CORE_TO_CLOUD_DISPATCH_PRIVATE_KEY }} + # create-github-app-token scopes to the current repository by + # default; this dispatch targets another repo. owner: Comfy-Org repositories: cloud