| title | 10. Per-org GitHub Apps for agent identity | ||||
|---|---|---|---|---|---|
| status | Proposed | ||||
| relates_to |
|
||||
| topics |
|
Date: 2026-04-02
Proposed
The admin CLI (branch agent-admin-cli-clean-room-v4) creates per-org GitHub
Apps using the manifest flow — one app per agent role (fullsend, triage, coder,
review) — and stores their private keys as repo secrets in the org's .fullsend
repo. This is an implicit design decision that has not been explicitly justified
or recorded.
The standard GitHub App model is designed for multi-tenancy without per-org apps: a single app owner holds the private key, many orgs install the app, and the owner's backend mints scoped installation tokens per org. This is how Dependabot, Codecov, Renovate, and every GitHub Marketplace app works.
The question is whether fullsend should follow that standard model (global shared apps) or require each adopting org to create its own apps.
The answer depends on where agent workloads execute. Fullsend's design requires adopting orgs to run agent workloads on their own infrastructure. Today that means GitHub Actions; in the future it may mean Kubernetes clusters or other compute platforms the org controls. The credential mechanism must work across all of these.
This ADR relates to ADR 0009, which addresses how ephemeral tokens are generated from app credentials. This ADR addresses the prior question: who owns the apps and where do the private keys live.
Each adopting org creates its own set of GitHub Apps (one per agent role) during
installation. The org holds the private keys and stores them in the .fullsend
repo's secrets. Workflow runs in the org use these keys to mint short-lived
installation tokens.
Pros:
- Compute-agnostic. The private key is a portable blob that can be stored in any secret store (GitHub Actions secrets, Kubernetes Secrets, Vault). Token minting is ~20 lines of code that runs identically on any platform.
- Zero external dependencies. No central service, no availability concerns, no single point of failure.
- True sovereignty. The org owns the apps, the keys, and the permissions. No trust relationship with a third party required.
- Blast radius isolation. A compromise of one org's keys affects only that org.
- Aligns with "the repo is the coordinator" — no external coordination layer.
Cons:
- Onboarding friction. The manifest flow requires browser interaction per app, multiplied by the number of agent roles (currently 4).
- Permissions drift. When a new permission or event subscription is needed for an agent role, every org must update their apps individually. There is no centralized upgrade path.
- Operational burden. Org admins manage multiple GitHub Apps (creation, installation, key rotation, deletion).
- App name squatting. GitHub App slugs are globally unique. A naming collision
(
fullsend-acme-coder) blocks the legitimate org from using that slug. - Lost key recovery. GitHub App private keys are only available at creation time. If lost, the app must be deleted and recreated.
Fullsend owns one set of global GitHub Apps. The private keys are stored as
secrets in the fullsend-ai org. A reusable workflow in the fullsend-ai org
mints installation tokens for calling orgs via workflow_call.
Pros:
- Simple onboarding. Orgs install the app with one click; no manifest flow.
- Centralized permission updates. New permissions are added once to the global apps.
- No private key management for adopting orgs.
Cons:
- Coupled to GitHub Actions.
workflow_callis a GitHub Actions primitive; Kubernetes pods and other compute platforms cannot call reusable workflows. - Reusable workflow outputs are visible in logs, potentially leaking tokens.
- Single point of compromise. If the fullsend org's secrets leak, all adopting orgs are affected.
- Centralized dependency. The fullsend org's workflows must be available for any adopting org to operate.
Fullsend owns global apps and runs a stateless token vending service (e.g., Cloudflare Worker, AWS Lambda). Callers present an OIDC token proving their identity; the service verifies it and returns a scoped installation token.
Pros:
- Simple onboarding. One-click app installation.
- Stateless service. No database, trivially simple (~50 lines of code).
- Self-hostable. Orgs can run their own instance.
Cons:
- Requires a running service — even a minimal one is infrastructure to operate, monitor, and secure.
- Identity federation complexity. GitHub Actions OIDC tokens only prove GitHub Actions identity. Kubernetes uses different OIDC tokens with different trust roots. Each new compute platform requires a new identity attestation integration, creating an ever-growing compatibility matrix.
- Network reachability. The service must be reachable from wherever compute runs, which may not be possible from air-gapped or firewalled clusters.
- Single point of compromise if a shared instance is used.
Offer global shared apps as the default path, with an option for orgs that want full sovereignty to create their own apps.
Pros:
- Low friction for most adopters.
- Sovereignty for those who need it.
Cons:
- Two code paths to maintain and test.
- The global path still has the compute-coupling and centralization problems of Options 2 or 3.
- Complexity of supporting both models may exceed the benefit.
Each adopting organization creates its own set of GitHub Apps — one per agent role — during fullsend installation (Option 1).
The decisive factor is compute-agnosticism. Fullsend requires adopting orgs to run agent workloads on their own infrastructure. Today that is GitHub Actions; in the future it will include Kubernetes clusters and potentially other platforms. The credential mechanism must work identically regardless of where the compute runs.
Per-org apps with org-held private keys satisfy this requirement cleanly:
- The private key is a portable secret that can be stored in any platform's secret management system.
- Token minting (sign JWT, exchange for installation token) is a simple, platform-independent operation.
- No network reachability to a central service is required.
- No platform-specific identity attestation (OIDC provider integration) is needed.
All alternatives that avoid per-org apps require either a central service (Options 3, 4) or a GitHub Actions-specific mechanism (Option 2). These approaches would require new integrations for each additional compute platform, creating an ever-growing compatibility matrix. Per-org apps avoid this entirely.
The "no managed service" framing understates the actual constraint. The real architectural requirement is that the credential mechanism must be compute-agnostic by design — a consequence of fullsend's bring-your-own- infrastructure model.
- Adopting orgs own their credential lifecycle. They create apps, store keys, rotate credentials, and delete apps on their own terms. No dependency on fullsend infrastructure.
- Onboarding requires manifest flow interaction. The admin CLI must guide org admins through creating and installing multiple GitHub Apps. This is more friction than a one-click marketplace install.
- No centralized permission upgrade path. When agent roles need new
permissions or event subscriptions, each org must update their apps. The
admin CLI should provide tooling to detect and remediate permission drift
(e.g., an
analyzesubcommand that compares installed app permissions against expected permissions). - App slug collisions are possible. The naming convention
(
fullsend-{org}-{role}) uses a global namespace. Mitigation: the CLI already supports slug overrides and known-slug mappings in config. - Future compute platforms work automatically. Moving from GitHub Actions to Kubernetes (or any other platform) requires only storing the existing private key in the new platform's secret store. No changes to the app model, no new service integrations, no identity federation.
- Blast radius is isolated per org. A key compromise in one org cannot affect any other org.