Skip to content

Setup

What you need before warden does anything useful: the CLI (or the Action), auth, and a policy file. The default mode is dry-run, which only reads and prints a plan, so the walkthrough below is safe to run end to end.

Install

No install needed for a one-off:

npx @intentius/github-warden reconcile --config .github/governance.yml \
  --token-env GH_TOKEN --mode dry-run

Or install the github-warden binary globally:

npm install -g @intentius/github-warden     # or add it to a project

In CI you can skip both and use the GitHub Action.

Node 22+.

Choose your auth

Pre-minted token (--token-env) GitHub App (--app-id-env + --installation-id-env)
Good for Repo-level cycles, audit, quick trials Everything, especially unattended/scheduled runs
Org-level cycles (org-settings, membership, teams, org rulesets) Only if the token was minted from an App with the org permissions Yes
Token cycles (token-governance, token-approval) No — these APIs are callable only by a GitHub App Yes
Secrets to hold One token App ID, installation ID, private key PEM

--token-env takes precedence when both are supplied. A classic PAT will get you through repo-level dry-runs, but the PAT governance endpoints and several org administration APIs reject anything that is not an App installation token, so plan on an App for real use.

GitHub App

Follow the App setup checklist. It walks you through creating the App with the webhook off, granting per-cycle permissions (read is enough for dry-run while apply needs write), generating a private key, installing it on the org, then noting the App ID plus installation ID. A refused read (403) never aborts the run and never errors a cycle: every cycle treats it as "nothing live", skips the gated slice, and appends a note to that cycle's plan (NOTE: <slice>: read was permission-gated (403); planned entries may fail on apply). So you can start with a narrow grant and widen later — the NOTE lines tell you exactly which slices the grant doesn't cover yet, and an apply into a still-gated slice surfaces as failed entries, not a crashed run.

Environment variables

warden never takes credentials on the command line, only env var names:

Variable Set when Meaning
whatever you pass to --token-env (e.g. GH_TOKEN) token auth A pre-minted installation token.
whatever you pass to --app-id-env (e.g. APP_ID) App auth The numeric App ID.
whatever you pass to --installation-id-env (e.g. INSTALL_ID) App auth The numeric installation ID.
GOVERNANCE_APP_PRIVATE_KEY (or GITHUB_APP_PRIVATE_KEY) App auth The private key PEM. Fixed names; not flag-configurable yet.

Write a policy

The governance file is the heart of the tool, and the policy reference documents every field. Start from the annotated starter in examples/governance.yml and declare only what you want warden to own:

cp examples/governance.yml .github/governance.yml
# edit the org and repo names, delete blocks you don't want managed

First dry-run

export GH_TOKEN=...   # or the App trio of env vars

npx @intentius/github-warden reconcile \
  --config .github/governance.yml \
  --token-env GH_TOKEN \
  --mode dry-run

With App auth instead:

GOVERNANCE_APP_PRIVATE_KEY="$(cat org-warden.pem)" \
APP_ID=123456 INSTALL_ID=78901234 \
npx @intentius/github-warden reconcile \
  --config .github/governance.yml \
  --app-id-env APP_ID --installation-id-env INSTALL_ID \
  --mode dry-run

The plan prints one === <cycle> @ <org> === block per cycle, listing the creates and updates warden would perform. Nothing has been changed. Iterate on the policy until the plan matches your intent, then run the same command with --mode apply. Guardrails still stand between the plan and the API: a block exits 1 and prints why.

From there, move the same command into a CI pipeline so the plan runs on every policy change and drift is corrected on a schedule.

Adopting an existing org

dumpOrg (src/reconcile/dump.ts, programmatic) exports live state as a starting config so adoption starts from reality rather than a blank file. It currently covers branch protection only, and the literal-name probe misses wildcard-pattern rules; trim and extend its output by hand before committing.

A disposable sandbox: the e2e stack

Unlike the sibling wardens, github-warden has no self-hosted instance to stand up: the CLI always talks to api.github.com, so there is no sandbox URL to point it at. The safe first contact with a real org is the dry-run above, which only reads. For exercising full apply loops without touching a real org, the repo ships a hermetic mock GitHub used by the e2e smoke suite (just e2e-up, then GITHUB_WARDEN_E2E_URL=http://localhost:8188 npm run test:e2e, then just e2e-down); e2e/README.md covers what it exercises.