Chapter 28Lesson 05~240 minutes

Checkpoint Lab — Organizations, Teams, Roles, Repository Access, Enterprise Policies, and Delegated Administration

Design and verify a three-repository organization access model through onboarding, temporary elevation, offboarding, and a quarterly access-review runbook.

CheckpointRole matrixBreak-glassAccess reviewGovernance

Learning objectives

  • Design engineering, security, and release teams across three repositories.
  • Predict and verify onboarding and temporary-elevation effects before applying them.
  • Prove offboarding removes every modeled access path, not merely one membership.
  • Write a quarterly access-review checklist and break-glass procedure.
  • Bridge organization governance into Chapter 29 audit/compliance evidence.

1. Checkpoint scenario and preflight

You are designing synthetic organization atlas-c28 with repositories app-api, security-policy, and release-ops. Persistent teams are Engineering, Security, and Release. The mandatory lab is local and free. Optional live verification may use a disposable GitHub organization you own. No real colleague identities, employer organization, custom enterprise roles, SSO changes, token creation, repository transfer, or deletion are required.

Preflight: Python 3 is required for the local evaluator. For optional hosted inspection, require gh auth status and confirm the exact disposable organization/repository before every mutation.

2. Design the least-privilege role matrix

Team app-api security-policy release-ops Why
Engineering Write Read Read Build application; inspect policy/release context without administering it.
Security Read Maintain Read Review source and own security policy without source/release write.
Release Read Read Maintain Operate release automation while keeping app source write separate.

Set base permission to none in the fixture. Predict two changes before running: (P1) onboarding devon into Engineering should yield Write/Read/Read; (P2) temporary Maintain on release-ops should raise only that repository, and removing the exception should fall back to Read.

3. Build the final model and evaluator

Save checkpoint.json with the role matrix. Do not use nested Engineering in this final matrix yet; nesting is introduced as a controlled failure later.

{
  "base_permission":"none",
  "repositories":["app-api","security-policy","release-ops"],
  "teams":{
    "engineering":{"parent":null,"repos":{"app-api":"write","security-policy":"read","release-ops":"read"}},
    "security":{"parent":null,"repos":{"app-api":"read","security-policy":"maintain","release-ops":"read"}},
    "release":{"parent":null,"repos":{"app-api":"read","security-policy":"read","release-ops":"maintain"}},
    "break-glass-release":{"parent":null,"repos":{"release-ops":"maintain"}}
  },
  "users":{
    "devon":{"active":true,"relationship":"member","teams":["engineering"],"direct":{}},
    "sam":{"active":true,"relationship":"member","teams":["security"],"direct":{}},
    "riley":{"active":true,"relationship":"member","teams":["release"],"direct":{}}
  }
}
#!/usr/bin/env python3
"""c28_access.py — local, synthetic GitHub access evaluator."""
import json, sys
from pathlib import Path
RANK = {"none":0,"read":1,"triage":2,"write":3,"maintain":4,"admin":5}

def max_role(*roles):
    return max(roles, key=lambda r: RANK[r])

def ancestors(team, teams):
    out=[]; seen=set(); cur=team
    while cur and cur not in seen:
        seen.add(cur); parent=teams.get(cur,{}).get("parent")
        if parent: out.append(parent)
        cur=parent
    return out

def effective(model, user, repo):
    u=model["users"][user]
    if not u.get("active", True): return {"role":"none","sources":["identity inactive"]}
    sources=[]; roles=[]
    if u["relationship"]=="member":
        roles.append(model["base_permission"]); sources.append(f"base:{model['base_permission']}")
    if repo in u.get("direct",{}):
        roles.append(u["direct"][repo]); sources.append(f"direct:{u['direct'][repo]}")
    for t in u.get("teams",[]):
        for current in [t]+ancestors(t, model["teams"]):
            grant=model["teams"].get(current,{}).get("repos",{}).get(repo)
            if grant: roles.append(grant); sources.append(f"team:{current}:{grant}")
    return {"role":max_role(*(roles or ["none"])),"sources":sources}

m=json.loads(Path(sys.argv[1]).read_text())
for user in m["users"]:
    for repo in m["repositories"]:
        print(user, repo, json.dumps(effective(m,user,repo)))
python c28_access.py checkpoint.json

Verify P1 from the output before continuing.

4. Simulate time-bound elevation

For incident INC-C28-0042, temporarily add Devon to break-glass-release. Record a synthetic approval and expiry in elevation.json:

{
  "principal":"devon",
  "team":"break-glass-release",
  "reason":"INC-C28-0042 release recovery",
  "approved_by":"release-duty-manager",
  "starts":"2026-08-19T19:00:00Z",
  "expires":"2026-08-19T20:00:00Z"
}

