Skip to content

ci: publish to batch-archive with a scoped bee-runner App token - #17

Open
darkobas2 wants to merge 2 commits into
mainfrom
ci/batch-sync-app-token
Open

ci: publish to batch-archive with a scoped bee-runner App token#17
darkobas2 wants to merge 2 commits into
mainfrom
ci/batch-sync-app-token

Conversation

@darkobas2

Copy link
Copy Markdown
Contributor

The first real run of Batch Sync (run 33394634626) failed at Checkout batch-archive:

Error: Input required and not supplied: token

GHA_PAT_BASIC is an org secret with selected visibility and this repo was never added to its list, so ${{ secrets.GHA_PAT_BASIC }} resolved to an empty string.

Why not just share the PAT

That PAT carries contents:write across 16 repos and is tied to a personal account. The bee-runner GitHub App (app_id 84297) is already installed org-wide with contents: write, and is the established pattern here — see ethersphere/bee/.github/workflows/swarm-cli-bee-version.yaml. A minted App token is scoped to one repo, narrowed to one permission, short-lived, and rotatable without touching anyone's account.

Changes

Before After
Checkout batch-archive used GHA_PAT_BASIC uses github.token with persist-credentials: false — batch-archive is public, so the clone needs no elevated token
push used the checkout-persisted credential pushes with an App token minted right before Publish

The token is minted after the export rather than at checkout: App tokens expire in ~1h and the export can run far longer, so minting early would leave the final push holding an expired credential. As a side effect no write-capable credential exists on disk for the length of the run.

Also configured (outside this diff)

  • batch-export added to the bee-runners runner group — jobs were queueing forever with no eligible runner, which is what stalled the run for 2h53m before it was cancelled.
  • BEE_RUNNER_KEY shared with this repo (BEE_RUNNER_APP_ID was already visibility all).
  • PRIVATE_GNOSIS_RPC_URL set as a repo secret. GNOSIS_RPC_USER / GNOSIS_RPC_PASSWORD remain unset — the self-hosted bee runners are inside the RPC IP allowlist, so Compose endpoint takes the unauthenticated path.

The GHA_PAT_BASIC org secret was never shared with this repo, so
`Checkout batch-archive` resolved its token to empty and failed with
"Input required and not supplied: token".

Rather than widen that PAT (contents:write across 16 repos, tied to a
personal account), mint a token from the bee-runner GitHub App, which is
already installed org-wide with contents:write. The token is scoped to
batch-archive alone, narrowed to contents:write, and expires in ~1h.

Because of that expiry it is minted after the export, not at checkout —
the export can easily outrun an hour. batch-archive is public, so the
initial clone needs no App token at all; it uses GITHUB_TOKEN with
persist-credentials: false so nothing long-lived sits in .git/config
while the export runs, and the final push carries the App token itself.
@gacevicljubisa
gacevicljubisa self-requested a review August 31, 2026 16:41

@gacevicljubisa gacevicljubisa left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@darkobas2 this works — I triggered Batch Sync from this branch and it completed successfully (run 33431048291), so the App-token flow is doing its job and the scoping looks right.

Two things below that could be improved later. Neither needs to hold up this PR.

# Minted here, not at checkout: an App token lives ~1h and the export
# above can outrun that. permission-contents keeps it to what the push
# needs, and repositories keeps it off every other repo in the org.
- name: Generate token for batch-archive

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minting the token here rather than at checkout makes sense given the ~1h token lifetime, but it does move credential failures to the end of the run. Previously a bad credential failed at Checkout batch-archive within seconds — that is how the empty GHA_PAT_BASIC got caught quickly. Now, if the App loses contents:write or the key visibility changes, it surfaces after an export that can run up to the full 120-minute timeout.

That is worse than just lost time: snapshot.ndjson.gzip only exists in the runner workspace, and the next run's git clean -ffdx wipes it, so the retry re-exports everything from scratch.

Two cheap options if you want to keep the late mint:

  • a pre-flight before Export that mints and discards a token — it validates the installation and the permission in a couple of seconds;
  • an actions/upload-artifact with if: failure() so a failed publish does not cost the whole export.

Worth noting the same loss applies to a non-fast-forward rejection on the final push.

Comment thread .github/workflows/batch-sync.yml Outdated
# orphaned tag can become a later run's resume point.
git push --atomic origin HEAD:main "refs/tags/${new_tag}"
git push --atomic \
"https://x-access-token:${ARCHIVE_TOKEN}@github.com/ethersphere/batch-archive.git" \

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small hardening idea: passing the token inside the URL puts it in git's argv, so while the push runs it is readable from ps -eo args or /proc/<pid>/cmdline by anything else on the host. With runs-on: [self-hosted, linux, bee] those are long-lived shared machines rather than ephemeral VMs, so another job on the same runner could read it and use it for contents:write on batch-archive until the post step revokes it.

Keeping the value in the environment avoids that:

git -c credential.helper= \
    -c credential.helper='!f(){ echo username=x-access-token; echo "password=${ARCHIVE_TOKEN}"; };f' \
    push --atomic origin HEAD:main "refs/tags/${new_tag}"

The single quotes are what keep ${ARCHIVE_TOKEN} off the command line. As a side benefit the push can stay on origin instead of a hardcoded github.com/ethersphere/batch-archive.git, which is currently the third literal copy of the repo slug in this file and the one place a GHES/mirror setup would break.

Addresses both review comments on #17.

Minting the publish token after the export means an App misconfiguration
surfaces up to 120 minutes in, where the empty GHA_PAT_BASIC failed in
seconds. Mint once at the top of the job purely as a check; the token is
never used and is revoked by the post step.

Passing the token in the remote URL puts it in git argv, readable via
ps -eo args or /proc/<pid>/cmdline. On the self-hosted bee runners those
are long-lived shared machines, so a co-tenant job could lift it and hold
contents:write on batch-archive until revocation. Read it from the
environment through a credential helper instead.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants