Skip to content

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.

MechanismUse forProsCons
Fine-grained PATSolo dev, single-user automationQuick to set up; no infrastructureTied to one user; expires; needs rotation
GitHub AppMulti-fleet automation, productionPer-installation tokens; scoped permissions; audit log; doesn’t tie to a personRequires app registration + installation
Verb categoryScopes needed
Read verbs (diagnose, status, validate, dashboard)None (local-clone reads)
Dry-run any verbNone
All write verbs --applyContents (read+write), Metadata (read), Pull requests (read+write)
request_merge_when_green --applyAbove + Administration (read) for branch-protection check
  1. GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens → Generate new token.
  2. Resource owner: the org that owns the governed repos.
  3. Repository access: the specific repos in registry.yaml. Don’t use “All repositories” unless you really mean it.
  4. Permissions: as listed above.
  5. Expiration: 90 days max (you’ll be prompted to rotate).
  6. Store the token in 1Password (vault: PETROVA, item: PETROVA_GITHUB_TOKEN).
Terminal window
export PETROVA_GITHUB_TOKEN="$(op read 'op://PETROVA/PETROVA_GITHUB_TOKEN/credential')"
petrova open_decision petrova-hq --input /tmp/decision.json --apply

For shell session persistence, either source from 1Password each time (recommended — token doesn’t sit in shell history) or add to .envrc if using direnv.

Tokens expire. Set a calendar reminder for 7 days before expiry. On rotation:

  1. Generate new token with same scopes.
  2. Update 1Password.
  3. Re-export in any active shell.
  4. Revoke old token in GitHub.
  1. Register the app in GitHub (the org’s settings → Developer settings → GitHub Apps → New).
  2. Permissions: repository contents (write), metadata (read), pull requests (write), administration (read for request_merge_when_green).
  3. Subscribe to events: push, pull_request (for the deferred webhook receiver).
  4. Generate + download the private key (.pem).
  5. Install the app on the target repos.
  6. Note the App ID (from the app’s settings) and the Installation ID (from the URL after install).
Terminal window
mkdir -p ~/.petrova
cp /path/to/petrova-app.private-key.pem ~/.petrova/
chmod 600 ~/.petrova/petrova-app.private-key.pem

Or store the key contents in 1Password and write to disk on demand.

Terminal window
export PETROVA_APP_ID=12345
export PETROVA_APP_PRIVATE_KEY_PATH=~/.petrova/petrova-app.private-key.pem
export PETROVA_APP_INSTALLATION_ID=67890
petrova open_decision petrova-hq --input /tmp/decision.json --apply

The 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.

Without credentials:

Terminal window
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 --apply

With credentials, --apply produces an applied status and a PR URL.