Repository navigation
Conversation
Hub's GitHub App can now create an installation token that holds only the read-only organization permission for Copilot requests. Each call creates a new token, and Hub refuses one that GitHub reports with any other permission, so it never hands out a token that can change repositories. A missing permission raises its own error. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The person who runs Hub can set REPLICA_COPILOT_BILLING=organization. Hub then ignores REPLICA_COPILOT_TOKEN and, for each Copilot request, passes replica-learning a new one-hour token from its GitHub App that can only make Copilot requests, so Copilot bills the organization that installed the App instead of a person. The default, token_owner, keeps today's behavior, and Hub never falls back from one to the other. The connection now records the credential's identity: the operator's token, or the App installation. Hub bills an organization only after a Hub administrator connects Copilot for that installation, so turning on organization billing never moves an existing connection to the organization's bill. A missing permission or a Copilot refusal is recorded against the installation and stops the daily runs until an administrator checks again. The Copilot and Learnings pages say who Copilot bills and what Hub needs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Copilot guide explains how to give Hub's App the Copilot requests permission, turn on organization billing, and connect Copilot again. The deployment, security, architecture, App setup and Learnings guides, the environment example, ADR 0001 and the learning package now describe both ways Hub can pay for Copilot. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
A Copilot token with read-only repository metadata could still read collaborators, commit comments and other details of every repository the App's installation covers. Hub now refuses a token unless GitHub reports exactly the Copilot requests permission, so the token can only make Copilot requests, as the Copilot page and guide say. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Hub creates its GitHub App as a private App, which only its owner can install, so an App that belongs to a personal account can never bill an organization. The Copilot page now says to create the App again under the organization or keep using a token, instead of asking to install it on an organization. The Copilot guide also no longer says that resetting the App leaves Copilot connected with organization billing: Hub stops until the new App is installed and an administrator connects Copilot again. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🔵 Needs a closer look
Billing-sensitive authentication has unresolved confirmation safeguards and still depends on an SDK upgrade and live organization validation.
4 open issues
What changed in this PR
Adds optional organization billing for Learnings through Hub’s GitHub App, while keeping token-owner billing as the default.
Changes:
- Mint fresh, permission-limited installation tokens for organization-billed Copilot requests.
- Bind saved connections to App credentials and installation identity, with billing-aware confirmations and recovery messages.
- Extend tests, Storybook states, and operator documentation.
| File | Description |
|---|---|
packages/learning/src/labelers/copilot.mjs |
Documents installation-token credentials. |
packages/learning/README.md |
Explains organization-billed authentication. |
hub/test/support/learning_test_helpers.rb |
Adds organization connection setup and configuration cleanup. |
hub/test/support/github_request_stubs.rb |
Adds Copilot token responses and permission failures. |
hub/test/models/learning/test_helpers_test.rb |
Checks billing configuration restoration. |
hub/test/models/learning/run_test.rb |
Covers per-command tokens and minting failures. |
hub/test/models/github_client_test.rb |
Checks structured error retention. |
hub/test/models/github_app_configuration/tokens_test.rb |
Covers token freshness, permissions, and installation changes. |
hub/test/models/copilot_connection_test.rb |
Covers organization confirmation and credential identity. |
hub/test/integration/learnings_test.rb |
Checks organization-billing status messages. |
hub/test/integration/copilot_connections_test.rb |
Covers billing-aware connection and recovery flows. |
hub/docs/security.md |
Describes token isolation and installation checks. |
hub/docs/learnings.md |
Explains billing choices and recovery. |
hub/docs/github-app-setup.md |
Documents the optional organization permission. |
hub/docs/deployment.md |
Adds billing configuration and restart requirements. |
hub/docs/copilot.md |
Documents organization billing, consent, and credential lifecycle. |
hub/docs/architecture.md |
Adds App-token authentication to the architecture overview. |
hub/config/locales/learnings.en.yml |
Adds organization-billing status messages. |
hub/config/locales/copilot_connection.en.yml |
Adds billing explanations, confirmations, and errors. |
hub/config/initializers/deployment_configuration.rb |
Validates the billing setting. |
hub/config/application.rb |
Reads the billing mode from the environment. |
hub/app/views/learnings/show.json.props |
Selects billing-aware Learnings status text. |
hub/app/views/copilot_connections/show.json.props |
Exposes billing identity and confirmation state. |
hub/app/models/learning/run.rb |
Tracks the credential behind each labeling request. |
hub/app/models/github_client/transport.rb |
Retains decoded GitHub error responses. |
hub/app/models/github_client/installations.rb |
Supports permission-limited token creation. |
hub/app/models/github_client.rb |
Adds structured response data to errors. |
hub/app/models/github_app_configuration/tokens.rb |
Mints and validates dedicated Copilot tokens. |
hub/app/models/github_app_configuration.rb |
Adds installation snapshots and credential fingerprints. |
hub/app/models/copilot_connection.rb |
Adds organization billing and credential lifecycle checks. |
hub/app/javascript/pages/copilot-connection-page/types.ts |
Adds billing and setup message properties. |
hub/app/javascript/pages/copilot-connection-page/stories.tsx |
Adds organization setup and recovery states. |
hub/app/javascript/pages/copilot-connection-page/index.tsx |
Displays billing and setup information. |
hub/app/javascript/pages/copilot-connection-page/index.test.tsx |
Checks token-owner billing information. |
hub/app/helpers/copilot_connections_helper.rb |
Selects translations by billing mode. |
hub/app/controllers/copilot_connections_controller.rb |
Reports billing-aware connection failures. |
hub/.env.example |
Shows the organization-billing setting. |
docs/adr/0001-learning-extension-architecture.md |
Records organization billing as an optional authentication path. |
Review effort: Max
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
# Conflicts: # hub/docs/copilot.md # hub/test/integration/copilot_connections_test.rb
# Conflicts: # hub/app/models/github_app_configuration.rb # hub/app/models/github_client/installations.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Why
Learnings asks Copilot to label prototype review comments. Today Hub can do that only with a personal access token in
REPLICA_COPILOT_TOKEN, so Copilot bills the Copilot plan of whoever created the token. On a shared Hub, that puts the whole team's usage on one person's license.GitHub now lets a GitHub App installed on an organization make Copilot requests with its own installation token, and Copilot bills that organization. It is in public preview: see server-to-server authentication and managing GitHub Apps that use your organization's Copilot. ADR 0001 deferred this option because GitHub required the App to have access to every repository in the organization. GitHub's updated instructions use an organization permission instead, so Hub's App keeps access to only the repositories it already has.
What changes
The person who runs Hub can set
REPLICA_COPILOT_BILLING=organization. Hub then ignoresREPLICA_COPILOT_TOKEN. For each Copilot request, Hub's GitHub App creates a new one-hour installation token that requests only the read-only organization permission Copilot requests. Hub passes the token only toreplica-learningfor that one command and never stores it. The default,token_owner, keeps today's behavior, and Hub never falls back from one choice to the other.flowchart LR subgraph today["Today, and the default"] T["Personal token in REPLICA_COPILOT_TOKEN"] --> L1["replica-learning"] --> C1["Copilot bills the token's owner"] end subgraph org["REPLICA_COPILOT_BILLING=organization"] K["Hub's GitHub App, already set up"] --> M["New one-hour token for each request, Copilot requests only"] --> L2["replica-learning"] --> C2["Copilot bills the App's organization"] endHub does not ask for the new permission when it creates its App, so an App that does not bill Copilot cannot spend the organization's AI credits. To turn this on, the App's owner adds the Copilot requests organization permission and an organization owner approves it. The organization also needs Copilot Business or Copilot Enterprise and must be enabled for Copilot requests from GitHub App installations. Usage counts against the organization's Copilot budget. GitHub has no budget for one App, and anyone who holds the App's private key, which Hub already stores, can spend that budget, so the Copilot guide explains how to set a budget that stops usage.
A Hub administrator still connects Copilot on the Copilot page, which says who Copilot bills and names the organization in the confirmation. Hub never moves an existing connection to an organization's bill. The confirmation is bound to the exact saved GitHub App configuration, credential revision, App identity, installation ID, and organization identity. Hub checks that snapshot immediately before and after GitHub mints every token and rechecks the Copilot connection before handing the token to Learnings. A reset, replacement, transfer, installation change, credential change, suspension, removal, or overlapping disconnect therefore discards the token instead of sending it to Copilot under a new or unconfirmed billing identity.
GitHub's installation-token endpoint can return HTTP 422 for validation or abuse failures as well as missing permissions. Hub records a durable Copilot refusal only for the exact structured response observed when the requested permission was not granted. Other 422 responses remain ordinary GitHub availability failures. Hub also remains fail-closed on the successful response: GitHub's documentation does not establish an implicit extra permission, so Hub accepts exactly
organization_copilot_requests: readand refuses any returned permission set with more or less access.The Copilot page and its Storybook stories cover organization billing, an unconfirmed installation, a missing organization installation, and refusal recovery. Screenshots are not attached yet because this execution host has no Docker, Node.js, installed Hub dependencies, or existing Storybook build for the browser canvas to render. A real smoke test against an organization with the public preview enabled also remains required to confirm GitHub's returned permission payload and billing attribution.
GitHub published these instructions together with Copilot runtime 1.0.102, which
@github/copilot-sdk1.0.25 includes. Hub pins 1.0.16, which includes runtime 1.0.90, and GitHub's earlier instructions said not to pass an installation token the wayreplica-learningpasses its token. Version 1.0.25 came out on October 21, 2026, so Replica's seven-day dependency rule allows it from October 28. This pull request remains a draft until that update and the real preview-enabled organization check are complete.