Design: obot GitHub App for agent and automation access

Requirement jwildfire/obot.roadmap#3 · 2026-07-03, updated 2026-07-04 · Approved — D1–D4 resolved by @jwildfire 2026-07-04
Drafted by Claude Code using Fable 5 for @jwildfire review. HTML design-doc format per obot-claw#59.

1. Context and goals

The obot portfolio previously ran on a dedicated GitHub machine account (obot-claw) plus an always-on OpenClaw runtime — a second full identity with long-lived credentials to secure and rotate. That setup was wound down in July 2026 (hub archived; see the #7 design), leaving a gap: agents and automation still need a non-personal identity with scoped access to the portfolio repos, so that automated actions are attributable to the automation rather than to @jwildfire personally.

A GitHub App fills the gap with far less surface area than a machine account: tokens are short-lived (1 hour), scoped to an explicit repo whitelist, and minted from a single private key that Jeremy controls. Success criteria (from #3):

2. Architecture — one app, two token flows

App registration (@jwildfire)          ── App ID + private key (PEM)
        │
        │  sign RS256 JWT (≤10 min TTL)
        ▼
POST /app/installations/{installation_id}/access_tokens
        │
        ▼
Installation token (1 h TTL, repo-whitelisted, permission-capped)
        │
        ├── local agent:  GH_TOKEN=$(obot-app-token) gh …   /  git push via x-access-token
        └── Actions:      actions/create-github-app-token@v2 → ${{ steps.app.outputs.token }}

Both consumers end at the same place: an installation token whose blast radius is capped three ways — by expiry (1 hour), by the installation's repo whitelist, and by the app's permission set (§3). Actions performed with the token are attributed to the app's bot login (<slug>[bot]), so bot work is visibly distinct from @jwildfire's own.

2.1 Which identity, when

ActorIdentityExamples
Jeremy working interactively; agent work Jeremy reviews before it posts @jwildfire (existing gh auth — unchanged) Requirement drafting, PRs from working sessions, sign-offs, merges
Automation acting on its own; agent actions that should read as obot's the app (<slug>[bot]) Scheduled workflows, cross-repo rollups, bot status comments, the future diary automation (separate requirement)

The AGENTS.md attribution convention ("This … was drafted by Claude Code using …") continues to apply to the content of issues, PRs, and comments regardless of which identity posts them.

Bot-attributed git commits use the standard app-bot author format, with the bot user ID looked up once after registration (gh api '/users/<slug>[bot]' --jq .id):

git -c user.name='<slug>[bot]' \
    -c user.email='<ID>+<slug>[bot]@users.noreply.github.com' commit …
git push https://x-access-token:<TOKEN>@github.com/<owner>/<repo>.git

3. App registration specification

FieldValue
Owner@jwildfire (personal account — no org required; registration is a personal-account action)
Nameobotclaw — actions read as obotclaw[bot] (D1, resolved — §8; availability re-checked at registration)
DescriptionAutomation identity for Jeremy Wildfire's obot portfolio (safety-graphics modernization)
Homepage URLhttps://jwildfire.github.io/obot.roadmap/
WebhookDisabled — the app is an authentication identity only; no event subscriptions, no endpoint to host or secure
Where can this app be installed?Only on this account
Installation"Only select repositories": obot.roadmap, safety.agent, safety.viz, gsm.safety, safety-histogram. New repos are added to the installation explicitly, never "all repositories".

3.1 Permissions (D2)

PermissionAccessWhy
MetadataReadMandatory for all apps
ContentsRead & writeClone, branch, commit, release assets
IssuesRead & writeFile/comment/label issues, rollups
Pull requestsRead & writeOpen PRs, PR comments
WorkflowsRead & writeBot commits that touch .github/workflows/ — e.g. rolling out the anticipated @claude PR-responder workflow (§9) across portfolio repos
ActionsReadWorkflow-run status and logs via the API, for cross-repo run monitoring
DiscussionsRead & writeAdded by @jwildfire at registration (2026-07-04) — bot participation in repo Discussions

As registered (2026-07-04): App ID 4215246 · installation 144370633 (the five §3 repos) · bot login obotclaw[bot], user ID 299836032 · commit author obotclaw[bot] <299836032+obotclaw[bot]@users.noreply.github.com>.

D2 resolved to this broader set (the draft recommended a minimal one): Workflows (write) and Actions (read) are requested up front so the anticipated follow-on consumers — the @claude PR-responder, which needs its workflow file pushed to each participating repo, and cross-repo automation that watches run status — don't each trigger an edit-and-re-approve round-trip on the installation. The set is still bounded: no administration, organization, or account permissions, and the webhook stays disabled.

Known limitation (verified against the GitHub App permissions reference, 2026-07-03): apps have no permission for user-owned Projects (v2) — the Projects permission exists only at the organization level. The app therefore cannot read the obot Roadmap user Project, and the site's ROADMAP_TOKEN PAT (project read) stays in place for Project-Status reads. The app replaces the obot-claw identity, not that token.

4. Key management (D3)

ItemSensitivityStorage
App ID, installation ID, bot user IDLow (identifiers, not secrets) Helper-script config (committed docs are fine)
Private key (PEM)Secret — equivalent to the bot identity Local: macOS Keychain via security add-generic-password -s obot-github-app, stored base64-wrapped (raw multiline values don't survive a security -w round-trip); the helper reads it with security find-generic-password -w | base64 -d. Never in dotfiles, repos, or plain files on disk.
Actions: repo secrets OBOT_APP_ID + OBOT_APP_PRIVATE_KEY, added only to repos that run app workflows (initially obot.roadmap).
Installation tokensShort-lived (1 h) Never stored; minted on demand, held in process environment only

5. Agent-side tooling (D4)

Local agents need installation tokens without hand-running the JWT dance. Recommended shape: a small self-contained script plus a thin skill, both in safety.agent (the agent scaffold owns agent tooling; there is no obot.agent repo — that idea is shelved):

Acceptance for the agent flow: from a normal working session, mint a token and post a comment as the bot on a designated test issue — then confirm the comment renders as <slug>[bot].

6. GitHub Actions integration

Workflows use the first-party actions/create-github-app-token@v2:

- id: app
  uses: actions/create-github-app-token@v2
  with:
    app-id: ${{ secrets.OBOT_APP_ID }}
    private-key: ${{ secrets.OBOT_APP_PRIVATE_KEY }}
    # owner/repositories: defaults to the current repo; widen per workflow as needed
- run: gh issue comment … --repo jwildfire/obot.roadmap
  env:
    GH_TOKEN: ${{ steps.app.outputs.token }}

7. obot-claw credential retirement

The requirement closes only when the old identity's credentials are dead, not merely unused:

ItemAction
obot-claw personal access tokens Sign in as obot-claw once, delete all PATs (@jwildfire — requires the account's credentials)
Local gh logins gh auth status on Jeremy's machines; gh auth logout any obot-claw session
OpenClaw-era configs (~/Documents/obot, old runtime configs) Audit for embedded tokens; scrub any found
Actions secrets on archived obot-claw repos Document as inert — archived repos cannot run workflows; no action possible or needed
The obot-claw account itself Keep, parked with zero credentials — it anchors the archived historical record. Deleting it is out of scope (and would free the name for squatters).

8. Decisions

All four decisions were resolved by @jwildfire on 2026-07-04 in a working session (recorded on #3). Original recommendations kept for the record; D2 resolved differently than recommended.

D1 — App name and branding. GitHub App names are global and "cannot use the same name as an existing GitHub account, unless it is your own" — which blocks obot, orangebot, and obot-claw (all existing accounts; @jwildfire is not an obot-claw org owner, so its name doesn't qualify as "your own"). Recommendation: obotclaw — verified free as both an app slug and an account name on 2026-07-03; keeps the 🍊😺 brand continuity. Fallbacks (also verified free): orange-obot, obot-hq.
Resolved (2026-07-04): obotclaw, as recommended; actions read as obotclaw[bot]. Availability re-checked at registration time.
D2 — Permission set. Everything the portfolio might need, or the minimum it needs now? Recommendation: minimal — Contents / Issues / Pull requests read-write plus mandatory Metadata read, deferring Workflows-write until a workflow actually pushes workflow files.
Resolved (2026-07-04): the broader set (§3.1) — Contents / Issues / Pull requests read-write, plus Workflows read-write and Actions read, so the anticipated @claude PR-responder follow-on (§9) and cross-repo run monitoring don't each need an installation-approval round-trip. Webhook stays disabled.
D3 — Private-key storage and rotation.
Resolved (2026-07-04), as recommended: macOS Keychain locally, Actions secrets per consuming repo (§4); two-key zero-downtime rotation, annual cadence or on suspicion. No PEM on disk, ever.
D4 — Agent-side token minting: CLI helper or skill?
Resolved (2026-07-04), as recommended: both, in safety.agent — a dependency-free scripts/obot-app-token script doing the work, and a thin skill telling agents when and how to use it (§5).

9. Out of scope

10. Task preview

To be filed as Tasks on #3 after design sign-off; listed here to show the intended decomposition.

  1. Register the app per §3 and generate a private key — @jwildfire, guided (interactive GitHub login required); install on the five whitelisted repos. (depends on D1/D2)
  2. Store credentials: PEM → Keychain; OBOT_APP_ID / OBOT_APP_PRIVATE_KEY secrets → obot.roadmap. (D3)
  3. Implement obot-app-token + the identity skill in safety.agent; acceptance: bot comment posted from a working session. (D4)
  4. Add app-smoke-test.yml to obot.roadmap; acceptance: dispatched run comments on #3 as the bot.
  5. Credential retirement audit per §7; record the outcome on #3.
  6. Document the identity rules (§2.1) in obot.roadmap AGENTS.md and the README repo table.