Skip to content
CommonLayer
Documentation menu
On this page

ADR-0046: Repositories stay with their maintainers

Status: Accepted (WP-80). Specified in plan 13; implemented in crates/commonlayer-github (access, backups, rule engine, stewardship port) and crates/commonlayer-projects (succession).

Context

The first design assumed the image and chart projects live in repositories CommonLayer owns. That takes projects away from the people who build them: their repository, their visibility, their name on the work. Maintainers would have little reason to bring a project. The opposite, repositories anywhere with no hold on them at all, lets a funded project disappear the day its owner loses interest.

Decision

  • A project's repository stays in its maintainer's own GitHub account or organization. CommonLayer provides the guardrails, the independent checks, the mirror of what was released, and the management of responsibilities, funding and audit. It does not own the repository and does not move it.
  • The repository is connected through the CommonLayer GitHub App. The access level is derived from the installation's permissions: managed (the App may manage collaborators), observed, or none. Funded maintenance needs managed; the runtime setting projects.require_managed_repository (default on) says so.
  • With managed access CommonLayer invites the maintainers it appointed and removes the access it granted when an assignment ends. It never removes the owner or anyone it did not add. Every change is audited.
  • CommonLayer never writes to a maintainer's repository: no pushes, no commits, no branch changes. It reads through the API and mirrors with git over HTTPS.
  • Every release, a weekly schedule and the opening of a succession take a source backup: a verified git bundle of every branch and tag with a manifest of the refs, kept in the backups bucket. A release whose commit is not in its backup fails the backup.
  • When the owner of a repository leaves the project, a succession case asks them to transfer it. If that does not happen within the grace period (default 30 days), a platform admin plans a restore from a backup into a new repository: the successor's own, or one in a stewardship organization when the operator configured one (the only place CommonLayer writes to a repository, and it is its own).
  • A succession ends only with the repository in appointed hands: GitHub reports an active maintainer other than the leaving person as its administrator (through their linked identity, compared by numeric account id), or it lives in the stewardship organization. A transfer counts only when GitHub reports a different owner account than when the case opened. A restored repository must be a new, unlinked repository and match the backup (default branch head and every release tag) before it becomes the project's primary. These checks run before anything is compared with the backup.
  • A succession is not a financial event and not a release. Funding, obligations and promoted artifacts are untouched.

Alternatives rejected

  • All repositories under one CommonLayer organization. Simple to manage, but it removes the maintainers' ownership and visibility, which is what the platform is meant to reward.
  • Forking every repository and building from the fork. The fork becomes the real project and the maintainer's repository a mirror; the same loss by another route, plus a permanent merge problem.
  • A deploy key or a personal token instead of the App. No way to manage collaborators, no per-repository consent the owner can see and revoke on GitHub, and a long-lived secret per project.
  • Taking over a repository by force. GitHub offers no such thing to an App, and it should not: the backup and a new repository reach the same goal without it.

Consequences

  • The App asks for Administration: write on connected repositories. That is a strong permission; the code uses a small named set of calls (collaborators and invitations), each audited and counted in commonlayer_repository_access_changes_total. Threat model B10 and O16 describe what an attacker gains from the App key.
  • The API process holds the App key as well as the worker, because the repository picker asks GitHub synchronously (threat model O16). Moving those calls behind the worker is possible and not done.
  • Backups hold git history only. Issues, pull requests, releases pages, wikis and Git LFS objects are not backed up; every manifest says so.
  • A project continued from a backup has a new repository, so the signing identity of future releases changes. Releases made before the switch keep the identity they were signed with and stay verifiable.
  • An owner who removes the App or deletes the repository makes the link unreachable; after projects.owner_unreachable_days a succession opens by itself.
  • Nothing here has run against github.com: the tests use an in-memory GitHub, wiremock and local bare repositories, plus one HTTPS mirror of a public repository.