Files
PaperClipAI/doc/connections/GITHUB.md
T
DottaandPaperclip 856813ba3a fix(connections): distinguish local setup from provider handoff (#12947)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Connections give those agents access to external services.
> - Connection setup first asks who may use the credential.
> - Some providers need a local method selection before OAuth starts.
> - The Access button said it would open GitHub even when it opened
another local step.
> - This pull request names the actual next action and shows the
external arrow only for a provider handoff.
> - Users can distinguish local setup from leaving Paperclip.

## Linked Issues or Issue Description

Refs: #12943

The browser audit found a second, separate clarity problem. GitHub
showed "Continue to GitHub" twice: once to open its local method
selection and once to start OAuth. The first label promised the wrong
action. This change fixes that label without adding or removing a setup
step.

## What Changed

- Use "Continue" when Access opens another local OAuth setup step.
- Keep "Continue to <provider>" and the external arrow when Access
starts OAuth directly.
- Test GitHub defaults, pre-enrollment setup, direct Notion OAuth, and
dialog behavior.
- Document the two GitHub button actions.
- Preserve the external-handoff arrow in the direct-OAuth Storybook
fixture.

## Verification

- Targeted connection tests: 114 passed.
- `pnpm --filter @paperclipai/ui typecheck`: passed.
- `pnpm --filter @paperclipai/ui build`: passed.
- `node scripts/check-token-gates.mjs`: passed.
- `git diff --check`: passed.
- After the Storybook review fix: 100 connection-flow tests, UI
typecheck, token gates, and Storybook build passed; visually confirmed
the Gmail handoff arrow and local GitHub step labels.
- Actual test-drive browser: confirmed default personal identity and Any
agent; clicked Continue to the local method screen; switched PAT and
OAuth; returned to Access with selections intact; cancelled without a
duplicate connection.
- Live OAuth, local MCP identity, and a real staging sandbox task passed
during the audit. This patch does not change those paths.
- Full local suite has known unrelated macOS path/runtime test failures
from the audit. All CI passed on head
`936ce09f4e1faaf09f9fb778c2cd4c65c2a41972`, including typecheck, build,
all test shards, browser tests, and canary dry run. Greptile: 5/5; no
unresolved comments.

## Risks

- Low risk. This changes button text and an icon cue only. There is no
new schema, permission, consent, or credential behavior.
- Providers with multiple methods now say Continue before their method
screen. Direct OAuth providers keep their existing wording.

## Model Used

- OpenAI Codex assisted with code, tests, terminal checks, and actual
browser verification. The runtime does not expose the exact model ID or
context-window size, so those details are unavailable.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` /
`Closes: #` / `Refs: #` OR (b) described the issue in-PR following the
relevant issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub references)
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass (targeted tests; full-suite
caveats above)
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-06 17:43:48 -05:00

5.9 KiB

GitHub managed connection

GitHub is a Paperclip Cloud-managed GitHub App connection with an advanced PAT compatibility method. Cloud owns the fixed public OAuth callback and signed webhook inbox; provider tokens are sealed to the enrolled instance and stored only in its existing encrypted secret system.

Self-hosted setup

The Access step uses Continue to open the local setup screen. Continue to GitHub on that screen starts the provider handoff. The first button does not imply that the browser is leaving Paperclip yet.

A self-hosted instance needs one Paperclip Cloud approval before its first managed connection. After approval, setup returns to step 2 and continues to GitHub without another instance approval or a service restart.

If an unapproved enrollment link expires, return to setup and select Continue. Paperclip asks the server for a valid link. The server reuses a live pending enrollment or replaces an expired one; this does not revoke or repeat an existing instance approval.

Identity resolution

Every MCP call, gh invocation, native Git operation, checkout, health check, and webhook binding uses the same order:

  1. An active dedicated GitHub grant for the current agent.
  2. The active personal GitHub grant owned by the run's responsibleUserId.
  3. For automated work without a responsible user, a personal grant only when an existing standing delegation names the agent.
  4. Legacy GH_TOKEN/GITHUB_TOKEN only when no managed GitHub connection is configured for the company.

An unavailable or ambiguous managed identity fails visibly. It never falls through to another person, an organization credential, or a legacy token. Agent grants are company-scoped, have exactly one subjectAgentId, cannot be organization defaults, and are installed only for that agent.

The connection installation is the credential owner's consent boundary. A personal setup may target every agent or a selected set, and runtime resolution considers only an enabled, active connection installed for the current agent. Within that boundary, Paperclip treats the run's server-resolved responsibleUserId as its credential principal, including for automated work; agents cannot choose or spoof this field. The owner must still be an active non-viewer company member at each use. A standing delegation is needed only when a run genuinely has no responsible user.

Credential lifecycle

The production, staging, and development GitHub Apps deliberately disable user-to-server token expiration. The resulting long-lived access token is checked with GitHub's /user endpoint every 30 days, together with installation and repository summary refresh. Routine continuity requires no browser visit.

If GitHub returns an expiring access token and rotating refresh token instead, Paperclip stores both encrypted and:

  • refreshes at least one hour before access expiry;
  • forces a rotation at least every 30 days while the instance is active;
  • serializes refresh through the existing database refresh lease and compare- and-swap update;
  • atomically advances both secret values before clearing the lease;
  • retries one forced refresh after a provider 401.

Only an unrecoverable provider invalidation marks a grant needs_reauthorization. Installation removal or suspension is reported as an installation-health failure, not as token expiry.

Repository access

OAuth completion verifies /user, /user/installations, and each installation's accessible repository count. Setup remains incomplete until at least one installation and repository are available. Paperclip stores user and installation summaries, not a repository-name cache. GitHub stays authoritative: removed repository access fails immediately even if a displayed count is stale.

The Apps UI links to GitHub's installation management page and offers Refresh access. Selected repositories are recommended. Choosing all repositories requires an explicit warning in setup.

Webhooks

Paperclip Cloud verifies X-Hub-Signature-256 against the exact bounded request body before parsing, deduplicates by X-GitHub-Delivery, and persists a minimal normalized event before returning 202. Raw webhook payloads are discarded. When registering an active binding, Paperclip sends the current user token only inside the signed, payload-bound broker request so Cloud can verify access to that exact installation; Cloud neither logs nor persists that proof token. Deliveries fan out independently to every enrolled instance bound to the GitHub installation and are sealed to each instance's public key.

The instance polls with backoff, stores a company-scoped idempotency receipt, and acknowledges only successful applications. A merged pull request updates its matching external-object snapshot and immediately runs the existing merge- confirmation resolver. It wakes the assignee only when that interaction's continuation policy requests it; unrelated Paperclip issues are not closed. The periodic GitHub merge sweep remains the reconciliation fallback.

Installation lifecycle events refresh or invalidate installation summaries and remove obsolete Cloud bindings. Activity records contain event identifiers and outcomes but no webhook content. GitHub webhook content is never first-party telemetry.

Run projection

The resolved token is leased at run start as an audited class-3 secret and is projected only into the child process:

  • GH_TOKEN, GITHUB_TOKEN, and an internal credential-helper environment key;
  • GIT_TERMINAL_PROMPT=0;
  • process-scoped GIT_CONFIG_COUNT/KEY_n/VALUE_n entries that clear ambient helpers, install a github.com-only helper, and rewrite GitHub SSH remotes to HTTPS;
  • author and committer identity using <numeric-id>+<login>@users.noreply.github.com.

Tokens never appear in arguments, URLs, files, logs, events, or model context, and the projection never replaces HOME.

Cloud deployment and exact GitHub App registration settings live in paperclip-cloud/docs/github-connector-deploy-bootstrap.md.