In one line
Governance is deciding who can do what.
Start with your orgs. Then shape them with teams, policies and rulesets.
Rules flow downward, to repos and to Copilot.
Three org models 📖 Docs 🏢 Ricoh case
-
ORG BASE PERMISSION
theomonfort-org → Member privileges ↗
Organization settings → Access → Member privileges → Base permissions
Open the dropdown:
No permission,Read,Write,Admin.No permission = Single org / 🔴 Red. Write = 🟢 Green / 🟡 Sandbox. Changes apply to every member at once, so show it without saving.
First decision: how many orgs? Each model changes what everyone can see by default.
Granting access 📖 Docs
Now let people in: IdP → teams → repos. Never individuals.
flowchart LR
IDP["🪪 IdP (Okta)<br/>single source"]
ENT["🏛️ Enterprise Team 📖<br/>Admin · all orgs"]
ORG["🏢 Org Team 📖<br/>this org · from org chart"]
REPO["📦 Repository"]
IDP -->|"SCIM (EMU only)"| ENT
IDP -->|"SCIM / team sync"| ORG
ORG -->|Write etc.| REPO
ENT -->|Admin| REPO
classDef idp fill:#1a0a2e,stroke:#ffb000,color:#ffb000,stroke-width:2px
classDef ent fill:#2a0a0a,stroke:#ff5555,color:#ff5555,stroke-width:2px
classDef org fill:#0a0e27,stroke:#00f0ff,color:#00f0ff,stroke-width:2px
classDef repo fill:#0a1a14,stroke:#9bbc0f,color:#9bbc0f,stroke-width:2px
class IDP idp
class ENT ent
class ORG org
class REPO repo
click ENT href "https://docs.github.com/en/enterprise-cloud@latest/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/create-enterprise-teams" "Enterprise teams docs" _blank
click ORG href "https://docs.github.com/en/organizations/organizing-members-into-teams/about-teams" "Organization teams docs" _blank
Org vs enterprise teams 📖 Docs
Many orgs → enterprise team. One org → org team.
| 🏢 Org team | 🏛️ Enterprise team | |
|---|---|---|
| 🎯 Scope | One org | Many orgs |
| 🎫 Grants | Repo roles, org roles, Copilot | All of the left + enterprise roles |
| 🪪 IdP sync | ✅ EMU, ✅ personal accounts | ✅ EMU, ❌ personal accounts |
| 💰 Cost center | ❌ | ✅ members follow the team |
| 💬 @mention | @org/team | @/ent:team |
| 📂 CODEOWNERS | ✅ | ❌ (as of 2026-10-14) |
⚠️ Assigning an enterprise team to an org adds its members with no invite. Outside collaborators become enterprise members and use a license.
Policies
-
POLICIES FOR ENTERPRISE-ASSIGNED USERS
octodemo → AI controls → Copilot ↗
Enterprise → AI controls → Copilot → Policies for enterprise-assigned users
Open the dropdown:
Enabled,Disabled.Disabled = every policy left on "Let organizations decide" is off for users licensed directly by the enterprise. Show it without saving.
People are in. Policies decide what they may do — set at org and enterprise, never at the repo.
- 🏛️ Enterprise — SSO / SCIM, allowed features, base policy for every org
- 🤖 Copilot models — the global model policy sets the default for new models. Enterprise teams can get extras (preview ↗).
- 🏢 Org — member privileges, repo creation, 2FA, Copilot and Actions access
- 📦 Repo — holds no policy, only inherits. The one thing a repo adds is a ruleset.
- 🔁 Rules flow down. An org can tighten enterprise rules, never loosen them.
- 👤 Copilot license, no org: “Let organizations decide” doesn’t apply. The enterprise’s Policies for enterprise-assigned users setting decides instead; if it is disabled, those features are off. Copilot policies ↗
🎯 Set guardrails top-down. Never repo by repo. Org policies ↗ · Enterprise policies ↗
Admin roles 📖 Docs
Policies are set. Now, who may change them? That is not a repo role.
- 🏛️ Enterprise owner — every setting and policy, yet no org settings or content by default
- 🏢 Org owner — full control of one org. Keep it small, never below two.
- 🛡️ Security manager — read on every repo plus alert management. Security teams need no Owner
- 🧩 Custom org roles — bundle only what is needed, e.g. “view the audit log” (GHEC)
🎯 Owner is a key, not a job title. Look for a smaller role first. Org roles ↗ · Custom org roles ↗
Repository roles
Now inside the repo: who does what. Roles are cumulative.
| Role | Adds to the role below |
|---|---|
| 👀 Read | View, clone, open issues |
| 🔺 Triage | Manage issues and PRs — label, assign, close |
| ✍️ Write | Push and merge |
| 🛠️ Maintain | Non-destructive repo settings |
| 👑 Admin | Full control — access, visibility, deletion |
🧩 If none fit, build a custom repository role at org level from any base role. Custom roles ↗
Rulesets 📖 Docs
-
CREATE CUSTOM PROPERTY
Prerequisites: dedicated demo repos, org owner, GitHub Team / Enterprise, one colleague with Write access for approval.
theomonfort-org → Custom properties ↗
Organization settings → Repository → Custom properties → New property
Name:
business_criticality
Description:Business impact of service disruptionType: Single select
Values:critical,standard,experimentalAllow repository actors to set this property: OFF (central control prevents repo-level opt-out)
Require this property for all repositories: OFF
Default: none → Save property -
ASSIGN REPO VALUES
Same Custom properties page → Set values → select existing demo repos → Edit properties
business_criticality=critical→ Save changesRepeat for another demo repo, setting it to
experimental.Example names only: payments-api / order-service = critical; recommendations-lab = experimental. Substitute your actual demo repos.
-
CREATE ONE ORG RULESET
Organization settings → Repository → Rulesets → New ruleset → New branch ruleset
Ruleset name:
Critical services: reviewed changes
Enforcement status: Active
Bypass list: leave emptyTarget repositories: Repositories matching a filter
Filter:props.business_criticality:criticalTarget branches: Add a target → Default branch
Require a pull request before merging: ON
Required approvals: 1
Dismiss stale pull request approvals when new commits are pushed: ON
Require conversation resolution before merging: ONBlock force pushes: ON
Restrict deletions: ONRestrict updates: OFF
Require status checks to pass before merging: OFF
Require signed commits: OFF → Create -
VERIFY THE PAYOFF
Push a new change directly to a critical repo's default branch → rejected. Open a PR from a working branch → one other person's approval required.
Custom properties → Set values → select the experimental demo repo → Edit properties →
business_criticality=critical→ Save changesIn that repo's Settings → Rules → Rulesets, show the same org ruleset applies. Change the classification, not the ruleset.
Policies say what is allowed, roles say who acts. Rulesets say what the code must pass.
- 🛡️ Branch protection’s successor — reviews, checks, signing, force-push bans, in one object
- 🏛️ Defined at ENT / ORG / REPO — set it high and every repo below inherits it
- 🔁 They stack — when several apply, the strictest wins. No loosening below
- 🧪 Evaluate mode — measure impact without enforcing. Start here on existing repos
🎯 Don’t grant bypass. A ruleset with bypass is a request, not a rule. Org rulesets ↗
12 anti-patterns
That is the structure. Here is what breaks it. 01, 02 and 11 are the hard ones to undo.
The 21 guardrails 📖 Well-Architected
Values to set, and where. 19 to 21 are Copilot. Short on time? 03, 15, 18.
Copilot managed settings (NEW)
-
SERVER-MANAGED (.github-private)
theomonfort-org/.github-private → copilot/managed-settings.json ↗
The baseline for everyone in the enterprise. Show
permissions,enabledPluginsandallowedMcpServers.teams/andteam-mappings.jsonin the same folder override it per team.Push to the default branch: applied within about an hour. Restart the client or sign in again to apply it now.
-
FILE-BASED (file on the device)
In Finder, press ⌘⇧G and paste the path:
/Library/Application Support/GitHubCopilot/A
managed-settings.jsonsits there (on this Mac:telemetrysettings).Same JSON as server-managed. The CLI only reads it if it's owned by root and not group- or world-writable. Restart the client after a change.
-
MDM (managed preferences)
In Terminal, run:
plutil -p "/Library/Managed Preferences/com.github.copilot.plist"On this Mac it shows
"remoteControl.mode" => "disabled".MDM doesn't deploy a JSON file: it pushes dot-separated keys with string values (Intune and similar tools). When settings overlap, MDM → server-managed → file win in that order.
Same idea for Copilot clients: copilot/managed-settings.json overrides local settings. Order: MDM → server-managed → file → user. Get started ↗ · All keys ↗
.github-private & source org
They live in one repo you own, set in Enterprise → AI controls → Agents.
.github-private/
├── agents/ # published enterprise-wide
├── .github/agents/ # staging, test before publishing
└── copilot/
├── managed-settings.json # the baseline
├── team-mappings.json # file → enterprise team
└── teams/*.json # per-team override
- 🏢 You pick the org. The repo name and
copilot/paths are fixed. - 🔒 Applies to everyone on the plan, repo access or not. Keep it internal and guard
copilot/**with CODEOWNERS. - ✅ Config errors show up under Copilot settings validation. 2026-09-25 ↗
★ Where it fits
Five layers, one rule: set them from the top.
| Layer | Scope | Examples |
|---|---|---|
| 🏢 Policies | org → enterprise | 2FA, visibility, feature access |
| 🔑 Admin roles | org → enterprise | Owner, Security manager, custom |
| 👤 Permission roles | Repository | Read / Write / Admin |
| 🛡️ Rulesets | Branches and tags | Required reviews, required checks, signing |
| 🤖 Managed settings | Copilot clients | Default model, bypass lock, plugins |
🎯 Top-down wins. Per-repo does not scale.
For cost allocation and spending controls, see Cost centers & budgets ↗.