Checkpoint Lab — GitHub Apps, OAuth Apps, Webhooks, Checks API, and Event-Driven Integrations
Operate a signed-delivery checkpoint: verify, deduplicate, inject failure, simulate token expiry/redelivery, write a least-privilege App policy, and optionally publish a check.
Learning objectives
- Prove HMAC rejection and delivery-ID deduplication with durable evidence.
- Simulate installation-token expiry and classify retry versus authorization failure.
- Produce an App permission/event/reliability policy.
- Model explicit failed-delivery recovery and same-ID redelivery.
- Optionally publish and independently verify a least-privilege check run.
1. Checkpoint mission
You will operate a small event-driven integration boundary end to end: signed synthetic webhook deliveries enter a local receiver, the receiver verifies and deduplicates them, and one safe derived action is emitted in dry-run mode. You will replay the delivery, inject a bad signature, simulate an expired App token, write an App permission/event matrix, and optionally publish a real check result only if you intentionally created a disposable GitHub App.
2. Preflight and predictions
gh auth status
gh repo view OWNER/atlas-c27-integration-lab --json nameWithOwner,visibility,defaultBranchRef
cd atlas-c27-integration-lab
git status --short
git rev-parse HEAD
Record these predictions before acting:
-
The first valid delivery ID will create exactly one durable
receiver record and one
DRY_RUNderived-action log. - Re-sending the same delivery ID will return 2XX but will not create another record/action.
- A payload with a bad signature will be rejected before the business path and will not alter the database.
- An expired installation-token fixture will require “refresh then retry a safe read,” while a 403 fixture will require permission/policy diagnosis rather than token refresh.
3. Build the local receiver/sender from Lesson 2
Use the exact receiver.py and
send_fixture.py implementations from Lesson 2. Keep the
synthetic secret local:
export CH27_WEBHOOK_SECRET='chapter27-local-only'
python receiver.py
In a second shell:
export CH27_WEBHOOK_SECRET='chapter27-local-only'
python send_fixture.py --delivery 27272727-2222-4333-8444-555555555555
python send_fixture.py --delivery 27272727-2222-4333-8444-555555555555
python send_fixture.py --delivery 27272727-2222-4333-8444-555555555555 --bad-signature
Expected observations: 202 accepted, then
200 duplicate ignored, then
401 invalid signature.
4. Prove the predictions independently
python - <<'PY'
import sqlite3, json
with sqlite3.connect('chapter27-deliveries.db') as db:
rows=db.execute('SELECT delivery_id,event,action,payload_sha256 FROM deliveries ORDER BY accepted_at').fetchall()
print(json.dumps(rows, indent=2))
assert len(rows) == 1, rows
assert rows[0][0] == '27272727-2222-4333-8444-555555555555'
print('PASS: one durable event despite redelivery + rejected spoof')
PY
Do not count console lines as the only proof. The database uniqueness constraint is the independent state that explains why a redelivery does not create a second action.
5. Simulate installation-token expiry and classify retry
Create metadata only—never a token value:
{
"installation_id": 990027,
"repository": "learner-example/atlas-c27-integration-lab",
"permissions": {"issues": "read", "checks": "write"},
"expires_at": "2000-01-01T00:00:00Z",
"last_api_result": {"status": 401, "message": "Bad credentials"}
}
Your runbook should classify this as: preserve request ID/status →
confirm installation still exists → mint/retrieve a new short-lived
installation token using the App's trusted server-side credential
path → retry an idempotent read once → verify. Now change the
fixture status to 403; the correct runbook must
not simply refresh. It must inspect App
permissions, repository selection, organization policy, and endpoint
requirements.
6. Produce the permission/event matrix
Save INTEGRATION_POLICY.md with at least the following:
# Chapter 27 integration permission/event policy
## Identity
- Preferred service identity: GitHub App installation.
- Installation: selected disposable repositories by default.
- No machine user or human PAT for the production service.
## Events
- issues: subscribe only if issue-open/update behavior is required.
- check_run/check_suite: only if the App supports reruns/requested actions.
- No organization events unless a reviewed org-level feature requires them.
## Repository permissions
- Issues: read for issue-policy evaluation.
- Contents: read only when source inspection is required.
- Checks: write only for the optional check publisher.
- Administration: none.
## Reliability
- Verify X-Hub-Signature-256 on raw bytes.
- Store X-GitHub-Delivery as durable idempotency key.
- Acknowledge quickly, process asynchronously in production.
- Reconcile current API state for order-sensitive decisions.
- Redelivery is explicit; same delivery GUID must not duplicate effects.
## Credential lifecycle
- App JWT <= 10 minutes.
- Installation token expires after 1 hour.
- Private key/webhook-secret rotation is documented and tested.
- Credential leak response begins with revoke/rotate.
This file is not a GitHub-enforced permission itself. It is the reviewed design contract against which an App registration/installation can later be audited.
7. Model real delivery failure and redelivery
Write a recovery note with this sequence: receiver unavailable → GitHub records failure after timeout/failed response → alert operator → restore receiver → inspect recent delivery evidence → explicitly redeliver only failed relevant delivery → same delivery GUID reaches receiver → dedupe/state machine determines whether business work already committed. GitHub does not automatically perform that redelivery, and current GitHub.com delivery records/redelivery are available for the recent three-day window.
8. Optional live extension: publish one check with least privilege
Only perform this if you intentionally registered a disposable
GitHub App, installed it on only the lab repository, granted
Checks: write, and securely hold a current installation
access token. Creating the App private key/token is
security-sensitive and outside mandatory learning.
SHA=$(git rev-parse HEAD)
cat > check-run.json <<EOF
{
"name": "chapter27-checkpoint",
"head_sha": "$SHA",
"status": "completed",
"conclusion": "success",
"output": {
"title": "Chapter 27 integration checkpoint",
"summary": "Signed fixture accepted; duplicate suppressed; policy matrix reviewed."
}
}
EOF
# Run only with an ephemeral App installation token supplied securely to curl.
curl -L --request POST \
--url "https://api.github.com/repos/OWNER/REPO/check-runs" \
--header "Accept: application/vnd.github+json" \
--header "Authorization: Bearer INSTALLATION_ACCESS_TOKEN" \
--header "X-GitHub-Api-Version: 2026-03-10" \
--data @check-run.json
Independently verify with your normal read-capable GitHub CLI identity:
gh api -H "X-GitHub-Api-Version: 2026-03-10" \
repos/OWNER/REPO/commits/$SHA/check-runs \
--jq '.check_runs[] | select(.name=="chapter27-checkpoint") | {id,name,status,conclusion,head_sha,app:.app.slug}'
If you do not perform the optional live path, use the request/response fixture from Lesson 2 and document why “Checks write” remains outside mandatory permissions.
9. Final production operating model
| Control | Checkpoint evidence | Production requirement |
|---|---|---|
| Identity | Permission matrix chooses GitHub App | App owner, installation scope, permissions/events reviewed |
| Ingress authenticity | Bad signature rejected | HMAC-SHA256 raw-body validation; HTTPS; secret rotation |
| Replay/idempotence | Duplicate delivery produces one DB row | Durable delivery state machine/unique key |
| Ordering | Runbook requires current-state reconcile | Do not trust delivery order or immediacy |
| Credential lifecycle | Synthetic expiry decision | JWT max window + installation-token refresh/revocation handling |
| Feedback | Optional check fixture/live check | Checks write only where rich check feedback is required |
| Recovery | Explicit redelivery model | Delivery monitoring, bounded redelivery, same-ID dedupe, audit logs |
10. Cleanup/rollback
-
Stop
receiver.pyand remove the local SQLite DB if no longer needed. -
Commit
INTEGRATION_POLICY.mdonly if you want to retain the training design; do not commit any secret/token/key. - If a real App was created: uninstall it from the disposable repository, revoke/delete private keys, rotate/remove the webhook secret, revoke active installation tokens where applicable, and verify the App no longer has repository access.
- Archive/delete the disposable repository only if you intentionally want to end the lab; repository deletion itself is destructive and not required.
Knowledge check
The first delivery succeeded, but an operator redelivers it while investigating an outage. What prevents a duplicate action?
The durable X-GitHub-Delivery idempotency record/state machine. GitHub preserves the same delivery GUID on redelivery.
A webhook is correctly signed but refers to an installation/repository your service should not manage. May it proceed?
No. Signature validates origin/integrity, not business authorization. Validate event/action/installation/repository scope before action.
A short-lived installation token returns 401 after one hour. What is the safe retry pattern?
Confirm installation still exists, obtain a fresh token through the trusted App credential path, retry a safe/idempotent read once, and verify. Do not log the token.
Why is Checks write excluded from the mandatory permission matrix?
The mandatory derived action is dry-run and needs no GitHub write. Granting Checks write would add privilege only for the optional check-publishing requirement.
What is the key difference between handling 401 and 403 for App API calls?
401 points toward invalid/expired/revoked authentication; 403 points toward authorization, installation scope, policy, endpoint capability, or throttling. Refreshing credentials is not the universal 403 fix.
What Chapter 27 adds to the production GitHub operating model
You can now model external GitHub automation as a production service: dedicated App identity, selected installation scope, minimal permission/event matrix, cryptographically verified webhook ingress, durable replay/idempotency control, current-state reconciliation, short-lived token lifecycle, explicit failed-delivery recovery, and structured check feedback. Chapter 28 moves from one integration's authorization boundary to organization-wide teams, roles, repository access, enterprise policies, and delegated administration.
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.