Chapter 29Lesson 05~240 minutes

Checkpoint Lab — Audit Logs, Security Policies, Compliance Evidence, Repository Governance, and Enterprise Controls

Produce a compact compliance-evidence packet from controlled repository changes and realistic audit fixtures, including ownership, review cadence, exceptions, retention, and verification.

CheckpointEvidence packetControl mappingExceptionsQuarterly review

Checkpoint outcomes

  • Predict repository/policy/evidence changes before performing them.
  • Collect Git, hosted-setting, and realistic audit evidence into one controlled packet.
  • Prove raw/normalized evidence integrity and explain provenance limits.
  • Define severity-independent governance ownership, review frequency, exception path, and retention location.
  • Produce an incident-friendly quarterly access/control review checklist.

1. Scenario, preflight, and assumptions

You are handing the fictional service-api repository to an operations team that must demonstrate three controls: vulnerability reporting is documented, governance files have owners, and high-impact repository/access/security-policy changes can be traced to appropriate evidence. The mandatory lab uses GitHub Free, a public disposable repository, Git, GitHub CLI, Python 3, and synthetic audit events. Enterprise Cloud audit API/streaming remains optional.

gh auth status
python --version
git --version

OWNER="YOUR_LOGIN"
REPO_NAME="c29-governance-evidence-lab"
REPO="$OWNER/$REPO_NAME"

gh repo view "$REPO" --json nameWithOwner,visibility,isArchived,defaultBranchRef   || echo "Run Lesson 2 setup or create a fresh disposable public repository"

2. Write predictions before changing state

Record predictions in governance/evidence/PREDICTIONS.md before you perform the checkpoint. At minimum predict these three outcomes:

  1. Changing SECURITY.md and the control matrix will advance the Git commit SHA; it will not by itself create a ruleset.
  2. Creating/updating a repository ruleset or team permission in an organization would be a hosted configuration change with a documented audit-event family such as repository_ruleset.create or team.update_repository_permission; a normal Git commit is not equivalent evidence.
  3. Modifying the retained raw audit fixture after hashing it will produce a different SHA-256 and invalidate the recorded manifest.

Prediction makes causality testable. Without it, learners often reinterpret observations after the fact.

3. Perform controlled repository policy actions

mkdir -p .github governance/evidence
cat > SECURITY.md <<'EOF'
# Security Policy

## Supported versions
Only the current default branch of this disposable lab is supported.

## Reporting
Use fictional training reports only. Never paste real secrets or private findings.
Training contact: learner@example.invalid
EOF

printf '/SECURITY.md @%s
/governance/ @%s
' "$OWNER" "$OWNER" > .github/CODEOWNERS

cat > governance/CONTROL_MATRIX.md <<'EOF'
# Chapter 29 Control Matrix

| ID | Objective | GitHub control/state | Evidence | Owner | Review | Exception | Retention |
|---|---|---|---|---|---|---|---|
| GOV-01 | Publish vulnerability reporting process | SECURITY.md | commit + blob SHA | Security | quarterly | EXCEPTIONS.md | Git + evidence archive |
| GOV-02 | Governance paths have accountable owners | CODEOWNERS | commit + blob SHA | Platform | quarterly | EXCEPTIONS.md | Git + evidence archive |
| GOV-03 | Protected ref changes follow policy | repository ruleset | ruleset GET + audit event where available | Platform | quarterly | expiring approved exception | external evidence archive |
| GOV-04 | Access changes are traceable | team/repository role | access state + audit event where available | IAM | quarterly | expiring approved exception | external evidence archive |
EOF

cat > governance/EXCEPTIONS.md <<'EOF'
# Governance Exceptions

Every exception must include: ID, control, scope, reason, owner, approver,
start time, expiry time, compensating control, and closure evidence.

No active exceptions in this checkpoint.
EOF

git add SECURITY.md .github/CODEOWNERS governance/CONTROL_MATRIX.md governance/EXCEPTIONS.md
git diff --cached --check
git commit -m "Complete Chapter 29 governance checkpoint"
git push
CHECKPOINT_SHA=$(git rev-parse HEAD)
echo "$CHECKPOINT_SHA"

Verify prediction 1 independently with GitHub's content API and the ruleset API:

gh api -H "X-GitHub-Api-Version: 2026-03-10"   "repos/$REPO/contents/SECURITY.md?ref=$CHECKPOINT_SHA" --jq '{path,sha,size}'
gh api -H "X-GitHub-Api-Version: 2026-03-10"   "repos/$REPO/contents/.github/CODEOWNERS?ref=$CHECKPOINT_SHA" --jq '{path,sha,size}'
gh api -H "X-GitHub-Api-Version: 2026-03-10"   "repos/$REPO/rulesets" --jq '[.[] | {id,name,enforcement}]'

4. Collect realistic audit evidence without requiring Enterprise

