GitHub Bot Identity — Distinct Identities for AI Agents
Date: 2026-03-03 Status: Research complete — interim solution deployed, long-term pending
Problem Statement
The SecurityV0 project runs multiple Claude Code agents (Alpha, Delta) that interact with GitHub — creating PRs, posting reviews, commenting on issues. All agents currently authenticate with the same Ivan-Fn personal access token.
This creates three problems:
- Attribution ambiguity. A review posted by Alpha looks identical to a PR created by Delta and to a comment left by Ivan directly. No one can tell who posted what without reading the content carefully.
- Audit trail gaps. GitHub's audit log shows all actions as
Ivan-Fn. If a bot posts something incorrect, there's no way to trace it to the responsible agent without checking timestamps against bot logs. - Self-review risk. Alpha reviews PRs that Delta creates, but GitHub sees both as
Ivan-Fn— meaning Alpha technically appears to approve its own team's PRs under the same identity.
Options Evaluated
1. GitHub Apps (one per agent)
Register a GitHub App per agent in the SecurityV0 org. Each app gets its own name, logo, and [bot]-badged identity.
| Aspect | Detail |
|---|---|
| Identity | sv0-alpha[bot], sv0-delta[bot] — distinct, clearly non-human |
| PR reviews | Full support: approve, request changes, comment |
| Cost | Free — app bot users do not consume GitHub seats |
| Token lifetime | 1-hour installation tokens, auto-rotated |
| Rate limit | 5,000 req/hr (standard) or 15,000 (enterprise) |
| Setup | Medium — register app, generate private key, write JWT→token exchange logic, install on repos |
| ToS | Fully supported and officially recommended by GitHub |
How it works:
- Register a GitHub App (e.g., "sv0-alpha") in org settings — set name, logo, permissions (
pull_requests: write,contents: read,issues: write) - Generate a private key (
.pemfile), store as a secret - Install the app on target repos (or org-wide)
- At runtime: sign a short-lived JWT with the private key, exchange for a 1-hour installation access token via GitHub API
- Use that token in API calls (
GH_TOKEN=<installation-token> gh pr review ...)
Libraries:
- Node.js:
@octokit/auth-app - Python:
PyGithub/githubkit - CLI:
tibdex/github-app-tokenGitHub Action for CI contexts
2. Machine User Accounts (separate GitHub accounts)
Create dedicated GitHub accounts (e.g., sv0-alpha-bot, sv0-delta-bot) and generate PATs from each.
| Aspect | Detail |
|---|---|
| Identity | Any username and avatar — looks like a regular user (no [bot] badge) |
| PR reviews | Full support |
| Cost | Free for first machine account per person; second may need a paid seat |
| Token lifetime | Long-lived PAT (until revoked) |
| Rate limit | 5,000 req/hr |
| Setup | Low — create account, generate PAT, store as secret |
| ToS | Permitted with limits: one machine account per human. Two bots owned by one person requires a paid seat for the second. |
Key risk: GitHub ToS explicitly states accounts must be set up by a human with a valid email, and limits one free machine account per person. Running Alpha and Delta under one person's ownership may require paid seats.
3. Built-in GITHUB_TOKEN (GitHub Actions)
Use the auto-injected GITHUB_TOKEN in CI workflows.
| Aspect | Detail |
|---|---|
| Identity | Always github-actions[bot] — cannot customize |
| PR reviews | Requires opt-in setting; cannot self-approve |
| Cost | Free |
| Setup | Near zero |
| ToS | Fully supported |
Not viable for our use case. All agents would appear as the same github-actions[bot] identity — does not solve the attribution problem. Also only works within GitHub Actions, not from local Claude Code sessions.
4. PAT on Personal Account (current state)
Use Ivan's PAT for all bot operations.
| Aspect | Detail |
|---|---|
| Identity | Ivan-Fn — indistinguishable from human activity |
| PR reviews | Full support |
| Cost | Free |
| Setup | Already done |
| ToS | No concerns |
Not viable long-term. This is the problem we're solving.
Comparison Matrix
| Dimension | GitHub Apps | Machine Users | GITHUB_TOKEN | PAT (current) |
|---|---|---|---|---|
| Distinct identities per bot | Yes | Yes | No | No |
[bot] badge on GitHub | Yes | No | Yes (generic) | No |
| Free at our scale | Yes | Maybe (ToS limits) | Yes | Yes |
| Short-lived tokens | Yes (1 hr) | No (long-lived) | Yes (per-run) | No |
| Works from local CLI | Yes | Yes | No (CI only) | Yes |
| Setup effort | Medium | Low | N/A | Done |
| ToS risk | None | Moderate | None | None |
| Granular permissions | Yes (per-app) | No (PAT scope) | Yes (per-repo) | No |
Recommendation
Long-term: GitHub Apps. One app per agent.
| App | Identity | Permissions |
|---|---|---|
sv0-alpha | sv0-alpha[bot] | pull_requests: write, issues: write, contents: read |
sv0-delta | sv0-delta[bot] | pull_requests: write, issues: write, contents: write |
Rationale:
- Free, no seat cost, no ToS friction
- Proper
[bot]badge — immediately distinguishable from human activity - Short-lived tokens reduce blast radius if a secret leaks
- Granular permissions — Alpha only needs review/merge access, Delta needs write access
- Officially recommended by GitHub for exactly this use case
Interim: Signature lines. Until GitHub Apps are set up, each bot appends a signature to every GitHub comment:
— Alpha (sv0-alpha)
— Delta (sv0-delta)
This is already deployed in both agents' CLAUDE.md files as of 2026-03-03.
Implementation Plan
Phase 1: Signature Lines (done)
- Add "Bot Identity on GitHub" section to
sv0-alpha/bot/CLAUDE.md - Add "Bot Identity on GitHub" section to
sv0-delta/CLAUDE.md - Both agents instructed to sign all GitHub interactions
Phase 2: GitHub App Registration
- Go to SecurityV0 org settings → Developer settings → GitHub Apps → New GitHub App
- Create
sv0-alphaapp:- Name:
sv0-alpha - Description: "SecurityV0 review and project management bot"
- Homepage URL:
https://github.com/SecurityV0 - Permissions:
Pull requests: Read & write,Issues: Read & write,Contents: Read - Where can this app be installed: "Only on this account"
- Webhook: disabled (not needed for API-only usage)
- Name:
- Upload a logo for visual distinction
- Generate and download the private key (
.pem) - Install the app on
sv0-platform,sv0-connectors,sv0-documentation - Repeat for
sv0-deltawithContents: Read & writepermission
Phase 3: Token Generation Integration
Add a helper to each bot that exchanges the app's private key for an installation token:
// bot/src/github-app-token.ts
import { createAppAuth } from "@octokit/auth-app";
import { readFileSync } from "node:fs";
export async function getInstallationToken(appId: number, installationId: number): Promise<string> {
const auth = createAppAuth({
appId,
privateKey: readFileSync(process.env.GITHUB_APP_KEY_PATH!, "utf8"),
installationId,
});
const { token } = await auth({ type: "installation" });
return token;
}
Each bot sets GH_TOKEN before running gh commands:
export GH_TOKEN=$(node -e "import('./src/github-app-token.js').then(m => m.getInstallationToken(APP_ID, INSTALL_ID).then(console.log))")
gh pr review 17 --approve --body "..."
Phase 4: Retire Signature Lines
Once GitHub Apps are active and both bots post under their own [bot] identity, the signature line convention can be removed from CLAUDE.md files. The [bot] badge replaces it.
Security Considerations
- Private keys must be stored outside the repo — use environment variables or a secrets manager, never commit
.pemfiles - Installation tokens expire in 1 hour — no long-term credential exposure
- Permission scoping — Alpha gets read-only content access (reviewer), Delta gets write access (implementer). Principle of least privilege.
- Rotation — if a key is compromised, revoke and regenerate from GitHub App settings. No need to rotate a shared PAT that affects all bots.
References
- GitHub Apps documentation
- Authenticating as a GitHub App installation
- GitHub Terms of Service — machine accounts
- @octokit/auth-app
- tibdex/github-app-token (GitHub Action)
Next Action
Status: adopted — partial
GitHub Apps registered: sv0-blue, sv0-echo, sv0-delta. Architecture documented and agreed.
Partial: Bots currently share the Ivan-Fn personal access token while dedicated app token auth is pending. AGENTS.md signature convention (-- Charlie (sv0-charlie), etc.) is in use as a workaround. Migration to per-app tokens is outstanding.