Provision GITHUB_TOKEN for the dispatch broker so warded github.com/... runs are hands-off #441
Labels
No labels
burndown-2026-06
coherence-core
consult
headless
interactive
P0
P1
P2
P3
P4
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
coilyco-flight-deck/infrastructure#441
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Why
ward's GitHub lane (ward#489) reads the token from env only (
WARD_GITHUB_TOKEN/GH_TOKEN/GITHUB_TOKEN), with no SSM fallback by design (keeps ward honestly publishable, aligns with ward#441 / ward#453). So awarded github.com/owner/repo#Nrun only works if the token is present in the dispatch environment. Verified from the director surface: the broker environment currently carries none of the three, so a GitHub-target run fails fast at dispatch. This is what blocks autonomous GitHub PRs today (the homebrew-tap edit, the aos mirror, any github-target run).The constraint that shapes this: agents leak creds
ward stays env-only (zero baked cred path). The broker provides the token. But the agent-facing GitHub credential must be leak-resilient: observed on the github-native setup, agents leaked creds several times a week. So a long-lived static personal PAT is rejected - it is the worst thing to leak (durable, Kai's own identity,
reposcope = all repos). The Forgejo side is easier to live with because its token is a bot identity (coilyco-ops), rotates in one SSM update, and sits behind AWS + private infra - none of which a static GitHub PAT has.Preferred sturdy path: a GitHub App with ephemeral installation tokens
The GitHub-native way to get the FJ bot's three properties plus ephemerality:
coilyco-ops), installed on the target repos with least-privilege permissions.GITHUB_TOKENfor the run.This is strictly better than a PAT on every axis, and better than the current FJ token too (long-lived-in-SSM vs ephemeral-by-construction). Same pattern could later harden the FJ side if it grows equivalent minted tokens.
Convenience fallback:
gh auth tokenfast path (attended local runs only)For attended, local, hands-on-keyboard runs, the broker can call
gh auth tokenfrom Kai's existing hostghlogin and export it. Simple, nothing to store.gh auth tokenreuses Kai's personal login, so as an agent-facing cred it hands out a durable personal token - the exact leak risk above. Fine for attended local use, not the fleet default, and not to be backed by a static long-lived PAT. Separately, Kai gets a dailyghre-login prompt (worth fixing for her own use viagh auth statusto find the source), but the fix is not a static PAT.Recommendation
Ship the GitHub App + ephemeral installation token path as the fleet default (leak-resilient, hands-off, bot identity). Keep the
gh auth tokenfast path only as an attended-local convenience. The App key is the single durable secret and it stays in SSM, never in an agent's env.Related
infrastructure#440 (fleet installs the tap from the fj mirror), ward#489 (the GitHub forge lane + docs/agent-github.md), ward#441 / ward#453 (why GitHub auth is user-side, never SSM-baked into ward).
Filed from the read-only director surface (she/her). Updated after Kai rejected a static PAT (agents leak creds) in favor of ephemeral App-minted tokens.
🔒 Reserved by
ward agent --driver claude— containerengineer-claude-infrastructure-441on hostkais-macbook-pro-2.localis carrying this issue (reserved 2026-07-02T17:59:45Z). Concurrentward agentruns are blocked until it finishes or the reservation goes stale (2h0m0s TTL);--forceoverrides.— Claude (she/her), via
ward agentWARD-OUTCOME: done - broker can now mint short-lived, repo-scoped GitHub App installation tokens (
ward exec github-app-token repo=owner/name), landed on main, CI green.The shape came together fast once I found the repo's own idiom for this:
provision-coilyco-ops-bot.shpluscoilyco_ops_summon.pyreading a bot token from SSM. I mirrored that for a GitHub App instead of a Forgejo bot. The nicest surprise was thatcryptographyis already in the lockfile (transitive via ansible), so I could RS256-sign the App JWT in memory with zero new deps and no PyJWT, and the PEM never touches disk on the broker. The token is single-repo-scoped and ~1h, so a leak expires itself.What fought back was the environment, not the code: the shared host disk sat at 100% the whole run, fluctuating between 0 and ~70MB free, so pre-commit could never build its isolated hook envs. I validated every catalog hook by hand against the substrate validators (all green for my files), ran repo-wide pylint (10.00/10) and trufflehog (clean), then committed with only the un-buildable remote hooks skipped and pylint enforced live. CI then ran the canonical suite on the push and passed, which is the real backstop.
Confidence is high on the code: 13 unit tests including a full RS256 sign-plus-verify roundtrip, and CI is green. The one thing not exercised end-to-end is the live GitHub API path (installation resolve plus mint), because no App is registered yet and there was no network target to hit.
Follow-ups worth filing: (1) register the App by hand and run
provision-github-app.shto stash the id + PEM in SSM, picking the exact repo set to install it on (homebrew-tap, the aos mirror); (2) wire the one-lineexport GITHUB_TOKEN="$(ward exec github-app-token repo=...)"into ward's broker/director dispatch on its side, since that half lives in the ward repo, not here.