Raxx · internal docs

internal · gated ↑ index

Agent GitHub identity

Status: in flight (App provisioning complete; agent dispatch wiring this PR) Tracking: issue #335 Owner: ops

Why

When dispatched agents (feature-developer, sre-agent, card-groomer, etc.) make GitHub API calls, push commits, or open PRs/issues, every action authors as the operator (MooseQuest). That makes activity ambiguous on a busy day — was that PR comment from Kristerpher or from the security agent? — and pollutes the operator's notification feed with bot self-traffic.

This design replaces the operator's PAT with three GitHub Apps acting as bot identities, scoped per agent class.

The three bots

Bot Used by Permissions
raxx-dev-bot feature-developer, ux-polisher, ux-designer Contents R/W, PRs R/W, Issues R/W, Workflows R/W
raxx-ops-bot sre-agent, security-agent, card-groomer Contents R, PRs R/W, Issues R/W, Code/Secret scanning alerts R/W
raxx-pm-bot product-manager, software-architect, marketing-strategist, business-legal-researcher, data-scientist Contents R/W, PRs R/W, Issues R/W, Discussions R/W

Mapping is canonical at scripts/agents/agent_bot_map.yaml.

Token-mint flow

[App ID] + [Installation ID] + [Private key PEM]   ← from Infisical
                  ↓
       Sign a 9-min JWT (RS256, iss = App ID)
                  ↓
   POST /app/installations/{id}/access_tokens
                  ↓
       Installation token (1-hour validity)
                  ↓
       Set GH_TOKEN, run gh CLI / git push

Implemented at scripts/agents/mint_github_token.py.

Secrets storage

Single source of truth: Infisical at /MooseQuest/<bot-name>/ (Infisical project MooseQuest; this path is independent of the GitHub org name). Three secrets per bot:

No local secret cache. Bot keys never live on the operator's disk. The mint script pulls them live from Infisical on every invocation via Universal Auth.

The only thing in the operator's shell environment is the Machine Identity that authenticates to Infisical:

This decouples bot key rotation from operator-workstation state. Rotating a bot's PEM in Infisical takes effect on the next agent dispatch — no local file update, no re-source-of-config, no operator-side action.

Trade-off: each gh call routed through the wrapper takes ~500ms–1s (Infisical login + secrets fetch + GitHub token mint). For agents making ≥3 gh calls in a row, the session pattern (eval "$(... --export)") caches the resulting GitHub token for its 1-hour lifetime, amortizing the network cost.

Setup runbook: docs/ops/runbooks/agent-bot-tokens-setup.md.

Agent dispatch pattern

Two patterns are supported:

A. Single command (preferred)

scripts/agents/with_bot_token.sh raxx-dev-bot gh pr create --title "..."

The wrapper mints a fresh token, sets GH_TOKEN for one command, runs the command, and discards the token. Falls back to the operator's PAT if the mint fails (logged warning to stderr).

B. Session-scoped

eval "$(python scripts/agents/mint_github_token.py --bot raxx-ops-bot --export)"
gh issue create ...
gh pr comment ...

GH_TOKEN stays in the shell environment for ≤1 hour. Use this when an agent makes ≥3 gh calls in quick succession.

Per-agent integration

Each agent's .claude/agents/<agent>.md system prompt includes a 5-line "GitHub identity" preamble pointing to its bot. Agents are responsible for using the wrapper or session pattern before any gh CLI call.

Failure modes + fallback

The mint script can fail in three ways:

  1. Missing env vars (exit 2) — Infisical secrets not pulled. Operator runs the dispatcher with proper Infisical context.
  2. GitHub API rejection (exit 3) — App credentials wrong, App revoked, scope mismatch. Cause is in the response body. Falls through to the operator PAT (with warning) so the agent can still complete its task — surface the warning so the operator notices.
  3. Network failure — Same as #2; agent gets a warning + falls back to PAT.

Never silently use the operator PAT without a logged warning. Auditability beats convenience.

Git commit attribution (deferred)

Bot identities for GitHub API calls ship in this PR. Bot identities for commit author require setting git config user.name "raxx-dev-bot[bot]" + user.email "<bot-user-id>+raxx-dev-bot[bot]@users.noreply.github.com" per-worktree. That requires looking up the bot's user ID after install (gh api /users/raxx-dev-bot%5Bbot%5D) and is tracked as a follow-up. v1 ships with API-call attribution only; commit attribution stays with the operator.

Rotation

Private keys rotate per the existing SOP at docs/ops/runbooks/rotation/github-app-installation-token.md. Cadence: 365 days per bot. The rotation pipeline (#300, #331) will eventually surface this in the console rotation UI.

GitHub org migration checklist

When the GitHub organization is renamed or the repo is transferred to a new org, three things change and must be remediated in order:

  1. App installations do not migrate automatically. GitHub App installations are scoped to an org. After an org rename or ownership transfer, every App must be re-installed on the new org. The App definition (App ID + private key) stays intact; only the installation (and its ID) changes.
  2. Installation IDs change. Each re-installation on the new org creates a new installation ID. The old ID returns 404 from POST /app/installations/{old_id}/access_tokens. Update INSTALLATION_ID in Infisical at /MooseQuest/<bot-name>/ for all three bots.
  3. Provisioning runbook URLs must be updated. Any hardcoded github.com/organizations/<old-org>/ links in runbooks need to point to the new org. See docs/ops/runbooks/github-app-provisioning.md.

Remediation procedure for org migration

  1. In the GitHub UI (https://github.com/organizations/raxx-app/settings/apps), confirm the three Apps (raxx-dev-bot, raxx-ops-bot, raxx-pm-bot) appear. If not, re-provision from scratch per docs/ops/runbooks/github-app-provisioning.md.
  2. For each App: go to Install App → install on the new org → select TradeMasterAPI → note the new Installation ID from the URL.
  3. Update INSTALLATION_ID in Infisical at /MooseQuest/raxx-dev-bot/, /MooseQuest/raxx-ops-bot/, /MooseQuest/raxx-pm-bot/.
  4. Smoke-test: scripts/agents/with_bot_token.sh raxx-ops-bot gh api /user — expect "login": "raxx-ops-bot[bot]".
  5. Verify token format starts with ghs_ (installation token), not ghp_ (PAT).

Vault path

Secrets live at /MooseQuest/<bot-name>/ in Infisical (Infisical project MooseQuest). The /MooseQuest/ prefix is the vault folder hierarchy and is independent of the GitHub org name — it does not need to change when the GitHub org is renamed.

The mint script reads INFISICAL_PATH_PREFIX from env (default /MooseQuest/). The effective secret path is {prefix}/{bot}, e.g. /MooseQuest/raxx-dev-bot.

Refs