Create governance/evidence/audit-raw.json using the synthetic event set below. These documented action names represent access/ruleset/security-configuration changes that an organization/enterprise audit log can record; the values are deliberately fictional.

[
  {"_document_id":"c29-001","@timestamp":1787175000000,"action":"team.add_repository","actor":"learner-example","org":"octo-c29-fixture","repo":"octo-c29-fixture/service-api","team":"octo-c29-fixture/release","permission":"push","request_id":"REQ-C29-001"},
  {"_document_id":"c29-002","@timestamp":1787175600000,"action":"team.update_repository_permission","actor":"learner-example","org":"octo-c29-fixture","repo":"octo-c29-fixture/service-api","team":"octo-c29-fixture/release","old_repo_permission":"push","new_repo_permission":"maintain","request_id":"REQ-C29-002"},
  {"_document_id":"c29-003","@timestamp":1787176200000,"action":"repository_ruleset.create","actor":"learner-example","org":"octo-c29-fixture","repo":"octo-c29-fixture/service-api","ruleset_name":"main-governance","ruleset_enforcement":"active","request_id":"REQ-C29-003"},
  {"_document_id":"c29-004","@timestamp":1787176800000,"action":"security_configuration.create","actor":"learner-example","org":"octo-c29-fixture","security_configuration_name":"public-baseline","request_id":"REQ-C29-004"}
]

If you have real Enterprise Cloud organization-owner access, you may replace this fixture with a small redacted/time-bounded export from a disposable organization. Never commit real organization audit data into this public lab repository.

5. Build the evidence packet and chain-of-custody manifest

from __future__ import annotations
import csv
import hashlib
import json
from pathlib import Path
from datetime import datetime, timezone

root = Path("governance/evidence")
raw = root / "audit-raw.json"
normalized = root / "audit-normalized.csv"
manifest = root / "SHA256SUMS.txt"
report = root / "EVIDENCE_PACKET.md"

def digest(path: Path) -> str:
    h = hashlib.sha256()
    with path.open("rb") as f:
        for chunk in iter(lambda: f.read(65536), b""):
            h.update(chunk)
    return h.hexdigest()

events = json.loads(raw.read_text(encoding="utf-8"))
events.sort(key=lambda e: e.get("@timestamp", 0))
with normalized.open("w", newline="", encoding="utf-8") as f:
    fields = ["timestamp_utc","document_id","action","actor","org","repo","target","request_id"]
    w = csv.DictWriter(f, fieldnames=fields)
    w.writeheader()
    for e in events:
        stamp = datetime.fromtimestamp(int(e["@timestamp"])/1000, tz=timezone.utc).isoformat()
        target = e.get("team") or e.get("ruleset_name") or e.get("security_configuration_name") or ""
        w.writerow({"timestamp_utc":stamp,"document_id":e.get("_document_id",""),"action":e.get("action",""),"actor":e.get("actor",""),"org":e.get("org",""),"repo":e.get("repo",""),"target":target,"request_id":e.get("request_id","")})

report.write_text("""# Compliance Evidence Packet

## Control objective
Trace governance policy, ownership, protected-ref policy, and access changes.

## GitHub setting/rule
SECURITY.md, CODEOWNERS, repository ruleset state, and access/security-policy audit events.

## Evidence
- Git commit and GitHub content/blob SHAs for policy files
- Read-only ruleset state
- Raw synthetic audit fixture plus normalized report

## Owner
Security / Platform / IAM according to CONTROL_MATRIX.md.

## Review frequency
Quarterly, plus incident-triggered review.

## Exception path
EXCEPTIONS.md; every exception has owner, approver, scope, expiry, and closure evidence.

## Retention location
Training repository plus an independently controlled evidence archive in production.
""", encoding="utf-8")

paths = [raw, normalized, report, Path("SECURITY.md"), Path(".github/CODEOWNERS"), Path("governance/CONTROL_MATRIX.md"), Path("governance/EXCEPTIONS.md")]
manifest.write_text("".join(f"{digest(p)}  {p.as_posix()}\n" for p in paths), encoding="utf-8")
print(f"events={len(events)}")
print(manifest.read_text(encoding="utf-8"), end="")

Expected: events=4 plus seven hash entries. Prediction 3 can now be tested safely on a copy of the raw fixture: modify the copy and confirm its SHA differs; do not alter the retained source.

cp governance/evidence/audit-raw.json /tmp/audit-mutated.json
printf '
' >> /tmp/audit-mutated.json
ORIGINAL=$(sha256sum governance/evidence/audit-raw.json | awk '{print $1}')
MUTATED=$(sha256sum /tmp/audit-mutated.json | awk '{print $1}')
printf 'original=%s
mutated=%s
' "$ORIGINAL" "$MUTATED"
test "$ORIGINAL" != "$MUTATED" && echo "PASS: byte change detected"

6. Optional live organization evidence

