Skip to content

Governance: who decides what

Settings that resolve across the platform, the organisation, the repository and the member; approval cards; a validation gate on push; and switches that ship off. All of it is driven by bin/policy and the console.

Policy console · three resolution rulessimulated
platform floorpolicy_profileunset
organisationacmeunset
repositoryacme/apiunset
memberdev@example.testunset

effective guarded

# security profile: the most restrictive STATED value wins.
# the default, guarded, applies only when no scope states one.
# press a command.

Choose a setting, then run its commands in order. Each one follows a different rule, which is the thing most worth knowing before you configure anything. The security-profile explanation follows the wording bin/policy uses; the rest simulates its effect.

What it is

Governance is the set of settings that decide how far a workspace and its agent may go, where each one is set, and who may loosen it. There is one implementation, bin/policy: the console writes the records, bin/policy decides what they mean, and the enforcers (the egress proxy, the git broker, workspace-agent, the chat) ask it and never interpret the records themselves.

It is

  • Security profiles (open, metered, guarded, strict) that decide egress and where credentials live.
  • Agent profiles, approval cards and a validation gate that refuses a push without a passing check for that exact commit.
  • Tighten-only organisation settings, operator floors, and an audit record of every change.

It is not

  • Policy as code in your repository. Settings live on the host and in the console.
  • A replacement for CI. The gate refuses a push with no passing verdict; your CI still runs.
  • One rule for everything. Security profile, agent profile and tunables resolve differently; see the console above.

How it works

  1. Security profile: strictest stated value wins across five scopes: platform (policy_profile, the floor), organisation, repository, user and organisation member. With nothing stated, guarded applies. policy profiles ends with Precedence: most restrictive wins (open < metered < guarded < strict).
  2. Agent profile: most specific wins. A member's own value overrides the organisation's, which overrides the platform's; unset means assisted. agent_profile_floor then caps autonomy for everyone.
  3. Tunables: organisations only tighten. An organisation's value is read against the host's on every read; a looser one is refused when written and ignored if it gets there another way. Some keys are host-wide, and max_workspaces is set for an organisation by an operator only.
  4. Approval cards end a chat turn at awaiting_approval. The approver is the member by default or a second person; binding is advisory by default.
  5. Validation gate. In the git broker, per push: each pushed commit needs a passed verdict produced on the host by the organisation's approved checks, run in fresh containers against that commit's own bytes. A check the upstream default branch declares cannot be removed to get past it.
  6. “Why it's stuck” names which of five gates holds a chat-started run: starting an agent from chat, the ACP agent, permission to run real work, permission for this workspace, and which model runs.
What each security profile controls
ProfileEgressGit credentialModel credentialBranch policy · bulk-read detection
openopentoken in the containerin the containeroff · off
meteredallowlisttoken in the containerin the containeroff · off
guarded defaultallowlistbroker, no token insidehost proxyoff · off
strictallowlistbrokerhost proxyon · on

Defaults

Governance settings
SettingScopeDefaultAllowed values
Security profile--profile on policy set.platformorganisationrepositoryusermemberguarded when none is statedopen · metered · guarded · strict
policy_profileThe platform floor. Raises everybody, lowers nobody.platformunsetunset or a profile
agent_profile · agent_profile_floorplatformorganisationmember · platformassisted · unsetsupervised · assisted · autonomous
chat_approval_approverplatformorganisationselfself · second-person
chat_approval_bindingplatformorganisationadvisoryadvisory · binding
plan_approval_enabledApprove a plan before it runs.platformorganisationoff until an admin turns it ontrue · false
agent_check_allowlistThe approved checks: they auto-run in chat and are what the gate runs.platformorganisationemptycheck names
git_validation_gate_enabledplatformorganisationoff until an admin turns it ontrue · false
git_branch_patternEnforced only under strict. {user} is the email's local part.platformfeature/{user}/*a ref pattern
chat_dispatch_enabledGate one of “Why it's stuck”.platformorganisationoff until an admin turns it ontrue · false
Live execution grantGoverned background runs, tied to the running commit.organisationnot grantedgranted against a proven evidence record
Require per-developer identityWhile off, the shared front-door password still works and is not attributable to a person.platformoffon · off (console Governance page)

Configure it

on the host
$ bin/policy profiles
$ bin/policy resolve --tenant acme --user dev@example.test --repo acme/api [--json]
$ bin/policy show --scope tenant --target acme
$ bin/policy set --scope tenant --target acme --profile strict [--dry-run]
$ bin/policy set --scope repo --target acme/api --profile strict
$ bin/policy set --scope user --target dev@example.test --profile strict
$ bin/policy clear --scope user --target dev@example.test
$ bin/policy set --key policy_profile --value guarded
$ bin/policy get-for --tenant acme --key git_validation_gate_enabled
$ bin/policy tunables --json
$ bin/tenant-audit [--json]

Scopes on the command line: tunables are set globally (no --scope), per organisation (--scope tenant --target acme) or per member (--scope org-user --tenant acme --target dev@example.test, for the few keys that allow it). The repo and user scopes hold only a --profile. There is no workspace scope.

Organisation

OrganisationsacmeSettings

