Skip to main content

Plan governance

Enterprise

This page describes a feature of the Enterprise edition. Each project's own merge policy (minimum approvals, blocking self-approval), event type owners, reviewers, required fields and contracts, and tripl check are part of Community.

A plan policy holds rules that every project of the organization, or the projects it names, must follow on top of each project's own merge policy:

  • naming rules: a pattern that event, event type, field or variable names must match;
  • required fields: fields every event type must define;
  • forbidden names: names no field, variable or tracked property may use;
  • approval of sensitive fields: a branch that adds a field marked sensitive, or changes a field's sensitivity, needs an approval from a member of a group you choose;
  • protected main: the main plan takes changes only through a merged plan branch.

tripl checks the rules at three points: when code or payloads are validated (tripl check and POST /plan/validate), when a branch is merged, and when someone edits main directly.

Open Settings → Organization → Plan governance. Owners and admins of the organization manage policies. Settings → Project → Plan rules in each project links here.

Policies​

A policy has a name, an On switch, and a scope: All projects, or the projects you pick. A policy that is off is kept but checks nothing. An organization can have several policies; every policy that is on and covers a project applies to it, and a violation names the policy it comes from.

Start a policy from scratch or from a preset:

PresetWhat it sets
Snake case namesEvent types, fields and variables in snake_case; event names in snake_case, with : or . between parts.
Privacy guardForbids field and property names such as email, phone_number, ssn, ip_address, and turns on approval of sensitive fields. Pick the approver group before you save it.
Protected mainTurns on protected main.

A preset only fills in the editor; change anything before you save.

Naming rules​

A naming rule is a regular expression (Python syntax) per kind of name: event names, event type names, field and property names, variable names. The whole name must match it: [a-z_]+ accepts checkout_started and refuses CheckoutStarted. Leave a kind blank to accept any name. A pattern is at most 300 characters, and one that does not compile is refused when you save.

Required and forbidden names​

Required fields are field names every event type must define, such as platform. Forbidden names may not be used by a field, a variable or a property a tracking call sends. Both lists compare names in any case, and a name cannot be both required and forbidden.

Sensitive fields​

A field or meta field has a sensitivity: none, PII, PHI, financial or secret. With Approve sensitive fields on, a branch that adds a field with a sensitivity other than none, or changes a field's sensitivity, cannot be merged until a member of the approver group has approved the branch's current content. The approval works like any other branch approval: it is tied to the reviewed content, so an edit after it makes it stale. The branch's author does not count, even when they are in the group. The refusal lists the members who can approve.

If the approver group is deleted, merges that change a sensitive field are refused until an owner or admin picks another group.

Protected main​

With Protected main on, every change to the main plan through the API or the app is refused with 409: create the change on a plan branch, get it approved and merge it. Merging is how changes reach main. Reading main is never affected.

Where the rules are checked​

WhereWhat is checkedWhat happens
tripl check, POST /projects/{slug}/plan/validateEach call: an event name the plan does not know against the event naming rule, every key it sends against the forbidden names and the field naming rule, and, for a whole payload (complete: true), the required fields.A policy_violation finding on the call, with rule. An error fails tripl check.
Merging a branchThe branch's plan against its merge base: names, required fields and forbidden names on what the branch adds or renames, and approval of sensitive fields.409 with policy_violations. The branch's page shows the message.
Editing mainProtected main.409 with policy_violations.

Only what a branch newly breaks blocks its merge. A name on main from before a rule existed is not reported against an unrelated branch, so turning a policy on does not stop every merge until main is cleaned up. A planned event that breaks a naming rule is reported where the plan changes, at the merge, not on each call that sends it.

The policies run after the project's own merge gates (approvals, owners), which are unchanged.

Rule names​

Each violation carries a rule:

ruleMeaning
naming.event, naming.event_type, naming.field, naming.variableA name does not match the naming rule for its kind.
required_fieldAn event type, or a whole payload, lacks a required field.
forbidden_fieldA field, variable or property uses a forbidden name.
sensitive_approvalA sensitive field was added or reclassified without an approval from the approver group.
protected_mainA direct edit of a protected main.

A refused merge or edit answers:

{
"detail": {
"message": "Blocked by 1 plan rule: Field 'track.Email' uses the name 'Email', which policy 'Privacy guard' forbids.",
"policy_violations": [
{
"rule": "forbidden_field",
"message": "Field 'track.Email' uses the name 'Email', which policy 'Privacy guard' forbids.",
"severity": "error",
"entity_type": "field",
"entity": "track.Email",
"field": "Email",
"approver_ids": []
}
]
}
}

A refused edit of main has the message in detail and policy_violations beside it.

API​

All routes are under /api/v1/orgs/{org}/governance, for owners and admins of that organization from a browser session. A member and an API key get 403; someone outside the organization gets 404.

RouteWhat it does
GET /optionsThe organization's projects and groups, and the presets.
GET /policiesEvery policy.
POST /policiesCreate a policy (201).
GET /policies/{policy_id}One policy.
PUT /policies/{policy_id}Replace a policy.
DELETE /policies/{policy_id}Delete a policy (204).

A policy body:

FieldTypeMeaning
namestring, 1 to 120Unique in the organization (409 otherwise).
descriptionstringOptional.
enabledbooleanDefault true.
all_projectsbooleanDefault true.
project_idslist of idsThe projects, when all_projects is false. At least one.
naming_rulesobjectevent, event_type, field, variable: a pattern each, optional.
required_fieldslist of namesUp to 100.
forbidden_fieldslist of namesUp to 100.
sensitive_approvalbooleanDefault false.
sensitive_approval_group_ididRequired when sensitive_approval is true.
protected_mainbooleanDefault false.
presetstringThe preset it started from, informative.

A project or group of another organization, or a pattern that does not compile, is 422.

Audit​

Every change is in the organization's audit log, with the policy before and after: org.governance_policy.create, org.governance_policy.update and org.governance_policy.delete.