If you control a disposable organization, make only a harmless, pre-approved lab change—for example add a disposable team to a disposable repository with the minimum needed role—and immediately inspect/revoke it. Before the change, predict the effective access and expected team.add_repository event. Afterward, verify current access and search/export the audit event. This is optional because it changes organization authorization.

Security-sensitive: do not add a real colleague merely for the lab, do not grant Admin, and do not broaden organization ownership. Removing the lab team's repository grant is part of cleanup. If your account/plan cannot expose the relevant event/API, use the fixture instead.

7. Final control/evidence matrix

Control objective GitHub setting/rule Evidence Owner Review Exception Retention
Safe vulnerability reporting SECURITY.md commit + blob SHA Security Quarterly Documented policy exception Git + evidence archive
Governance ownership CODEOWNERS commit + blob SHA; enforcement state separately Platform Quarterly Temporary alternate reviewer Git + evidence archive
Protected ref policy Repository/org ruleset ruleset GET + repository_ruleset.* event where available Platform Quarterly + on change Owner/expiry/compensating control External evidence archive
Repository access Team/direct role effective access + team.*/org.* events IAM Quarterly + offboarding Time-bounded elevation External evidence archive
Security feature baseline Security configuration configuration attachment/state + security_configuration.* events Security Quarterly Risk acceptance with expiry External evidence archive

8. Quarterly access/control review checklist

  1. Confirm policy owner and backup owner for every control.
  2. Compare SECURITY.md/CODEOWNERS/control matrix at default-branch HEAD with approved policy.
  3. Inventory active repository and organization rulesets; identify unexpected bypass actors.
  4. Review effective team/direct access, outside collaborators, owners, security managers, Apps, and temporary elevation.
  5. Confirm security-configuration coverage for repositories in scope and distinguish free from licensed features.
  6. Review recent high-impact audit events using absolute time windows and supported action qualifiers.
  7. Find exceptions expiring before the next review; close or formally renew them.
  8. Verify the external evidence archive can be read by responders and cannot be silently overwritten by ordinary administrators.
  9. Sample hashes/manifests and compare retained raw events with normalized reports.
  10. Record drift, remediation owner, target date, and follow-up evidence.

9. Independent verification

git status --short
git log --oneline -5
cat governance/evidence/SHA256SUMS.txt
sha256sum -c governance/evidence/SHA256SUMS.txt

gh repo view "$REPO" --json nameWithOwner,visibility,isArchived,defaultBranchRef
gh api -H "X-GitHub-Api-Version: 2026-03-10" "repos/$REPO/rulesets"   --jq '[.[] | {id,name,enforcement,target}]'

The verification uses two independent state families: Git/repository content identity and hosted ruleset state. If an optional organization audit source exists, add its exported/raw-event hash and retrieval query to the packet rather than replacing these repository checks.

10. Cleanup and rollback

Archive the disposable repository after the checkpoint so evidence remains available but active development stops. If you used an optional organization team/grant/ruleset, restore the pre-lab access/policy state and verify the reversal. Do not delete the repository/organization or rewrite history as “cleanup”; those operations would remove the evidence the lab is designed to preserve.

gh repo archive "$REPO" --yes
gh repo view "$REPO" --json isArchived --jq '.isArchived'
# Expected: true

11. What Chapter 29 adds to a production GitHub operating model

Earlier chapters built controls: identity, reviews, Actions permissions, environments, packages, dependency/code/secret scanning, provenance, APIs, integrations, and organization access. Chapter 29 adds the governance evidence plane: policy owners define intent; GitHub controls enforce selected transitions; audit/Git/API state records evidence; external retention protects investigation continuity; exceptions are explicit and expiring; periodic review detects drift.

Chapter 30 moves from governance evidence to developer environments and documentation delivery: Codespaces, dev containers, GitHub Pages, and cloud developer environments. The same governance model will apply there—developer convenience must still have reproducible configuration, scoped secrets, ownership, and observable change.

Knowledge check

Prediction: you edit SECURITY.md and push a commit. Should a repository ruleset suddenly exist?

Why is the raw audit fixture retained after the normalized CSV is produced?

A ruleset exists and an audit event proves it was created. Does that prove it blocked every unsafe change?

An exception says “temporary” but has no expiry. What governance failure exists?

Why is deleting the disposable repository a poor default cleanup for this chapter?

What is the correct next course topic after this governance evidence plane?

Summary

You completed a governance evidence loop without requiring Enterprise: explicit control objectives, policy/ownership files, hosted-state inspection, realistic audit events, normalized evidence, cryptographic integrity checks, owner/review/exception/retention metadata, independent verification, and reversible cleanup. The production extension is not “more logs”; it is durable evidence tied to enforceable controls and accountable operations.

Next chapter

Codespaces, Dev Containers, GitHub Pages, Documentation, and Cloud Developer Environments: Concepts, Architecture, and Mental Model

Further reading — current primary sources

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.