Security profile, Self-approval, Agent features, Live execution and the other cards. The Checks tab approves checks; “Why it's stuck” diagnoses runs.
Operator

ConsoleGovernance

Posture across organisations, the floor, and “Require per-developer identity”.

Tasks

Tighten one contractor

  1. Set their agent profile to supervised

    OrganisationsacmeMemberscontractor@example.test

    Expected: their agent reads and proposes only; the organisation's default is unchanged.

  2. Put their security posture on strict

    on the host
    $ bin/policy set --scope user --target contractor@example.test --profile strict
    $ bin/policy resolve --tenant acme --user contractor@example.test --repo acme/api

    Expected: strict applies, so pushes must match feature/contractor/* and bulk reads are flagged.

Set a floor for every organisation

  1. Find who sits below it

    on the host
    $ bin/tenant-audit

    Expected: a table of organisations (SLUG STATES RESOLVES NOTE); a note like LOOSER than the default - real credentials will be mounted marks the ones to talk to. Exit 1 if any resolves looser than the default.

  2. Set the floor

    on the host
    $ bin/policy set --key policy_profile --value guarded

    Expected: tenant-audit now shows those rows as states open, held at guarded by a stricter scope, and exits 0.

Require a passing check before any push

  1. Approve at least one check

    OrganisationsacmeChecks

    With no approved check, every push is refused.

  2. Turn the gate on for the organisation

    on the host
    $ bin/policy set --scope tenant --target acme --key git_validation_gate_enabled --value true
  3. Validate, then push

    In the workspace Git panel, Validate this commit runs the approved checks on the host; Push goes through the broker.

    Expected: a push of an unvalidated commit is refused with commit 1a2b3c4 has not been validated. Use Validate this commit in the Git panel, then push again.

Grant governed runs

  1. Prepare the evidence on the host

    on the host
    $ bin/deployment-readiness --prepare
    $ bin/deployment-readiness --json

    Expected: exit 0 when ready, 1 otherwise, with the reason.

  2. Grant it for one organisation

    OrganisationsacmeSettingsLive execution

  3. Re-grant after each deploy

    The grant is tied to the commit it was proven on and lapses with the next one, by design.

Find out why a run is stuck

  1. Open the tab

    OrganisationsacmeWhy it's stuck

  2. Read the first row that is not OK

    Rows read OK, NEEDS FIXING, NOT CHECKED or NOT YOURS, in gate order. Each names its fix; copy for your operator copies what a platform operator needs to do.

Make every login attributable

  1. Give everyone an account first

    Invite every developer; configure SSO if you use it (see Identity & SSO).

  2. Turn on “Require per-developer identity”

    ConsoleGovernance

    Expected: the shared front-door password no longer opens workspaces; every session belongs to a person.

Verify it works

On the hostsynthetic

$ bin/tenant-audit default guarded floor guarded SLUG STATES RESOLVES NOTE acme strict strict acme-ops open guarded states open, held at guarded by a stricter scope

Good looks like: tenant-audit exits 0; bin/policy resolve names the scope that decided each profile; bin/policy get-for returns what each organisation actually gets; in the console, the Governance page shows every organisation's posture and every settings change is on the audit trail with its previous value.

Troubleshooting

Push refusedrefused by validation gate: commit 1a2b3c4 has not been validated. Use Validate this commit in the Git panel, then push again

Fix: validate that commit. If it says yet (it is still running), wait for the verdict.

Push refusedcommit 1a2b3c4 failed validation (…). Fix it, commit, and validate again

Fix: the verdict is bound to that commit; a new commit needs a new validation.

Branch delete refuseddeleting feature/x is refused while validation is required - there is no commit to validate. An org admin can delete it on GitHub

Fix: delete it on GitHub as an organisation admin.

Push refused under strictrefused by branch policy: refs/heads/main does not match the allowed pattern 'feature/{user}/*' for profile strict

Fix: push to your own feature branch. Test a ref first with bin/git-broker check --tenant acme --workspace dev01 --ref refs/heads/main (exit 2 on refusal).

An organisation cannot loosen a member…: … is looser than acme's …. A member setting may only tighten - raise the organisation's value first.

Fix: change the organisation's value, then the member's.

A key will not set per organisation… is host-wide and cannot be set per tenant (per-tenant: …)

Fix: set it without --scope, for the whole host. The message lists the keys that are per organisation.

Quota cannot be changed by the organisationmax_workspaces is set FOR a tenant by a platform operator and cannot be set BY that tenant (operator-only: max_workspaces)

Fix: an operator sets it.

Wrong scope for a tunable--scope user cannot hold a tunable - only a profile (--profile). Tunables are global, per tenant, or per member (--scope org-user).

Fix: use --scope tenant or --scope org-user.

Limits and what is not built yet

  • The validation gate has not been seen on a live GitHub push. It is off by default and on for no real organisation yet.
  • Grants for governed runs lapse on every new commit until the evidence is re-run.
  • No policy as code in your repository.
  • open and metered mount the git token in the container. tenant-audit flags them.
  • Profiles decide how much the agent asks, not what it can reach. Reach is egress and the brokers.
  • The shared front-door password is unattributable until “Require per-developer identity” is on, which is off by default.

Verified against the main branch, 28 Sep 2026.