
Staying Evergreen: How we automated secure access and repo hygiene in a shared org
GitHub engineers describe Evergreen, a Go-based GitHub App that runs quarterly outside-collaborator access reviews and archives stale repositories in a shared org. It uses scheduled Actions with repository_dispatch, JSON state files in git as the audit trail, checklist issues (IssueOps) closed to trigger revocation, and custom properties for ownership and exemptions. Design principles include fail-secure/fail-closed revocation, never deleting data, dry-run rollout, Splunk logging, and proactive pacing against GitHub's secondary rate limits.
What it is
Evergreen is a small Go webhook server, packaged as a GitHub App, that GitHub runs against a shared org where employees work with customers and partners. It does two jobs: a quarterly review of outside-collaborator access, and flagging, warning and archiving repos with no human activity past a threshold. The post presents it as a pattern you can rebuild from native GitHub features.
How it works
- Clock and memory: a controller repo holds four scheduled Actions workflows that fire
repository_dispatchevents quarterly. Each workflow also supportsworkflow_dispatchfor manual runs. Reviews open two weeks before cleanup, and a built-in grace period stops a stray manual run from fast-forwarding to revocation. - State: one JSON file per repo at
state/repos/<repo_id>.json. It records the access snapshot, review open date, deadline, who decided and the outcome.git logon that file answers "why did this person lose access". - Decision channel: each repo gets a checklist issue. Checked collaborators stay; closing the issue fires the
issues.closedwebhook, and unchecked ones are revoked. A receipt comment lists what was kept and removed. - Authorization: if the closer is not an authorized employee, the app reopens the issue and posts an explanation.
- Owner resolution: use the
evergreen_repo_ownercustom property (a@useror@org/team) if set. Otherwise take recent issue and commit authors and cc those who are on the access review team.
flowchart TD
A[Review issue closed] --> B{Closer authorized?}
B -- No --> C[Reopen + explain]
B -- Yes --> D[Keep checked, revoke rest]
E[Deadline passes, no response] --> F[Revoke all in snapshot]
D --> G{All removals succeeded?}
F --> G
G -- Yes --> H[Record revoked, post receipt]
G -- No --> I[Stay pending, issue open, retry next run]What to do
If you copy the pattern:
- Fail closed on partial failure. Record "revoked" only after every removal succeeds. Otherwise leave the review pending and the issue open so the next run retries.
- Write every change into the issue. Restoring access then means re-adding the listed handles.
- Provide opt-outs via org-owner-set custom properties:
evergreen_exempt_from_access_reviewandevergreen_exempt_from_archive. - Roll out in order: 1. Ship a
DRY_RUNmode that logs every intended write, and run it against the real org first. 2. Announce repeatedly before enabling. 3. Send structured logfmt logs to Splunk. The authors say this caught empty collaborator lists, unusual permissions and repos with issues disabled. - Pace for rate limits. Issue and comment creation hits GitHub's secondary content-creation limits of 80 requests/minute and 500/hour. Evergreen batches repos, spaces content-creating calls by a couple of seconds, sleeps between batches, and counts only repos that actually create content. The authors chose proactive avoidance over reactive back-off.
Caveats
- The post gives no code, repo link or configuration for Evergreen. It describes the design only, so you would be building it yourself.
- Authorization is described only as "Hubbers" (employees). How the check is implemented is not stated.
- Archiving is never destructive: nothing is deleted. The post does not give the staleness threshold or the exact reminder timing.
Organisations named here
- IssueOps
- outside collaborators
- repository_dispatch
Technologies named here
- Evergreen
- GitHub Actions scheduled workflows
- GitHub custom properties
- GitHub issues and webhooks
- DRY_RUN mode
- GitHub
- GitHub Actions
- Go
- Splunk
- GitHub Copilot
Techniques named here
- Quarterly outside collaborator access review
- Fail secure
- Fail closed
- Authorization check
- Owner resolution fallback chain
Related in DevSecOps
JFrog: The Governance Gap Between Your Policy and Your Pipeline

Dev Machine Guard Now Supports Windows - StepSecurity

Socket: TanStack npm Packages Compromised
