Skip to content

feat(hub): bill Learnings' Copilot use to the organization - #75

Draft
thegovind wants to merge 14 commits into
mainfrom
copilot-org-billing
Draft

thegovind wants to merge 14 commits into
mainfrom
copilot-org-billing

Conversation

@thegovind

@thegovind thegovind commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

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 ignores REPLICA_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 to replica-learning for 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"]
  end
Loading

Hub 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: read and 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-sdk 1.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 way replica-learning passes 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.

thegovind and others added 10 commits October 22, 2026 07:02
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>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 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

Comment thread hub/app/models/copilot_connection.rb Outdated
Comment thread hub/app/models/copilot_connection.rb Outdated
Comment thread hub/app/models/copilot_connection.rb
Comment thread hub/test/models/github_app_configuration/tokens_test.rb
Ben Thomas and others added 4 commits October 22, 2026 12:08
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants