Declarative governance for the fullsend-ai organization: the org converges to what is committed here. The config was snapshotted from the live org and validated with zero planned mutations.
| Concern | Owner | Config |
|---|---|---|
| Members, admins, teams, grants, collaborators, fleet inventory | peribolos | peribolos.yaml |
| Member privileges, Actions allow policy (covered by neither tool) | gh api steps |
org/*.json |
| Repo settings, org-level rulesets | safe-settings | settings.yml, repos/<repo>.yml |
| Daily compliance verdicts | policy checks | .github/workflows/policy.yml |
Peribolos and safe-settings converge by deleting unlisted entries — the guarantee wanted for access. OpenTofu was rejected for this stack: it only corrects drift on resources it tracks, so rogue additions stay invisible.
This repo is public deliberately: it is the org's roster (GitHub lists team
rosters only to org members). Private repos appear by name and access list
— accepted exposure; source code stays protected by repository visibility.
GHSA advisory repos (*-ghsa-*) are not tracked.
Planned evolution: org-membership and team-roster management is being contributed to safe-settings upstream (#690); when it lands, peribolos can be removed.
- Peribolos only pins
members_can_create_repositories; the other privilege flags are applied fromorg/member-privileges.jsonviagh api(not tracked upstream). - Safe-settings has no Actions-policy support; the allow policy is
pinned from
org/actions-*.jsonviagh api(#656). - Safe-settings has no secret-scanning support; it remains unmanaged (#583).
- Safe-settings runs in full-sync mode from Actions
(docs),
config at the repo root (
ADMIN_REPO=.github,CONFIG_PATH=.). - Peribolos runs from a pinned
kubernetes-sigs/prowcheckout; thegcr.io/k8s-prowimages are stale since the Aug 2024 repo move.
- App definitions (registration, permissions, webhooks): not manageable as code — GitHub exposes no API for App registrations.
- App installations (repo access per App): rejected — mutating them needs an enterprise-level credential (safe-settings ADR 0001).
- App grants in rulesets: bypass actors are pinned in
repos/*.yml.
Minimal set, verified against the endpoints each workflow calls (safe-settings' full table is only needed for more plugins, e.g. labels):
| Scope | Permission | Level | Used for |
|---|---|---|---|
| Repository | Administration | Read & Write | repo settings, rulesets, collaborators + invitations, vulnerability alerts |
| Repository | Contents | Read-only | config reads, policy file checks |
| Repository | Metadata | Read-only | mandatory |
| Organization | Administration | Read & Write | org settings, member-privileges PATCH, Actions allow policy, org rulesets |
| Organization | Members | Read & Write | peribolos: membership, teams, grants |
- As an org owner: github.com/organizations/fullsend-ai/settings/apps/new.
- Name it (e.g.
fullsend-ai-governance), homepage = this repo, webhook inactive (scheduled full-syncs only, no webhooks). - Set the permissions above; installable Only on this account.
- Note the App ID; generate a private key (
.pem). - Install on fullsend-ai → All repositories.
In Settings → Secrets and variables → Actions:
- Variable
GOVERNANCE_APP_ID= the App ID - Secret
GOVERNANCE_APP_PRIVATE_KEY= the full.pemcontents
Merges to main and the daily schedule apply for real; PRs get dry-run
checks that must report zero unexpected mutations. Until the App is
wired, the apply jobs fail at token minting — expected on the first
push. The Actions policy is pinned to the live permissive values
(allowed_actions: all); restricting it is a deliberate future change —
run a uses: inventory across the org first. All governance writes land
in the audit log as the App's bot user.
- People, teams, repo access →
peribolos.yaml - Repo settings and rulesets →
settings.yml/repos/<repo>.yml - Every change lands through a PR with a lint check and a dry-run check.