PAT Rotation and Diagnostics#
The GH_PAT secret is a GitHub Personal Access Token that allows the CI/CD
pipeline to read build state and trigger cross-repository workflows. It must be
present in every repository that participates in the pipeline.
The ccat pat subcommands provide a single, safe interface for checking and
rotating this secret across all ccatobs repositories.
Why a PAT is needed#
The orchestrate-deploy.yml workflow in system-integration dispatches
builds to leaf repositories and reads their workflow status. Both operations
require a token with permissions beyond the default GITHUB_TOKEN scope.
The canonical list of repositories that need GH_PAT is maintained in
ci/dependency-graph.yml. Adding a new repo to that file is sufficient to
include it in the next ccat pat rotate run.
Generating a PAT with minimum permissions#
Create a fine-grained Personal Access Token:
Go to GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens → Generate new token.
Set Resource owner to
ccatobs(the organisation).Set Repository access to All repositories (or select individual repos matching
ci/dependency-graph.yml).Under Permissions → Repository permissions, grant:
Actions: Read-only (allows reading workflow run status)
Secrets: Read and write (allows
ccat pat rotateto set the secret)
Set an expiry date and note it somewhere so you know when to rotate.
Note
Classic PATs also work. The minimum scopes are repo and
workflow. Fine-grained tokens are preferred because their permissions
are narrower and auditable per repository.
Where the secret must be set#
GH_PAT is read by orchestrate-deploy.yml in ccatobs/system-integration.
However, leaf repositories also need the secret so their own triggered workflows
can authenticate back to the orchestrator.
The complete list of repositories is driven by ci/dependency-graph.yml.
Run ccat pat status at any time to see the current state.
Checking PAT status#
$ ccat pat status
Prints a table for every repository in the dependency graph:
╭────────────────────────────────────┬────────────────┬──────────────────────────╮
│ Repository │ Secret present │ Last rotated │
├────────────────────────────────────┼────────────────┼──────────────────────────┤
│ ccatobs/system-integration │ ✓ present │ 2026-02-21T22:17:45Z │
│ ccatobs/ops-db │ ✓ present │ 2026-02-21T22:17:46Z │
│ ccatobs/ops-db-api │ ✗ missing │ — │
│ ... │ ... │ ... │
╰────────────────────────────────────┴────────────────┴──────────────────────────╯
Exit code is non-zero if any secret is missing, making it safe to call from CI.
Note
The Secrets API returns only the updated_at timestamp. The token value
itself is never exposed. To verify that a token is still valid, generate a
new one and run ccat pat rotate — the old one is overwritten atomically.
Rotating the PAT#
Preview what would be updated (no secrets written):
$ ccat pat rotate --dry-run
Perform the rotation:
$ ccat pat rotate
Paste GH_PAT token:
The command:
Prompts for the token with hidden input (not echoed to the terminal and not stored in shell history).
Iterates over every repository in
ci/dependency-graph.ymlplussystem-integration, callinggh secret set GH_PATfor each.Stops on the first failure and reports which repositories were already updated. Re-running after fixing the error is safe — setting the same secret twice is idempotent.
Requires the gh CLI to be authenticated as an organisation member with
Secrets: Read and write access to all ccatobs repositories (i.e. the same
account used to generate the PAT above, or an admin account).
Recovery after partial failure#
If ccat pat rotate stops mid-run, the output clearly lists which
repositories were already updated. The remaining repositories still hold the
old token.
To recover:
$ ccat pat rotate # re-run with the same new token
The repositories that were already updated are set again (no-op), and the run continues from where it left off.
Recommended rotation cadence#
Fine-grained PATs: rotate every 90 days or before expiry, whichever comes first.
Classic PATs: rotate every 60 days (shorter because the scope is broader).
After any team member with access to the token leaves the organisation.
Immediately upon any suspected exposure.
Run ccat pat status after rotation to confirm all secrets show an updated
Last rotated timestamp.