Gitfence

Monorepo Permissions for Per-Team Access in One Repo

Git and GitHub have no per-folder read permissions, so how do teams restrict access inside a monorepo? Options, tradeoffs and configs for per-team access.

Gitfence team5 min read

Short answer: On GitHub, everyone who can read a monorepo can read all of it. You can control who changes each team's folders with CODEOWNERS, branch protection and push rulesets, but per-team read access isn't available natively. Teams that need it either split the sensitive parts out of the monorepo, or serve each team a filtered view of it.

That's the main tradeoff of a monorepo. This article covers what works for write ownership, what the options are for read restrictions, and how to choose.

Why monorepos and access control collide

A monorepo's benefits come from everyone seeing everything: atomic cross-project changes, one dependency graph, shared tooling, easy refactors. Git reinforces that: a clone contains every commit and every commit covers the whole tree.

GitHub's permission model matches. Repository roles (Read, Triage, Write, Maintain, Admin) apply per repository, and custom roles on Enterprise Cloud are still repository-scoped. A long-running GitHub community request for folder-level access control describes the result: "developers with access to the Mono repository gain visibility and access to all folders."

For most internal teams that's fine. It stops being fine when:

  • A small part of the repo is core IP (models, pricing, detection logic) and the company is growing.
  • Contractors, agencies or offshore teams work on one product area.
  • AI agents run against the repository and you want them scoped to their task.
  • Regulation or a customer contract requires limiting who sees certain code.

Per-team write ownership (native)

CODEOWNERS by team

# .github/CODEOWNERS
*                    @acme/platform
/apps/web/           @acme/web
/apps/mobile/        @acme/mobile
/services/billing/   @acme/billing
/packages/ui/        @acme/design-system
/.github/            @acme/platform

Per GitHub's docs, the last matching pattern wins, code owners are requested for review automatically, and teams listed must be visible and have write permission. Turn on "Require review from Code Owners" in branch protection or a ruleset to make the approval mandatory. Protect CODEOWNERS itself by owning /.github/.

Push rulesets

Push rulesets can block pushes that change specified paths in private and internal repositories, with bypass lists. Useful for paths nobody should change outside a specific process.

Path-filtered CI

on:
  pull_request:
    paths:
      - "services/billing/**"

Run each team's checks only for their folders, and require them as status checks.

None of this affects reading. See CODEOWNERS is not access control.

Per-team read access: the options

1. Split the sensitive part out

Move services/billing/ into its own repository and publish what others need as a package. This is the native GitHub answer and what Git itself recommends for private data (gitnamespaces).

Tradeoffs: you lose atomic changes across the boundary, add package versioning, and the teams outside can't run code that depends on it without the published package.

2. Hybrid: monorepo plus private satellite repos

Keep most code in the monorepo and put the few sensitive modules in private repos pulled in as submodules or packages. Cheaper than splitting everything, but the boundary friction remains for the modules that matter most.

3. Sparse checkout per team

Useful for performance (each team checks out only its folders), not for access control. Every object is still fetchable. Details in Git sparse checkout vs real access control.

4. Projections (Josh, Copybara, filtered mirrors)

Tools like Josh can serve a subfolder of a monorepo as its own repository with history, and Copybara moves code between a private and a public or partner repo with transformations. They solve the mechanics of filtered history; authentication, per-team policy and moving contributions back are yours to build and run.

5. Leave Git

Systems with centralized servers (Perforce, Subversion) can enforce path-based read permissions because clients don't hold the full history. Few teams will switch VCS for this, but it's the reason those systems are still used for some game and hardware codebases.

Choosing

Situation Reasonable choice
All employees, no sensitive modules Monorepo, CODEOWNERS, no read restrictions
One or two sensitive modules, mostly trusted staff Move those modules to private repos
External teams on specific product areas Filtered views per team
AI agents scoped per task Filtered views per agent
Regulatory separation for a large part of the code Separate repositories, possibly separate orgs

How Gitfence gives each team its slice

Gitfence sits in front of GitHub. GitHub stays the source of truth, and the full-access core team works there with nothing changed. Everyone else, people and AI agents, clones a Gitfence view of the monorepo containing only their slice.

You set rules per folder or file, per group, with per-person exceptions. In the permissions screen, an agency group's rules might read like this:

group: mobile-agency
  apps/mobile/          Can edit
  packages/ui/          Can read
  packages/api-client/  Types only
  services/             No access
  infra/                No access
  • Types only replaces a hidden TypeScript module with generated declarations (literal values redacted), so apps/mobile still type-checks against packages/api-client without its source.
  • Pushes become pull requests. Each changed path is validated against the person's permissions; CI config, .gitattributes and .gitmodules are read-only by default. Accepted changes are opened as normal GitHub PRs, where your CODEOWNERS, rulesets and reviews still apply.
  • Tests on the full monorepo. Fenced CI runs the test suite on the real code in a sandbox without network access, with hidden source redacted from output.
  • Rules change without force pushes. Views are forward-only: granting or revoking access adds a commit, and everyone's git pull stays a fast-forward.
  • Users with identical permissions share a view, so the number of views grows with the number of distinct permission sets, not people.

Limits: it's GitHub-only today and Types only covers TypeScript. It controls what each team sees from now on, not what they cloned earlier. Commits that touch both hidden and visible files keep their messages. Large-repository performance depends on your history size; try it on a copy first.

Pricing counts only restricted people; full-access teammates are free. See pricing, how it works and compare.

Practical tips for any approach

  • Keep secrets out of the monorepo entirely; no folder rule fixes a credential in history.
  • Own CODEOWNERS and .github/ with a platform team.
  • Make boundaries explicit in code too: a module's public API in one entry file makes splitting or fencing much easier.
  • Review access when people change teams, not just when they leave, and start new hires narrow (onboarding without full codebase access).

FAQ

Can I restrict a team to certain folders in a GitHub monorepo?

You can restrict what they can change with CODEOWNERS, branch protection and push rulesets. You can't restrict what they can read on GitHub alone.

How do large companies handle monorepo permissions?

Some run custom version control or servers that enforce path-level rules; others keep sensitive code in separate repositories. Public descriptions vary and most internal setups aren't documented, so be cautious about copying any one pattern.

Do monorepo build tools like Nx, Turborepo or Bazel restrict access?

They can enforce module boundaries and visibility rules at build time (which project may import which). That's dependency hygiene, not access control: the source is still in everyone's clone.

Does CODEOWNERS work with GitHub teams?

Yes. Use @org/team-name; the team must be visible and have write access to the repository.

Should we split our monorepo instead?

If only a few modules are sensitive, splitting those out or fencing them is usually less work than splitting everything. Split broadly only if teams are truly independent.

Fence your first repository in minutes

Free for one repository and your own team. Add a restricted person and Pro is free for 14 days, no card.