gh-iga™
The open-source identity governance scanner for GitHub.
Know who has access to what — in 60 seconds.
Most GitHub orgs have no idea who can actually push to production.
gh-iga gives you a complete picture of your GitHub org's access posture in a single command — members, teams, repos, permissions, and the risks hiding inside all of it. No dashboards to set up. No agents to deploy. Just run it and get a report you can share with your team or hand to an auditor.
$ gh-iga scan --org myorg
gh-iga — Identity Governance Scanner for GitHub
─────────────────────────────────────────────────
Org: myorg
Members: 84 Teams: 12 Repos: 203
RISK FINDINGS
✗ 12 users have admin access to 5+ repos (admin sprawl)
✗ 8 outside collaborators have write or admin access
✗ 19 users inactive 90+ days still hold write/admin
✗ 6 repos have 4+ admins (over-permissioned)
⚠ 31 users on no team and no direct repo access (orphaned)
⚠ 14 users with direct repo access could move to teams
Report written → gh-iga-report-myorg-20260509.html
JSON output → gh-iga-report-myorg-20260509.json
Why gh-iga™?
GitHub is where your code — and your blast radius — lives. But GitHub's native UI makes it nearly impossible to answer the questions that actually matter for security and compliance:
- Which engineers can push to every repo in the org?
- Who joined 18 months ago and still has admin on 30 repos?
- Which outside contractors still have write access?
- Are any repos owned by no team — just a handful of individual admins?
gh-iga answers all of these, automatically, every time you run it.
Install
Prerequisites
- Python 3.9+ — python.org/downloads
- Windows: check "Add Python to PATH" during install
- pipx is recommended for installing CLI tools in an isolated environment
From PyPI
pipx install gh-iga
Or, with pip:
python -m pip install gh-iga
From source
Use this path if you want to contribute to gh-iga or run unreleased code:
git clone https://github.com/abhishek20c/gh-iga.git
cd gh-iga
python -m pip install -e .
Verify it worked:
gh-iga --version
# gh-iga, version 1.0.0
Quickstart
1. Create a GitHub token
Go to github.com/settings/tokens → New classic token and grant these scopes:
One classic token covers the entire scan — human-identity governance and every non-human-identity inventory. No GitHub App, no second credential, no infrastructure.
| Scope | Required for |
|---|---|
repo |
Repo collaborators/permissions, deploy keys, Actions secrets, webhooks, workflow-token settings (per-repo) |
read:org |
Org members and teams (org scan only) |
admin:org |
Installed GitHub Apps + org-level Actions secrets, webhooks, and workflow-token defaults (org scan only) |
gh-igaonly performs read API calls and never modifies your org, repos, or permissions. Full inventory still requires broad classic PAT scopes (repo,read:org, andadmin:org) because GitHub exposes some read-only inventory data only through those scopes. Run the token as an org owner so the NHI inventories (apps, deploy keys, secrets, webhooks, workflow settings) are visible.
2. Set your token
| Shell | Command |
|---|---|
| Mac / Linux | export GITHUB_TOKEN=ghp_your_token_here |
| Windows PowerShell | $env:GITHUB_TOKEN = "ghp_your_token_here" |
| Windows CMD | set GITHUB_TOKEN=ghp_your_token_here |
3. Scan
No org? Scan your personal repos:
gh-iga scan-user
Have a GitHub org:
gh-iga scan --org your-org-name
That's it. A self-contained HTML report, a Markdown report, and a JSON file land in your current directory. Open the .html file in any browser.
What it scans
| Area | Detail |
|---|---|
| Org members | All members with role (owner / member) |
| Outside collaborators | Every external user and their repo-level permissions |
| Repos | Per-repo access list with permission levels (admin / maintain / write / triage / read) |
| Teams | Membership, team-level repo permissions, and nesting |
| Installed GitHub Apps (NHI) | Every app installed on the org — permissions, repo scope, suspended state |
| Deploy keys (NHI) | Per-repo SSH credentials — read/write, last used, added by |
| Actions secrets (NHI) | Repo- and org-level CI secrets — name + age only (never values) |
| Webhooks (integration surface) | Repo- and org-level webhooks — URL, secret presence, transport security |
| Actions token permissions (NHI) | Default GITHUB_TOKEN permissions (org + repo) and PR-approval setting |
| Activity | Last commit/PR activity per user — proxy for "is this person still active?" |
What it flags
High severity
- Admin sprawl — users with admin access to more than N repos (default: 5)
- Inactive admins/writers — users with no activity in 90+ days who still hold write or admin access
- Privileged outside collaborators — any external user with write or admin on any repo
Medium severity
- Over-permissioned repos — repos with more than N admins (default: 3)
- Orphaned users — org members on no team and with no direct repo access
Hygiene
- Direct access candidates — users with direct repo access who could be governed through a team instead
Non-human identities (NHI)
- Over-permissioned apps — installed GitHub Apps holding admin (high) or write (medium) permissions — NHI5
- Org-wide apps — apps installed with access to all repositories — NHI5
- Suspended apps still installed — partially offboarded app identities — NHI1
- Read-write deploy keys — per-repo SSH credentials that can push code — NHI5
- Stale deploy keys — keys unused for 90+ days or never used — NHI1
- Unrotated Actions secrets — CI secrets not updated in 365+ days — NHI7
- Webhooks with no secret — payloads can't be verified as from GitHub — NHI3
- Insecure webhook transport — http:// or SSL verification disabled — NHI3
- Read-write
GITHUB_TOKENdefault — Actions token overprivileged by default — NHI5 - Actions can approve PRs — automation bypasses required review — NHI5
The GitHub App inventory (and org-level secrets/webhooks/workflow defaults) is org-scan only and requires admin:org. Deploy keys, repo Actions secrets, repo webhooks, and repo workflow-token settings are read on both org and personal scans (per-repo, where the token has admin on the repo). NHI risk tags are applied to genuine non-human identities and to webhooks as a related third-party trust surface under NHI3. See OWASP-NHI-Top10-mapping.md for the full risk mapping.
All thresholds are configurable through the CLI flags shown below.
Output formats
| Format | Flag | Use case |
|---|---|---|
| Terminal summary | (default) | Quick review in CI or your shell |
| HTML report | --html (default on) |
Share with your team or auditors |
| Markdown report | --markdown |
Drop into a GitHub issue or Confluence |
| JSON | --json (default on) |
Pipe into SIEM, Splunk, your own scripts |
All output is written locally. Nothing is sent anywhere.
Options
Usage: gh-iga scan [OPTIONS]
Options:
--org TEXT GitHub org to scan [required]
--token TEXT GitHub token (or set GITHUB_TOKEN env var)
--output-dir TEXT Directory to write reports (default: current dir)
--format [html|md|json|all]
Output format (default: all)
--inactive-days INT Days of inactivity to flag (default: 90)
--admin-sprawl-threshold INT
Repos with admin access to flag user (default: 5)
--max-admins-per-repo INT
Admins per repo before flagging (default: 3)
--no-activity Skip activity checks (faster, but disables
the inactive-user rule)
--no-html Disable HTML report
--no-json Disable JSON output
--help Show this message and exit.
CI / automation
Run gh-iga on a schedule in GitHub Actions:
name: Weekly access review
on:
schedule:
- cron: '0 9 * * 1' # every Monday at 9am
jobs:
scan:
runs-on: ubuntu-latest
steps:
- run: python -m pip install gh-iga
- run: gh-iga scan --org ${{ github.repository_owner }}
env:
GITHUB_TOKEN: ${{ secrets.GH_IGA_TOKEN }}
- uses: actions/upload-artifact@v4
with:
name: access-report
path: gh-iga-*.html
Roadmap
| Version | Status | What |
|---|---|---|
| v0.1–v0.6 | ✅ Shipped | Core org and personal-account scanning, human and non-human identity inventories, risk rules, and HTML / Markdown / JSON reports |
| v1.0 | ✅ Stable | Reliable point-in-time scanner, explicit scan-completeness reporting, stable CLI commands, and documented report semantics |
| v1.x | Candidate | Service/shared-account detection, branch-protection drift, scheduled-scan helpers, and delta reports |
| Later | Exploring | Optional GitHub App authentication, continuous monitoring, webhook-driven updates, and Slack/email alerts |
Two auth tiers (by design): the default scan uses a single classic PAT with the documented scopes to read members, repos, teams, GitHub App inventory, deploy keys, and more, with zero setup. A future advanced tier will support GitHub App installation auth for the handful of org-admin endpoints PATs can't reach (notably fine-grained PAT inventory). Keeping the App tier optional preserves gh-iga's "paste a token, nothing leaves your machine" simplicity for everyone who doesn't need the deeper inventory.
A note on AI coding tools: when a tool like Copilot is installed as an org GitHub App, gh-iga surfaces it in the app inventory (NHI3). Tools that are user-authorized OAuth/GitHub apps — e.g. an individual authorizing an AI assistant on their personal account — are not enumerable through any GitHub API, so no third-party scanner can inventory them; they are visible only in each user's account settings.
Version 1.0 scope and limitations
Version 1.0 is a stable point-in-time, read-only scanner. It defines a dependable baseline for the existing CLI and report formats; it is not a hosted or continuously running governance service.
- No continuous monitoring or built-in alerts. Run scans manually or schedule the documented GitHub Actions workflow. Delta reports, webhook-driven updates, and Slack/email notifications are future work.
- Classic PAT authentication only. Full inventory requires the documented broad scopes and, for some endpoints, organization-owner or repository-admin visibility. GitHub App authentication and fine-grained PAT inventory are not included.
- Visibility follows the token. A partial scan is labeled in terminal, HTML, Markdown, and JSON output, with skipped scopes and reasons. Counts from a partial area must not be interpreted as proof that no resources exist.
- Activity is an approximation. The inactive-user rule uses visible GitHub event activity; it is not an employment-status, login, or identity-provider signal.
- Secrets remain secret. GitHub exposes Actions secret names and timestamps, not values. Rotation findings use the available timestamps.
- Some identities cannot be enumerated. User-authorized OAuth apps and personal AI-tool authorizations are visible only in each user's GitHub settings.
- No automatic remediation.
gh-igaperforms read requests and writes local reports; it does not change memberships, permissions, credentials, hooks, or workflow settings. - Large organizations consume GitHub API quota. Runtime and completeness depend on organization size, enabled GitHub features, token visibility, and rate limits.
See the changelog for release history.
Comparison
| gh-iga | GitHub native UI | Gitguardian / Nightfall | Terraform / Policy-as-code | |
|---|---|---|---|---|
| Org access overview | ✅ | ⚠ Partial | ❌ | ❌ |
| Inactive user flagging | ✅ | ❌ | ❌ | ❌ |
| Admin sprawl detection | ✅ | ❌ | ❌ | ❌ |
| Outside collaborator audit | ✅ | ⚠ Manual | ❌ | ❌ |
| Shareable HTML report | ✅ | ❌ | ✅ (paid) | ❌ |
| JSON / pipeline output | ✅ | ❌ | ✅ (paid) | ✅ |
| Free & self-hosted | ✅ | ✅ | ❌ | ✅ |
| No write API calls | ✅ | — | ✅ | ✅ |
Security & privacy
gh-igaonly performs read API calls. The classic PAT scopes needed for full visibility are broad, but the tool does not call write endpoints and cannot modify your org, repos, or permissions.- All data stays on your machine. No telemetry, no callbacks, no external services.
- The token is never written to disk or included in any report output.
- If you find a security issue in
gh-igaitself, please report it privately via GitHub Security Advisories rather than a public issue.
Who's using gh-iga?
See ADOPTERS.md for organizations and individuals running gh-iga in the wild.
Using it yourself? Open a PR to add yourself — or drop a note in Discussions.
Feedback
gh-iga 1.0 provides a stable scanner baseline and continues to be shaped by real-world use cases.
If you've run it against your org — even just to kick the tyres — I'd love to hear:
- What access problems did it surface?
- What would make the report more useful to your team or auditors?
- What's missing from the roadmap?
👉 Start a discussion or open an issue. Every piece of feedback directly influences the roadmap.
License
MIT — see LICENSE.
Built to make GitHub access reviews something you actually do.
If this saves you time, a ⭐ goes a long way.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gh_iga-1.0.0.tar.gz.
File metadata
- Download URL: gh_iga-1.0.0.tar.gz
- Upload date:
- Size: 50.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
31b28bbd8a417724f859bdac7802786071ab7f7b91429d7d3ee95e55b5d6e00c
|
|
| MD5 |
3aa6c03e1e385e44038018b8fea7c728
|
|
| BLAKE2b-256 |
fc3b133a185e31a4b5f2cb2e51e1d8e9de8908dd45bc7dc27ec9f8a2848d83e0
|
Provenance
The following attestation bundles were made for gh_iga-1.0.0.tar.gz:
Publisher:
publish.yml on abhishek20c/gh-iga
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gh_iga-1.0.0.tar.gz -
Subject digest:
31b28bbd8a417724f859bdac7802786071ab7f7b91429d7d3ee95e55b5d6e00c - Sigstore transparency entry: 2191843437
- Sigstore integration time:
-
Permalink:
abhishek20c/gh-iga@05fb2fba3176015dd138d1b62c95e4454c526271 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/abhishek20c
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@05fb2fba3176015dd138d1b62c95e4454c526271 -
Trigger Event:
release
-
Statement type:
File details
Details for the file gh_iga-1.0.0-py3-none-any.whl.
File metadata
- Download URL: gh_iga-1.0.0-py3-none-any.whl
- Upload date:
- Size: 40.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f16fcdbfd5050fbf0929969f5dc5537e86eaa676dcc3209f4b82f8c18ef822bd
|
|
| MD5 |
477871e9c46d547f414429266abc8b1a
|
|
| BLAKE2b-256 |
9e0e23ed2288013d6932ba15c345479ab91c624d767cc6051d39c520bd41ab98
|
Provenance
The following attestation bundles were made for gh_iga-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on abhishek20c/gh-iga
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gh_iga-1.0.0-py3-none-any.whl -
Subject digest:
f16fcdbfd5050fbf0929969f5dc5537e86eaa676dcc3209f4b82f8c18ef822bd - Sigstore transparency entry: 2191843454
- Sigstore integration time:
-
Permalink:
abhishek20c/gh-iga@05fb2fba3176015dd138d1b62c95e4454c526271 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/abhishek20c
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@05fb2fba3176015dd138d1b62c95e4454c526271 -
Trigger Event:
release
-
Statement type: