You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Replace the manual PAT entry in town settings with a one-click GitHub OAuth Device Flow that authenticates git operations as the individual user. Supersedes #1001 (git commit co-authorship) — this approach solves attribution, contribution credit, and PR authorship in one step, without needing to separately resolve user name/email or inject commit trailers.
Today, agent git operations (push, PR creation, merge) either use a GitHub App installation token (actions show as "bot") or a manually-entered PAT (requires the user to navigate to GitHub settings, create a token, copy-paste it). Neither is ideal:
App tokens: Commits and PRs are attributed to the app, not the user. No contribution graph credit. No human provenance trail.
Manual PAT: High-friction setup. Users frequently get scopes wrong, tokens expire, and there's no refresh mechanism.
Solution
Add a GitHub OAuth Device Flow to the town settings UI. The user clicks "Connect GitHub," enters a short code in GitHub's browser UI, and the resulting OAuth user token is stored in the town config. All subsequent git operations authenticate as that user.
Why Device Flow (not standard OAuth redirect)
The standard OAuth Authorization Code flow requires a redirect URI, which is awkward for a settings page that's already open. The Device Flow is designed for exactly this UX: the user authorizes in a separate browser tab and the token arrives via polling. No redirect, no popup, no copy-paste.
What the user token provides
Capability
App Installation Token
User OAuth Token
Commit GIT_AUTHOR
Bot / app identity
User's name + email
PR author on GitHub
App
User
Contribution graph credit
No
Yes
Access scope
Repos where app is installed
All repos user can access
gh CLI compatible
Yes
Yes
Token lifetime
1 hour (auto-refresh)
Non-expiring (until revoked)
With the user token, GIT_AUTHOR_NAME/GIT_AUTHOR_EMAIL and GIT_COMMITTER_NAME/GIT_COMMITTER_EMAIL can both be resolved from the token's identity — or the agent can remain as committer while the user is the author (per #1001's original design). Either way, the token itself handles authentication.
UX Flow
User opens Town Settings → Git Authentication section
Clicks "Connect GitHub" button
UI calls worker endpoint POST /api/towns/:townId/auth/github/device-code
Worker calls POST https://github.com/login/device/code with the OAuth App's client_id and scopes (repo, workflow)
UI displays: "Enter code ABCD-1234 at github.com/login/device" with a link that opens in a new tab
Worker starts polling POST https://github.com/login/oauth/access_token with the device_code every interval seconds
User enters the code in GitHub's UI and clicks "Authorize"
Poll returns access_token + token_type + scope
Worker stores the token in townConfig.git_auth.github_token and resolves the user's name/email via GET /user on the GitHub API
UI shows "Connected as @username" with a disconnect button
Data stored
townConfig.git_auth={github_token: string;// OAuth user token
github_username: string;// Resolved from GET /user
github_user_email: string;// Resolved from GET /user/emails (primary)
github_user_name: string;// Resolved from GET /user (display name)
connected_at: string;// ISO timestamp};
Agent dispatch changes
When dispatching any agent, pass the user's git identity to the container:
GitLab has an equivalent Device Flow — this issue focuses on GitHub but the architecture should be extensible.
Non-expiring OAuth tokens can be revoked by the user at any time on GitHub (Settings → Applications). The agent should handle 401s gracefully and surface "reconnect needed" in the UI.
Summary
Replace the manual PAT entry in town settings with a one-click GitHub OAuth Device Flow that authenticates git operations as the individual user. Supersedes #1001 (git commit co-authorship) — this approach solves attribution, contribution credit, and PR authorship in one step, without needing to separately resolve user name/email or inject commit trailers.
Parent: #204 (Phase 4: Hardening)
Supersedes: #1001
Problem
Today, agent git operations (push, PR creation, merge) either use a GitHub App installation token (actions show as "bot") or a manually-entered PAT (requires the user to navigate to GitHub settings, create a token, copy-paste it). Neither is ideal:
Solution
Add a GitHub OAuth Device Flow to the town settings UI. The user clicks "Connect GitHub," enters a short code in GitHub's browser UI, and the resulting OAuth user token is stored in the town config. All subsequent git operations authenticate as that user.
Why Device Flow (not standard OAuth redirect)
The standard OAuth Authorization Code flow requires a redirect URI, which is awkward for a settings page that's already open. The Device Flow is designed for exactly this UX: the user authorizes in a separate browser tab and the token arrives via polling. No redirect, no popup, no copy-paste.
What the user token provides
GIT_AUTHORghCLI compatibleWith the user token,
GIT_AUTHOR_NAME/GIT_AUTHOR_EMAILandGIT_COMMITTER_NAME/GIT_COMMITTER_EMAILcan both be resolved from the token's identity — or the agent can remain as committer while the user is the author (per #1001's original design). Either way, the token itself handles authentication.UX Flow
POST /api/towns/:townId/auth/github/device-codePOST https://github.com/login/device/codewith the OAuth App'sclient_idand scopes (repo,workflow)device_code,user_code,verification_uri,intervalPOST https://github.com/login/oauth/access_tokenwith thedevice_codeeveryintervalsecondsaccess_token+token_type+scopetownConfig.git_auth.github_tokenand resolves the user's name/email viaGET /useron the GitHub APIData stored
Agent dispatch changes
When dispatching any agent, pass the user's git identity to the container:
This gives:
ghCLI = authenticated as the user (PRs opened by the user)Commit trailers (from #1001)
Retain the polecat system prompt instruction to include trailers:
The
Requested-bytrailer from #1001 becomes unnecessary sinceGIT_AUTHORalready identifies the human.Implementation
New endpoints
POST /api/towns/:townId/auth/github/device-code— Initiates the device flow, returns{ userCode, verificationUri, expiresIn }GET /api/towns/:townId/auth/github/poll— Polls for the token (called by UI on interval). Returns{ status: 'pending' | 'complete', username? }DELETE /api/towns/:townId/auth/github— Disconnects: clears the stored tokenUI changes
@usernamewith avatar and a "Disconnect" buttonPrerequisites
repoandworkflowscopesclient_idstored as a worker secret/env varAcceptance Criteria
GIT_AUTHOR= user,GIT_COMMITTER= agentghCLI in the container authenticates as the userNotes