Auth
--apply requires GitHub credentials. Read verbs (status,
diagnose, validate, dashboard, verbs) work without auth —
they read local clones. Dry-run write verbs also work without auth
— they don’t touch the network.
Only --apply and the live constraint checks it triggers
(idempotency lookup, branch SHA, branch protection) need
credentials.
Two paths: PAT or GitHub App
Section titled “Two paths: PAT or GitHub App”| Mechanism | Use for | Pros | Cons |
|---|---|---|---|
| Fine-grained PAT | Solo dev, single-user automation | Quick to set up; no infrastructure | Tied to one user; expires; needs rotation |
| GitHub App | Multi-fleet automation, production | Per-installation tokens; scoped permissions; audit log; doesn’t tie to a person | Requires app registration + installation |
Path A: Fine-grained PAT
Section titled “Path A: Fine-grained PAT”Required scopes per target repo
Section titled “Required scopes per target repo”| Verb category | Scopes needed |
|---|---|
Read verbs (diagnose, status, validate, dashboard) | None (local-clone reads) |
| Dry-run any verb | None |
All write verbs --apply | Contents (read+write), Metadata (read), Pull requests (read+write) |
request_merge_when_green --apply | Above + Administration (read) for branch-protection check |
Set up
Section titled “Set up”- GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens → Generate new token.
- Resource owner: the org that owns the governed repos.
- Repository access: the specific repos in
registry.yaml. Don’t use “All repositories” unless you really mean it. - Permissions: as listed above.
- Expiration: 90 days max (you’ll be prompted to rotate).
- Store the token in 1Password (vault:
PETROVA, item:PETROVA_GITHUB_TOKEN).
export PETROVA_GITHUB_TOKEN="$(op read 'op://PETROVA/PETROVA_GITHUB_TOKEN/credential')"petrova open_decision petrova-hq --input /tmp/decision.json --applyFor shell session persistence, either source from 1Password each
time (recommended — token doesn’t sit in shell history) or add to
.envrc if using direnv.
Rotation
Section titled “Rotation”Tokens expire. Set a calendar reminder for 7 days before expiry. On rotation:
- Generate new token with same scopes.
- Update 1Password.
- Re-export in any active shell.
- Revoke old token in GitHub.
Path B: GitHub App
Section titled “Path B: GitHub App”- Register the app in GitHub (the org’s settings → Developer settings → GitHub Apps → New).
- Permissions: repository contents (write), metadata (read),
pull requests (write), administration (read for
request_merge_when_green). - Subscribe to events: push, pull_request (for the deferred webhook receiver).
- Generate + download the private key (
.pem). - Install the app on the target repos.
- Note the App ID (from the app’s settings) and the Installation ID (from the URL after install).
Storage
Section titled “Storage”mkdir -p ~/.petrovacp /path/to/petrova-app.private-key.pem ~/.petrova/chmod 600 ~/.petrova/petrova-app.private-key.pemOr store the key contents in 1Password and write to disk on demand.
export PETROVA_APP_ID=12345export PETROVA_APP_PRIVATE_KEY_PATH=~/.petrova/petrova-app.private-key.pemexport PETROVA_APP_INSTALLATION_ID=67890
petrova open_decision petrova-hq --input /tmp/decision.json --applyThe CLI’s resolveAuth() (in cli/src/github-app.ts) tries
PETROVA_GITHUB_TOKEN first, falls through to the App env triple
if PAT is unset.
Verifying
Section titled “Verifying”Without credentials:
petrova diagnose petrova-hq # works (local read)petrova open_decision petrova-hq --input /tmp/decision.json # works (dry-run)petrova open_decision petrova-hq --input /tmp/decision.json --apply# error: AUTH_MISSING — set PETROVA_GITHUB_TOKEN before --applyWith credentials, --apply produces an applied status and a PR URL.
See also
Section titled “See also”- Configuration — full env var reference.
- Auth setup runbook — step-by-step with troubleshooting.
- Dry-run vs apply — why
--applyis explicit.