Add the team, run the evaluator, and confirm only release-ops rises to Maintain. Then remove the team and prove it returns to Engineering's Read. This verifies P2 and demonstrates that break-glass elevation is a state transition with evidence and expiry.

5. Inject a nested-team failure and diagnose it

Change security so its parent is engineering. Predict the effect: Security already has Read on app-api, but it now inherits Engineering Write, so Sam unexpectedly gains Write. Run the evaluator and preserve the failure output.

Repair by restoring security.parent to null. Rerun and prove Sam returns to Read on app-api. This is the chapter's policy-failure exercise: hierarchy placement can expand repository authorization.

6. Offboard Devon completely

Before action, predict that removing only Engineering membership will remove persistent team access but any forgotten direct/elevation grant could survive. Perform the complete synthetic sequence: remove all teams, clear direct grants, mark the identity inactive, and verify none across all repositories. In a real environment, also remove/disable the authoritative IdP or organization identity state, reassign owned automation/release duties, revoke personal credentials where policy requires, and review Apps/deploy keys/secrets that were personally controlled.

7. Produce the quarterly access-review checklist

Quarterly GitHub access review — atlas-c28
[ ] Export/inspect current organization members, owners, outside/repository collaborators.
[ ] Confirm every owner is still a continuity/high-impact administrator; keep appropriate redundancy.
[ ] Inventory teams, parent/child relationships, maintainers, and synchronized-IdP ownership.
[ ] For every repository, enumerate team grants + direct grants + base permission + org roles.
[ ] Flag direct grants without business owner, reason, and expiry.
[ ] Reconcile temporary/break-glass grants against expiry records.
[ ] Review custom/predefined organization roles and all-repository permissions where available.
[ ] Review internal-repository visibility assumptions at enterprise scope.
[ ] Confirm departed/transferred users have no residual team/direct/role/credential ownership.
[ ] Sample high-risk repositories and independently prove effective access for representative users.
[ ] Record exceptions, remediation owner, deadline, and evidence link.
[ ] Preserve review date, reviewer, scope, and completion evidence for Chapter 29 audit work.

8. Optional hosted verification

If you own a disposable organization, compare the local model with hosted state. Use only synthetic users/teams you control. Read operations first:

ORG="octo-c28-governance-lab"
gh api --paginate -H "X-GitHub-Api-Version: 2026-03-10" "orgs/$ORG/teams"   --jq '.[] | {slug,parent:(.parent.slug // null),privacy}'
gh api --paginate -H "X-GitHub-Api-Version: 2026-03-10" "orgs/$ORG/members"   --jq '.[].login'

Do not publish the output if it contains real personal identities. Optional grant/revoke steps from Lesson 2 may be repeated only in the disposable organization. Cleanup removes the grant/team/member created for the lab and verifies absence; it does not delete the organization.

9. Access governance runbook

Event Action Independent verification
Onboard Provision identity → add member → add function team(s) → no broad direct grants. Calculate/inspect effective access on representative repositories.
Role change Add new function team before removing old only if overlap is approved and time-bounded. Verify final access matches new role, not union of both jobs.
Temporary elevation Use dedicated elevation team/role; record approver/reason/expiry. After expiry, prove elevated source is absent and effective role fell back.
Offboard Disable authoritative identity; remove org/team/direct/role access; reassign ownership; revoke applicable credentials. Enumerate all access sources and test representative resources.
Quarterly review Reconcile identity source, organization, teams, direct grants, roles, enterprise policy. Preserve signed/dated review evidence and exception remediation.

Knowledge check

During the checkpoint, why did nesting Security under Engineering unexpectedly increase Sam's access?

What proves temporary break-glass access was removed?

Why is a quarterly review based only on the repository People page incomplete?

What should happen to organization ownership during normal onboarding?

What does Chapter 28 add to the production operating model?

What evidence does Chapter 29 need from this checkpoint?

Summary and bridge to Chapter 29

Chapter 28 adds the access-governance plane to the GitHub operating model. Persistent access follows teams and task-specific roles; broad ownership is exceptional; temporary elevation is explicit and expiring; offboarding proves every access path is gone; and periodic reviews reconcile effective access rather than trusting one grant list. Chapter 29 will turn these controls and their changes into audit logs, compliance evidence, security policy, and enterprise governance records.

Next chapter

Audit Logs, Security Policies, Compliance Evidence, Repository Governance, and Enterprise Controls: Concepts, Architecture, and Mental Model

Official references

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.