skip to content

How would you design least-privilege access for a GitHub organization with dozens of teams and hundreds of repositories?

level: principalimportance: should knowfreq 35%

answer

  1. Highest grant wins, so lower the floor
  2. Structure beats individual invitations
  3. Reserve the destructive role narrowly
  4. Non-members need their own review cadence
  5. Separate organizations are the real boundary

basics

~20 s

Keep the organization base permission at read or none, grant everything else through nested teams that mirror the org, reserve admin to a small owners group, use outside collaborators for non-employees, and drive automation with app tokens rather than personal ones.

solid answer

~50 s

Start from the resolution rule: GitHub takes the **highest** grant across base permission, teams and direct grants, so least privilege means keeping the floor low and expressing everything above it in a structure you can audit. Concretely: set **base permission** to read, or none where repositories are sensitive. Grant through **teams** — never individual invitations — with nested teams mirroring the org so membership changes propagate automatically. Reserve **Admin** to a small set of owners plus a documented break-glass path; use **Maintain** for the people who only needed one setting. Put contractors in as **outside collaborators** with per-repository grants and a scheduled review, since they appear on no team roster. Restrict who may create, transfer, delete or change the visibility of repositories, and restrict forking of private repositories. For machines, use GitHub App installation tokens or fine-grained tokens, never a named human's credential. Then enforce SSO and 2FA, manage grants declaratively, and review access on a cadence using the audit log and the permission APIs.

go deeper

for a junior

You are unlikely to design this, but know the vocabulary: base permission, teams, repository roles, outside collaborators — and that access is granted through teams rather than person by person.

for a middle

Be able to explain why a low base permission plus team grants is the auditable shape, and what breaks in the offboarding story when access was handed out as individual invitations.

for a senior

Show operational depth: restricting repository creation, visibility changes and private forking; moving automation off personal tokens; and running access reviews from API data rather than from what the wiki claims.

for a principal

Own the tradeoff explicitly — isolation versus discoverability, precision versus friction — and be able to argue when a separate organization is warranted and how you migrate an org that has been on write-by-default for years.

## Start from how access resolves Every design decision follows from one mechanic: GitHub grants the **maximum** of the applicable permissions — organization base permission, every team grant including those inherited through a parent team, and any direct collaborator grant — with organization owners holding admin everywhere. Nothing subtracts. So least privilege is not achieved by adding restrictive grants; it is achieved by keeping the floor low and making every grant above it visible and removable. ## The floor Set **base permission** to read for a typical engineering organization: people can discover and clone internal code, which is worth a great deal for reuse and incident response, and nobody can push anywhere by default. Where repositories hold regulated or highly sensitive material, either set base permission to none and grant explicitly, or move that code into an organization of its own — **organization boundaries are the strongest isolation GitHub offers**, and a separate org is the honest answer when "nobody else should even see this" is a real requirement. Setting base permission to write is the single most common mistake. It looks like enabling collaboration; what it actually does is guarantee that every joiner can push to every repository, which no access review can ever unwind without changing the setting. ## Grants through teams, always Individual collaborator invitations are the debt that compounds: they are invisible from the team structure and surface only when someone walks repository by repository. Grant to **teams**, and use **nested teams** so that a member of a specialised child team automatically holds the broader parent team's access. Mirror the organization chart closely enough that adding someone to the team that reflects their job is the only access action onboarding requires — and removing them from it is the only one offboarding requires. Most engineers need **Write** and nothing more. Reach for **Maintain** when someone needs repository settings but should not hold deletion, transfer and visibility. Reserve **Admin** for a small owners group, with a documented break-glass procedure so nobody keeps admin permanently "in case". Where the built-in ladder does not fit — a role that needs Write plus one specific capability — **custom repository roles** on Enterprise plans let you compose that instead of over-granting; use them sparingly, because every custom role is another thing a reviewer must understand. ## The people who are not employees **Outside collaborators** — repository access without organization membership — are the right shape for contractors and external partners: base permission does not apply to them and they cannot be placed on teams, so their access is exactly the grants they hold. That precision is also the risk: nothing about them appears on a roster, so they need an explicit expiry and a scheduled review. Restrict who may invite them to owners, so the population is created deliberately. ## Repository lifecycle levers Access design is not only "who can push". Restrict who may **create** repositories (or at least which visibilities members may create), who may **change visibility**, and who may **transfer or delete**. Restrict **forking of private repositories**, because a fork is a copy that leaves your ruleset behind. Each of these is an organization setting and each closes a route by which content or permissions escape the model you designed. ## Machines Never build automation on a named human's long-lived token; it carries their whole reach and dies when they leave. Prefer **GitHub App installation tokens** — short-lived, scoped to selected repositories and permissions — for anything that touches the API; **fine-grained tokens** with expiry where an app is disproportionate; **read-only deploy keys** for a machine that clones exactly one repository. Inside GitHub Actions the run's own `GITHUB_TOKEN` already covers the workflow's repository. ## Making it stick Enforce **2FA** for the organization and, where available, SSO so identity is centrally revocable — the fastest offboarding is the one that happens in the identity provider. Manage the structure **declaratively** (an infrastructure-as-code provider or the organization API) so team and grant changes are reviewed like code and drift is visible. Then run periodic access reviews driven by data rather than memory: enumerate collaborators and their `role_name` per repository, list outside collaborators, list repository deploy keys, and diff against what the declared model says should exist. The audit log answers "who granted this and when". ## The tradeoff to say out loud Every restriction has a friction cost, and a model so tight that people route around it with shared accounts is worse than a looser one they follow. The judgment is picking where precision is worth it — production deployment paths, credential-bearing repositories, anything regulated — and accepting read-level openness elsewhere so the organization keeps its ability to find and reuse code.

  • When is a separate GitHub organization the right isolation boundary rather than a private repository?
    When the requirement is that most employees should not even know the code exists, or when a different policy regime applies — distinct base permission, distinct owners, distinct app installations and audit stream. A private repository inside the main organization still sits under that org's owners, base permission and installed apps; a separate organization gives you a clean boundary at the cost of duplicated administration and cross-org access friction.
  • How do you run a meaningful access review across hundreds of repositories?
    Drive it from data, not memory: enumerate each repository's collaborators and reported role, list outside collaborators and their repositories, list deploy keys per repository, and diff all of it against the declared model in your infrastructure-as-code. Investigate the exceptions — direct grants, admin holders, credentials nobody claims — and use the audit log to see who created each. Reviewing what the model says is worthless; review what the platform reports.
  • Why restrict forking of private repositories at the organization level?
    A fork is a full copy in another namespace. It leaves behind the rulesets, required reviews and scanning configured on the original, and it can outlive the contributor's involvement with the project. Restricting private forking keeps sensitive code inside the boundary you actually govern; where forking is genuinely needed, permit it per repository rather than organization-wide.

saying these in an interview costs you the question

  • Sets base permission to write for convenience
  • Grants admin because someone needed one setting
  • Invites individuals instead of using teams
  • Runs CI on a named engineer's personal token
  • Treats a private repo as isolation from the org

context