Typed Policy Authoring
In v11 the policies your organization writes are a typed policy document: one versioned document for your organization, validated against the actions your deployment actually registers, compiled, tested against cases you declare, signed, and made live by activating it. It is the only way to author policy in v11; the v10.x system-policy and tenant-policy write APIs answer 409 LEGACY_POLICY_WRITE_FROZEN and name this route instead.
You do not need a document to be governed. An organization that has published nothing runs the shipped policy set from the first boot; see Shipped Policy Posture. A document is how you add policies of your own.
Where you author
| Surface | Available on | What it is |
|---|---|---|
/api/v1/typed-policies | every edition | The authoring API: served by the Orchestrator and proxied by the Agent, so it answers on the Agent's port like the rest of the API. Community and Evaluation validate, publish and activate on it, while rollback and withdraw are served by the Enterprise customer portal only; on Enterprise it validates and reads, but cannot publish (below) |
| The portal's Policy Authoring page | Enterprise | The lifecycle as an editor, under Governance in the customer portal, and where an Enterprise organization publishes |
An Enterprise publication must name at least one approver other than its author. The API route names none, so on Enterprise it refuses every publication with APPROVER_IS_AUTHOR: an Enterprise organization publishes in the portal.
The API routes are:
| Method | Path | Purpose |
|---|---|---|
GET | /api/v1/typed-policies/edition | What this deployment's vocabulary is, and what your edition may write |
POST | /api/v1/typed-policies/validate | Validate a document and return every finding |
POST | /api/v1/typed-policies/publish | Validate, compile, test, sign and admit a version |
POST | /api/v1/typed-policies/activate | Make a published version live, by digest |
GET | /api/v1/typed-policies/active | The document currently in force |
GET | /api/v1/typed-policies/system | The platform's shipped policies, read-only: 116 compiled from the 85 shipped rows |
Comparing a draft with the version in force, the published-version history, each version's signed source and rollback are served by the customer portal (POST /api/v1/typed-policies/diff, GET /api/v1/typed-policies/artifacts, GET /api/v1/typed-policies/artifacts/{digest}/source, POST /api/v1/typed-policies/rollback), not by these routes.
Documents, published versions and activations are stored durably, so a restart or a second replica sees the same history.
Grants and ceilings
A document works with four kinds of policy. Two of them have friendly names, and two deliberately do not.
| Kind | Called | What it does |
|---|---|---|
| Permission | Grant | Allows an action when it matches. Grants compose by union, so a grant can only ever widen what is allowed. |
| Constraint | Ceiling | Denies or narrows an action that something else allowed. A matched ceiling overrides every grant, so a ceiling can only ever narrow. |
| Requirement | Requirement | Attaches a mandatory obligation, such as a redaction, an approval or an audit record. |
| Inspection | Inspection | Selects content controls. It can never grant: a detector can only report that nothing looked wrong, which is a different claim from "this request is legitimate". |
Grants and ceilings are separate sections with separate forms, and the forms use opposite words on the same controls: a grant says Allow when, a ceiling says Deny when. That is not decoration. On your organization's own document the two carry the same fields, and their effects are opposite, so an interface that told them apart by one dropdown value would hide the most consequential fact about a policy.
Two consequences worth knowing before you start:
- A grant cannot deny, and a ceiling cannot attach an obligation. If you need an approval or an audit record, that is a Requirement, not a ceiling with an extra field.
- Your ceilings cannot be broken by break-glass. Break-glass is declared by whoever owns the constraint, and an organization document cannot declare it. A ceiling you write is unbreakable by construction; if you need an exception, write one into the ceiling's Except when clause, where it is visible and versioned.
What a document starts from
Your document sits beside the platform's own policies and cannot modify them: the platform's shipped policies are a separate, signed policy root, and an organization policy can only work within them.
The policies v10.x shipped as tenant policies (the dangerous-command blocks, the DROP TABLE and TRUNCATE guards, and a few PII and EU AI Act rules) ship on your organization's root instead, because they were always yours to edit. A document your organization publishes starts from them, so you edit them there like any other policy in the document. They count as the deployment's policies rather than ones your organization wrote, so a document carries them on every edition, Community included: the Evaluation floor on reading detector output (signal.*) applies to policies your organization writes, not to these.
The vocabulary
A vocabulary decides which actions, trust realms and resource types a policy may name. A deployment's own vocabulary is the default, so no variable is needed; AXONFLOW_TYPED_AUTHORING_CATALOG on the Orchestrator (and, on Enterprise, the customer portal) is honoured if it is set:
| Value | What it loads | Can a document using it be activated? |
|---|---|---|
deployment (the default) | This deployment's own vocabulary: its registered actions, the trust realms its identity plane declares, its enforcement planes, and the shipped inspection controls | Yes: this is what a real installation runs |
conformance | A fixture vocabulary used by AxonFlow's own test corpus | No: publishable and validatable, never activatable |
With an unrecognised value, the Orchestrator's routes that need a vocabulary answer 503 naming the variable, and the customer portal does not register its typed-policy routes at all. It does not fall back to an empty vocabulary, because that would refuse every policy you write for naming an action that "is not registered" - a deployment problem rendered as your typo.
GET /api/v1/typed-policies/edition reports what your deployment resolved, including a catalog_digest: two deployments reporting the same digest accept and refuse exactly the same policies.
Writing, publishing and activating
- Add grants and ceilings. Each one needs an identifier, the principals it applies to (your whole organization, or named groups), the registered actions it reaches, and its condition. Only actions your deployment registers can be named, and a group is chosen from the realms your deployment declares rather than typed: a realm that is not declared is refused.
- Validate. The platform checks the document against its declared rules and returns every finding at once, in the order you should read them, each with a code, a plain-language summary and the specific value that caused it. Rejections block; a warning describes a policy that is well formed and will not do what it appears to, such as one that can never match or an approval requirement that applies where it cannot hold (see Where a requirement binds).
- Compare with live. A diff against the version currently in force, before you commit to anything.
- Publish. One operation that does five things in order: validate, compile, run the publication gates against the bundle it just compiled, sign, and admit. It needs at least one test case, because a document nobody tested has not been shown to do anything. The portal's publish dialog opens a builder with one row per attribute your policies read; you supply the value and say what each policy should return for it (MATCH, NO_MATCH or UNKNOWN). Every verdict starts at not asserted on purpose - a pre-filled one would be the tool asserting the behaviour on your behalf. On Enterprise a publication names at least one approver other than its author; an organization with fewer than two eligible approvers may enable self-approval, and each self-approved publication records the reason and the fact on its audit row.
- Activate. Publishing puts nothing in force. A published version becomes live when it is activated, by its content digest. On Enterprise, activation is by one of the version's recorded approvers and never by its author, unless the organization has enabled self-approval. On Community and Evaluation, where separation of duties does not apply so that a single administrator can run their own deployment, the author may activate their own version, and the activation is still attributed.
- Roll back. A rollback activates a digest that was activated before, so the thing you return to has already been through the whole pipeline once. It records a reason, because an unexplained reversal of policy is what an audit trail exists to prevent.
Before a digest becomes active, the platform builds the engine that would enforce it and refuses if it cannot be built, so a document never becomes active and then fails at its first request. A document whose vocabulary is the conformance fixture, or which carries a grant that permits every action with no condition, is refused at activation by name.
A requirement that holds a step for approval
In v11 policy makes a step wait for a person only through a requirement that attaches the approval_challenge obligation to it, whether your organization publishes it or a policy pack your deployment installs carries it. No v10.x require_approval system policy, tenant policy or override holds a step on v11. (Execution mode holds steps with no policy involved: confirm mode holds every step of a multi-agent plan, and step mode runs the first step and holds each later one.)
Approval requirements are Enterprise-only (a Professional licence counts as Enterprise here). Publication checks every obligation against your edition, and on Community or Evaluation a document carrying approval_challenge is refused with OBLIGATION_FAMILY_NOT_IN_EDITION. On Enterprise the publication runs in the customer portal, as every Enterprise publication does (see Where you author).
Where a requirement holds
The engine answers a matching request with a challenge. What happens next depends on the enforcement point:
| Enforcement point | Result of the challenge |
|---|---|
Workflow step gate (POST /api/v1/workflows/{id}/steps/{step_id}/gate) | The gate answers decision: "require_approval", the step stays pending, and a reviewer entry is created. The step is approved or rejected through the workflow approval routes or the portal's Approvals page |
Multi-agent workflow or plan step, outside confirm and step mode (POST /api/v1/workflows/execute, and POST /api/v1/plan/execute in auto, sequential, parallel or balanced mode) | Withheld, on every deployment. The call answers HTTP 403 with code approval_requires_durable_record. The step and every step after it do not run, and no reviewer entry is created, because these routes keep no durable approval record. To have a person approve multi-agent steps, run the plan in confirm or step mode: each step is then held on the workflow step gate (confirm mode holds every step without asking the policy engine; step mode runs the first step and holds each later one). From v11.1.0 every multi-agent step is decided on every deployment, and AXONFLOW_HITL_ENABLED is ignored |
MCP routes and POST /api/v1/decide (from v11.1.0, Enterprise) | Held as a pending approval: the call is refused with a pending_approval naming a reviewer entry (decide answers verdict: "needs_approval"), and a retry of the same call naming the approval passes once after a person approves it. See Pending approval on MCP and decide |
POST /api/request, POST /api/policy/pre-check, the MCP response pass, and every agent plane on the Community build | Refused with reason approval_required, naming the enforcement point. Nothing is held and no reviewer entry is created |
So a requirement that applies everywhere also reaches the enforcement points above that do not hold, where a matching request is refused or withheld rather than held open. From v11.1.0, binds_on confines it to the enforcement points that hold (see Where a requirement binds).
A workflow or plan step is presented to the engine as the action its step type maps to, and with no content: llm_call as llm.completion; tool_call and connector_call as tool.call; human_task as agent.invoke. On a multi-agent plan, llm-call maps to llm.completion, and connector-call, function-call and api-call map to tool.call. A requirement therefore selects steps by action and by who is calling, and, from v11.1.0, by the step's name (args.context.step__name) and, on the workflow step gate, by its tool (args.context.tool__name, the gate's tool_context.tool_name; a multi-agent step carries no tool name). It cannot select a step by a value in its input, so keep checks on values such as an amount in your application.
A step's name and tool are caller-typed labels: the workflow sends them with the step. Declare the label optional and read it only as a positive equality whose absence is a non-match:
{ "path": "args.context.step__name", "type": "string", "optional": true }
{ "kind": "compare", "path": "args.context.step__name", "op": "eq", "literal": "export_ledger", "on_absent": "no_match" }
Publish refuses any other use of a label: in a permission (a label never grants), inside unless or under not, with an operator other than eq, as a set or attribute comparison, or against an empty string. To match any of several names, use an or of such comparisons. A condition on a step's name applies only to requests that carry one: a request that is not a workflow or plan step carries no name and is not matched, and a caller that omits or renames the step is not caught by it. Scope the policy to the step's action as well.
A held step's severity comes from the platform's risk score for the step. A typed approval carries no severity of its own.
Where a requirement binds
From v11.1.0 any policy in your document can name the enforcement planes it applies on, in binds_on. On every other plane the policy is left out, as if it were withdrawn there: nothing is refused or held for it, and no decision names it. A policy without binds_on applies on every plane, which is what every document published before v11.1.0 means.
| Plane | Where requests reach it | Actions it presents |
|---|---|---|
wcp | The workflow step gate | tool.call, llm.completion, agent.invoke |
orchestrator_request | POST /api/v1/process and POST /api/v1/plan/execute, from v11.1.0. On POST /api/v1/plan/execute this plane decides the request to run the plan, as agent.invoke; the steps the plan then runs are decided on map. Neither route can hold an approval: there a requirement is refused approval_required, never held | llm.completion, agent.invoke |
map | Multi-agent workflow and plan steps: POST /api/v1/workflows/execute, and POST /api/v1/plan/execute outside confirm and step mode. An approval requirement is withheld here, never held | tool.call, llm.completion |
mcp:request | MCP tool calls, including check_policy | tool.call |
mcp:response | What an MCP tool returns | tool.call |
decide | POST /api/v1/decide | tool.call, llm.completion, agent.invoke |
gateway_request | POST /api/policy/pre-check | tool.call, llm.completion |
proxy_request | POST /api/request | llm.completion |
openai_compatible | The OpenAI-compatible API | llm.completion |
orchestrator_response | The response of POST /api/v1/process | llm.completion |
cowork_ingest | Enterprise only: what the Claude Cowork and Claude Code OTEL ingest stores (POST /v1/logs, POST /v1/metrics) | tool.call, llm.completion |
Until v11.1.0 the two request routes decided under wcp, so a policy naming wcp applied to the workflow step gate and to POST /api/v1/process and POST /api/v1/plan/execute. A document published before v11.1.0 keeps that meaning: it still applies on both, because the document records the vocabulary it was published against. Republish it and wcp means the step gate alone, as the table says. Nothing you published is narrowed by upgrading.
To hold workflow steps for approval without refusing MCP tool calls, scope the requirement to the planes that hold:
"binds_on": ["wcp"]
A multi-agent plan run in confirm or step mode is held on the workflow step gate by its mode, which asks no policy; a requirement's challenge on such a step is refused approval_required. Add map only if you want the requirement to also withhold multi-agent steps run in any other mode. There they are refused with approval_requires_durable_record, not held.
Publication refuses a list that could not mean what it says:
| Code | Refused |
|---|---|
BINDS_ON_EMPTY | "binds_on": []. Leave binds_on out to apply the policy everywhere |
PLANE_NOT_DECLARED | A plane your deployment does not enforce, including mcp without its phase (mcp:request or mcp:response) |
ACTION_NOT_PRESENTED_ON_PLANE | A plane that presents none of the actions the policy selects, for example tool.call on openai_compatible |
BINDS_ON_DUPLICATE_PLANE | The same plane twice |
DETECTOR_CONTROL_BINDS_NOWHERE | The policy reads a platform detector and binds_on names only planes that run no detector at all (wcp, map, orchestrator_request), so it would apply nowhere |
It also warns, without refusing, when an approval requirement applies where it cannot hold:
BINDS_ON_MCP_RESPONSE: it applies onmcp:response, by naming it or by leavingbinds_onout. The MCP response pass cannot hold, so there a tool's response is refusedapproval_required.BINDS_ON_ORCHESTRATOR_REQUEST_NO_HOLD: it applies onorchestrator_request, by naming it or by leavingbinds_onout, and selects an action those routes present.POST /api/v1/processandPOST /api/v1/plan/executecannot hold, so there the request is refusedapproval_required; only workflow steps are held. Before v11.1.0 this warning namedwcp(BINDS_ON_WCP_ROUTE_SEAM) and fired only when you named that plane explicitly.BINDS_ON_MAP_NO_HOLD: it applies onmap, by naming it or by leavingbinds_onout, and selects an action that plane presents (llm.completion,tool.call). A multi-agent step there is withheld withapproval_requires_durable_recordand never runs; nothing is held. No plane holds a multi-agent step's approval: confirm and step mode hold steps by their mode, without asking the policy engine, and refuse a challenged step withapproval_required. Bind the requirement on the planes that hold (wcpfor workflow steps, and on Enterprisemcp:requestfor a tool call anddecide); if a person must gate multi-agent steps, run the plan in confirm or step mode. New in v11.1.0.
It also warns with DETECTOR_CONTROL_UNBOUND_ON_PLANES when a policy reads a platform detector and some of the planes it applies on run no detector. See A policy that reads a detector.
binds_on does not change confirm or step mode on a multi-agent plan: those hold steps without asking the policy engine. GET /api/v1/typed-policies/active/summary counts one plane and lists in not_bound_here the policies of your document that do not apply on it.
A policy that reads a detector
Some policies read a platform detector: the DROP TABLE detector, the SQL-injection detectors, a PII detector. A detector runs only where the platform evaluates it, and three enforcement planes run none at all: the workflow step gate (wcp), the multi-agent plane (map) and the orchestrator's request routes (orchestrator_request).
From v11.1.0 a policy of yours that reads a detector does not apply on those three planes. It is left out of them exactly as binds_on leaves a policy out: nothing is refused or held for it there, and no decision names it. GET /api/v1/typed-policies/active/summary names it, the detector it reads and the planes that do run it, and publication warns you with DETECTOR_CONTROL_UNBOUND_ON_PLANES.
Before v11.1.0 such a policy could not be evaluated on those planes, and so it refused every request that reached them, whatever the request contained. If you relied on one to refuse requests on POST /api/v1/process, POST /api/v1/plan/execute, workflow steps or multi-agent steps, it was refusing them because it could not be evaluated. Requests that reach AxonFlow through the Agent are unaffected: the detector runs there, and the policy applies as it always did.
The document
This document holds every tool.call workflow step in the organization for approval, and leaves MCP tool calls alone. Adapt the identifiers, the action, the condition and the planes:
{
"document": {
"api_version": "authoring.axonflow.com/v1",
"metadata": {
"document_id": "refund-approvals",
"title": "Refund tool calls wait for a reviewer"
},
"policy": {
"root": "organization",
"version": 1,
"attributes": [
{ "path": "principal.id", "type": "string", "optional": false },
{ "path": "action.id", "type": "string", "optional": false },
{ "path": "action.tags", "type": "array", "optional": false }
],
"policies": [
{
"id": "refunds.approval.tool_call",
"authority": "requirement",
"root": "organization",
"scope": { "organization": true },
"actions": { "actions": [{ "kind": "action", "type": "Action", "local": "tool.call" }] },
"where": { "kind": "compare", "path": "action.id", "op": "eq", "literal": "Action::tool.call" },
"obligations": [
{
"type": "approval_challenge",
"params": { "eligible": "Group::axonflow-trusted-header:refund-approvers", "quorum": "1" },
"mandatory": true,
"source_policy": "refunds.approval.tool_call",
"schema_version": 1
}
],
"mandatory": true,
"assurance": "enforcement",
"binds_on": ["wcp"]
}
]
}
},
"fixtures": [
{
"name": "a refund tool call is held",
"attributes": {
"principal.id": { "state": "known", "value": "User::axonflow-minted:[email protected]", "source": "directory", "source_version": 1, "observed_at": "2026-01-01T00:00:00Z" },
"action.id": { "state": "known", "value": "Action::tool.call", "source": "platform", "source_version": 1, "observed_at": "2026-01-01T00:00:00Z" },
"action.tags": { "state": "known", "value": ["stage:tool"], "source": "platform", "source_version": 1, "observed_at": "2026-01-01T00:00:00Z" }
},
"expect": { "refunds.approval.tool_call": "MATCH" }
}
]
}
| Field | What it is |
|---|---|
metadata.document_id | The document's stable identity across versions. Required |
metadata.title | A human-readable name |
policy.root, policies[].root | organization: the only root you can publish under |
policy.version | Advances with each version of the document you publish |
policy.attributes | Every attribute path the policies and fixtures use, with its type |
policies[].id | The policy's identifier. It is recorded on every decision it takes part in, and a fixture names it |
authority | requirement: attaches a mandatory obligation (see Grants and ceilings) |
scope | Who it applies to. {"organization": true} is everyone in your organization |
actions | The registered actions it reaches. Name an action your deployment registers |
where | The condition, required on every policy. The example matches every tool.call |
obligations[].type | approval_challenge |
params.eligible | The groups whose members are meant to approve: qualified group identifiers, comma-separated. At least one is required. Publication refuses a document whose eligible groups are all in realms the deployment declares non-interactive, where no person can answer (POOL_NOT_INTERACTIVE). The axonflow-trusted-header realm in the example is illustrative: name the realm your approvers' groups come from |
params.quorum | How many approvals are meant to be required: a positive whole number, as a string |
params.expiry_seconds | Optional: how long the approval stays open after the decision that raised it, in whole seconds from 60 to 604800 (7 days), as a string. Outside that range publication refuses it. See How long an approval stays open |
params.severity | Optional, from v11.1.0: the severity the reviewer entry carries, low, medium, high or critical. Any other spelling is refused. Left out, the entry's severity is derived from the risk score |
obligations[].mandatory, policies[].mandatory | true: the obligation cannot be dropped |
source_policy | The id of the policy the obligation belongs to |
schema_version | 1 |
assurance | enforcement: a deterministic control. Publication refuses a declared class that contradicts what the policy reads |
binds_on | Optional, from v11.1.0: the planes the requirement applies on. ["wcp"] holds workflow steps and leaves every other request alone. Left out, it applies on every plane (see Where a requirement binds) |
fixtures | The test cases publication runs. A publication with no fixture is refused. expect says what each named policy must return for the attributes given |
Two fields in a stored document are filled in by the server, so leave them out. metadata.author is always the signed-in caller. policy.interactive_realms is copied from the deployment's own realm declarations.
eligible and quorum do not do in v11Publication checks that eligible names at least one well-formed group, that not every one of them is in a realm the deployment declares non-interactive, and that quorum is a positive whole number. It does not check the quorum against how many people the groups contain. And they are not enforced when a step is approved: the eligible groups and the quorum are recorded on the reviewer entry for the reviewer to read, but no approve route checks that the approver belongs to an eligible group, counts approvals towards the quorum, or applies separation of duties. Treat eligible and quorum as a record of who you intend to approve, and control who can approve through access to the approval routes and the portal's Approvals page.
How long an approval stays open
A held step can be approved only inside its approval window, counted from the decision that held it. An approval after the window is refused (approval_expired), because a timeout is a deny.
- If the requirement states
expiry_seconds, that is the window, shorter or longer than the deployment's. When several mandatory requirements hold one step, the shortest stated value wins. - Otherwise the deployment's window applies:
AXONFLOW_APPROVAL_TTL_SECONDSon the Agent and the Orchestrator, 15 minutes when unset. - Both are bounded to 60 seconds..7 days and refused outside it, never rounded into range: publication refuses the parameter, and a deployment whose variable is out of range does not start.
- An advisory obligation's
expiry_secondsandseverityare ignored.
Before v11.1.0 the window was 15 minutes whatever was configured: expiry_seconds could only shorten it.
Publishing and promoting it on Enterprise
Publish and promote in the customer portal, either from the Policy Authoring page or through the portal's API under a signed-in session with the policy:write permission. In v11.0.0 the Policy Authoring page lists requirements and can remove one, but it cannot author one, so publish a requirement through the portal's API.
-
Publish, naming an approver who is not you. The publication body is the document above plus
approvers, the principals who will review this version:jq '. + {approvers: [{"kind":"principal","type":"User","qualifier":"portal","local":"[email protected]"}]}' \refund-approvals.json > publish.jsoncurl -sS -X POST "$PORTAL_URL/api/v1/typed-policies/publish" \-b "axonflow_session=$SESSION" -H 'Content-Type: application/json' \--data @publish.json | jq '{digest: .artifact.digest, findings}'A publication whose approvers do not include someone other than its author is refused with
APPROVER_IS_AUTHOR. -
The approver promotes it. The approver signs in to the portal and makes the published version live by its digest:
curl -sS -X POST "$PORTAL_URL/api/v1/typed-policies/promote" \-b "axonflow_session=$APPROVER_SESSION" -H 'Content-Type: application/json' \--data '{"digest":"sha256:…","reason":"refunds need a reviewer"}' | jqThe author cannot promote their own version, except under the single-administrator exception below. Once it is promoted, the next matching step is held. No restart is needed.
The single-administrator exception. An organization with fewer than two members who can approve has no second person to name. Its administrator can enable self-approval (PUT /api/v1/typed-policies/self-approval with {"enabled": true}, which needs the sso:configure permission and is refused self_approval_not_eligible once the organization has two eligible approvers). A self-approved publication names the author as its approver and adds self_approval_reason, which is signed into the version and recorded on its audit row. This is an exception for an organization with one approver. It is not the normal procedure: an organization with two people who can approve publishes for a second person.
If a deployment's licence later drops below Enterprise, the workflow step gate no longer creates reviewer entries: a step that would be held is withheld with decision: "block" and the reason capability_requires_upgrade. Entries created before the change can still be approved or rejected.
What activation changes
The most recent activation is your organization's active document. It is what the policy decision engine enforces for your organization, on the planes it governs, together with the platform's shipped policies. Until you activate one, the engine decides under the shipped set; activating a document replaces that implicit bundle, by digest. The platform's policies and the deployment's baseline permissions keep composing beside your document, and the 22 organization-editable policies are replaced by your document's content, so carry the ones you want to keep, which a document can do on every edition; the publish response lists any your document drops. A published document replaces the organization template: any template policy the document omits stops deciding once the document is active, and the publish and activate responses list the omissions as template_omissions. Seed every draft from the template (the portal editor does this), and treat an omission as a deliberate edit.
Every decision records the digest it was decided under (policy_bundle), so "which version of our policy produced this decision" is answered from the decision record, and survives every later edit.
What "rendered back without loss" means
Every published version has a Source view showing the exact bytes that were signed. Those bytes parse back into the document you wrote, re-render identically, and recompile to the module being enforced - all three were proven before the signature and are proven again every time the version is loaded.
That matters for a reason worth stating plainly: an operator reading policy source has no other way to know that what they are reading is what is being enforced.
Limits
| Limit | Detail |
|---|---|
| One authority root | You write your organization's document. The platform's shipped policies are a separate root and cannot be authored here; you change how one of them behaves for your organization with its per-policy controls (disable, re-enable, change the action), not by editing it. |
| Edition limits | GET /api/v1/typed-policies/edition reports what your edition may write, and max_documents, which despite its name is how many policies your organization may author: 20 on Community, 50 on Evaluation, unlimited on Enterprise. |
| Portal editor | Enterprise, where an Enterprise organization publishes, compares and rolls back. Community and Evaluation run the lifecycle through the API. |
| v10.x policies are not converted automatically | A system-policy or tenant-policy row your organization created in v10.x is not carried into v11. Re-author it as a typed policy, or on Enterprise import it with the operator-run import command before you upgrade; the v10 → v11 Migration Guide covers both. |
Related
- Policy and Identity Control Plane - what decides a request, and what your organization controls.
- Shipped Policy Posture - every policy that runs before you write any.
- Policy Hierarchy - the platform's policy root and your organization's.
- Testing Policies - dry runs before you activate.
