Managing Policies
In v11 an organization manages three things:
| What | Where | What you do with it |
|---|---|---|
| The shipped policies | The platform's policy root | Read their actions; disable, re-enable or re-action each one for your organization |
| Your own policies | Your organization's typed policy document | Write, validate, publish, activate and roll back |
| The policy rows you created in v10.x | The v10.x system-policy and tenant-policy tables, now read-only | Export them, then re-author or import them |
The v10.x system (static) and tenant (dynamic) policy families are closed to writes, and their rows no longer decide anything: the engines that read them author no verdict in v11. The field reference below remains so you can read what you exported.
Managing the shipped policies
Every shipped policy, its category, its action on each plane and why is listed in Shipped Policy Posture.
For each shipped policy your organization can:
- disable it, so it no longer decides for your organization;
- re-enable it;
- change its action to log, warn, redact or block, making it stricter or more lenient.
The change applies to your organization only. To change the platform's policies in a whole category at once, record a detection-posture override: pii reaches the 18 pii-* platform policies, sqli the 38 SQL-injection platform policies and dangerous_command the four indirect prompt-injection guards; dangerous_query changes no policy, and obligation_fallback sets what happens when a plane cannot carry out a redaction.
A category-wide override recorded for your organization, such as every platform PII policy to redact, reaches the engine on the Decision API, Gateway Mode's pre-check, MCP, the OpenAI-compatible route, Proxy Mode's request, the orchestrator's response filter and the policy test surface: there the platform's policies in that category decide with the recorded action for your organization. See Detection posture. It does not reach the 22 organization-editable policies, among them the dangerous-command, DROP TABLE, TRUNCATE and two SQL-injection blocks: your organization changes those in its own typed policy document.
Managing your organization's policies
Policies your organization writes live in its typed policy document, authored through /api/v1/typed-policies on every edition, or the portal's Policy Authoring page on Enterprise:
| Method | Path | Purpose |
|---|---|---|
GET | /api/v1/typed-policies/edition | The vocabulary and your edition's limits |
POST | /api/v1/typed-policies/validate | Validate a document |
POST | /api/v1/typed-policies/publish | Publish a version with its test cases |
POST | /api/v1/typed-policies/activate | Activate a published version by digest |
GET | /api/v1/typed-policies/active | The document in force |
GET | /api/v1/typed-policies/system | The platform's shipped policies, read-only: 116 compiled from the 85 shipped rows |
Typed Policy Authoring walks through the lifecycle, and Policy Syntax documents the parts of a policy.
Managing policies in the portal
On Enterprise the customer portal carries:
- the Policy Authoring page, for your organization's typed policy document: edit, validate, compare with live, publish with test cases, activate, and roll back by digest;
- the published-version history, with each version's signed source and every activation's actor and reason;
- the Detection Posture settings, which re-action the platform's policies in a whole category for your organization.
The v10.x Policies page, which listed and edited system and tenant policy rows, is removed in v11.
The v10.x policy families, read-only
v10.x had two families of policy, both of which you may still hold rows in after upgrading:
| Family | Where it lived | v10.x API path | Typical use |
|---|---|---|---|
| System policies (also called static policies) | The Agent | /api/v1/system-policies | Pattern-based checks: PII detection, SQL-injection blocking, secret-leak detection |
| Tenant policies (also called dynamic policies) | The Orchestrator | /api/v1/tenant-policies | Context-aware rules built from conditions and actions: risk, cost, user, content, and media governance |
/api/v1/static-policies and /api/v1/dynamic-policies are their deprecated aliases.
In v11:
- Writes are refused. A create, update, delete, toggle or import on either family answers
409 LEGACY_POLICY_WRITE_FROZEN, and the error names/api/v1/typed-policies. - Reads remain, for export. The list, get, effective, versions and export routes still answer, so you can take what you had before you re-author it. Every response on these routes (reads, the
409on writes,401and503) carriesLink: </api/v1/typed-policies>; rel="successor-version"andX-AxonFlow-Removed-In: v11.1, and from the v11.0.0 release aDeprecationheader (RFC 9745) dated to the release; v11.1 removes the routes. - A row you created is not carried forward. Re-author it as a typed policy, or on Enterprise import it with the operator-run import command; the v10 → v11 Migration Guide covers both. The 85 system rows AxonFlow shipped, and the 22 it shipped at the tenant tier, are carried forward as the shipped set.
To export your tenant policies:
curl "http://localhost:8080/api/v1/tenant-policies/export" \
-H "Authorization: Basic $(echo -n 'CLIENT_ID:CLIENT_SECRET' | base64)"
Policy Field Reference
The tables below describe the fields of a v10.x policy row, as the read routes and an export return them.
System (static) policy fields
| Field | Type | Meaning | Values |
|---|---|---|---|
name | string | Human-readable policy name | Up to 255 characters |
description | string | Plain-English explanation of what the policy detects and why it exists | Free text |
category | string | Classification that groups the policy (see Policy Syntax → Categories) | One of the thirteen system-policy categories, e.g. security-sqli, pii-global, pii-us, code-secrets, sensitive-data |
pattern | string | Regular expression matched against request content | Go (RE2) regular expression |
action | string | What the Agent did when the pattern matched | block, redact, warn, log or require_approval |
severity | string | How serious a match is | low, medium, high, critical, or empty on a row created without one |
priority | integer | Evaluation order - higher values were evaluated first | Integer; 50 when created without one |
enabled | boolean | Whether the policy was active | true / false |
tags | array of strings | Free-form labels for grouping (for example compliance:hipaa) | Optional |
tier | string | Which tier the policy belonged to | system, organization or tenant |
risk_level | string | Risk classification used by the session-override contract, seeded from the category | low, medium, high, critical |
allow_override | boolean | Whether an administrator could relax this policy with a session override; false whenever risk_level is critical | true / false |
id | string (UUID) | Internal unique identifier | Assigned by AxonFlow |
policy_id | string | Stable string identifier (slug) | custom_ followed by 12 hex characters for a policy you created |
tenant_id | string | Tenant the policy belongs to | From the credentials that created it |
org_id | string | Organization the policy belongs to | The creating caller's organization |
segment_id | string | Governance segment a policy is scoped to | Present only on effective reads of a segment-scoped policy |
version | integer | Increments on every change | Assigned by AxonFlow |
created_at / updated_at | string (ISO 8601) | Creation and last-modified timestamps | Assigned by AxonFlow |
created_by / updated_by | string | Who created or last changed the policy | From the X-User-ID header |
deleted_at | string (ISO 8601) | Set when a policy is soft-deleted | Assigned by AxonFlow |
metadata | object | Structured metadata attached by AxonFlow | Assigned by AxonFlow |
A read through /api/v1/system-policies/effective also carries the override information that applied in v10.x:
| Field | Type | Meaning |
|---|---|---|
has_override | boolean | Whether an override was in effect for this policy |
override_action | string | The action the override enforced in place of action |
override_enabled | boolean | The enabled state the override enforced in place of enabled |
override_expires_at | string (ISO 8601) | When a temporary override reverts |
override_reason | string | Why the override was created |
Tenant (dynamic) policy fields
| Field | Type | Meaning | Values |
|---|---|---|---|
name | string | Human-readable policy name | 3-100 characters |
description | string | Plain-English explanation of the rule | Up to 500 characters |
type | string | The kind of policy | One of content, user, risk, cost, context_aware, media, rate-limit, budget, time-access, role-access, mcp, connector |
category | string | Classification for filtering and reporting | Starts with dynamic- (for example dynamic-risk) or media- (for example media-pii) |
conditions | array | The rules that decided whether the policy matched (see Conditions) | At least one condition |
actions | array | What happened when all conditions matched (see Actions) | At least one action |
priority | integer | Evaluation order - higher values were evaluated first | 0-1000 |
enabled | boolean | Whether the policy was active | true / false |
tags | array of strings | Free-form labels for grouping | Optional |
tier | string | Which tier the policy belonged to | organization or tenant |
id | string (UUID) | Internal unique identifier | Assigned by AxonFlow |
tenant_id | string | Tenant the policy belongs to | From the credentials that created it |
organization_id | string | Organization scope for organization-tier policies | Assigned by AxonFlow |
version | integer | Increments on every change | Assigned by AxonFlow |
created_at / updated_at | string (ISO 8601) | Creation and last-modified timestamps | Assigned by AxonFlow |
created_by / updated_by | string | Who created or last changed the policy | From the X-User-ID header |
deleted_at | string (ISO 8601) | Set when a policy is soft-deleted | Assigned by AxonFlow |
Conditions
A condition is an object with three keys. All conditions in a policy had to match for the policy to trigger.
{ "field": "risk_score", "operator": "greater_than", "value": 0.8 }
| Key | Meaning |
|---|---|
field | The request attribute to inspect |
operator | How to compare the field to value |
value | The value (or list of values) to compare against |
field values: query, response, user.email, user.role, user.department, user.tenant_id, risk_score, request_type, connector, cost_estimate, the media-governance fields (media.has_faces, media.face_count, media.has_biometric_data, media.nsfw_score, media.violence_score, media.content_safe, media.document_type, media.is_sensitive_document, media.has_pii, media.pii_types, media.has_extracted_text, media.extracted_text_length), and the retry-aware workflow fields (step.gate_count, step.completion_count, step.prior_completion_status, step.prior_output_available, step.last_decision, step.first_attempt_age_seconds, step.idempotency_key).
operator values: equals, not_equals, contains, not_contains, contains_any, regex, greater_than, less_than, in, not_in.
When you re-author a tenant policy as a typed policy, its conditions become the policy's where clause over the deployment's vocabulary; see Policy Parameters Explained → Conditions.
Operator semantics
How the v10.x tenant-policy engine compared each operator, from platform v9.19.0 on, when every engine that evaluated a tenant policy ran the same matcher:
| Operator | Behavior |
|---|---|
contains, not_contains | Case-insensitive everywhere. |
contains_any | Every item in the value list is compared as text, whatever its JSON type. |
greater_than, less_than | A numeric string parses and compares. An operand that is neither a number nor a parseable numeric string is not comparable, and the condition is false. |
regex | The value must be a string. A non-string value is rejected at write time and does not match at evaluation time. |
| All ten operators | Implemented at every call site. |
These rules are what an exported row meant when it was enforced, which is what to preserve when you re-author it.
A policy with no conditions
Two different "no conditions" shapes exist in stored v10.x rows and they do not mean the same thing:
| Stored shape | What it meant in v10.x |
|---|---|
conditions is JSON null, or the key is absent | Applied to everything, the way an empty WHERE clause matches every row. This was the platform's own shape for a condition-less system policy. |
conditions is an explicitly empty array [] | Excluded from evaluation entirely from platform v9.19.0, and counted on axonflow_policy_condition_unevaluable_total{reason="empty_conditions",plane}. A released gap in the update API let a PUT clear a row's conditions to []; such rows are residue, not policy. |
An exported row with [] conditions enforced nothing in v10.x from v9.19.0 on, and has nothing to re-author.
Actions
An action is an object with a type and an optional config:
{ "type": "redact", "config": { "fields": ["ssn", "salary"] } }
type | What it did | Typical config |
|---|---|---|
block | Rejected the request | { "reason": "…" } |
redact | Masked the listed fields in the response | { "fields": ["field1", "field2"] } |
require_approval | Paused for human approval (HITL) | - |
warn | Allowed the request and recorded a warning | - |
log | Allowed the request and recorded an audit entry | - |
alert | Sent an alert to your monitoring system | Varies by alerting integration |
route | Applied an LLM routing rule | Varies by routing rule |
modify_risk | Adjusted the computed risk score | { "modifier": 1.5 } |
In v11 a shipped policy's action is one of block, redact, warn or log, and a policy you write expresses its action as its authority and obligations; see Policy Syntax → How a shipped action maps to a policy.
How risk is expressed
v10.x modeled risk in three distinct ways, none of them a free-form numeric field set on a policy:
risk_scorewas a request attribute computed at evaluation time (a value from0.0to1.0), referenced in a condition.modify_riskwas an action that nudged that computed score for downstream policies.risk_levelwas a system-assigned classification on system policies that drove the session-override contract.
In v11 a policy you write reads the computed risk score in its condition, on Evaluation and Enterprise (a policy your organization writes cannot read signal.* facts on Community; the organization-editable policies AxonFlow ships read them on every edition) , as the shipped anomalous-access policy does; see Policy Parameters Explained → Risk thresholds.
Editions and limits
- The shipped policies are enabled on every edition, and every organization controls each one for itself.
- Your own typed policies are available on every edition, within the limit
GET /api/v1/typed-policies/editionreports (20 policies on Community, 50 on Evaluation, unlimited on Enterprise): through the API on Community and Evaluation, and on Enterprise through the portal editor, where a publication names its approvers. - Group scope and human approval requirements are Enterprise constructs, and conditions on
signal.*facts in policies your organization writes need Evaluation or Enterprise. - Separation of duties on activation applies on Enterprise: a version's author cannot activate it, unless the organization has enabled self-approval.
Compare what each edition unlocks in Community vs Evaluation vs Enterprise.
Related
- Shipped Policy Posture - every shipped policy and its action
- Typed Policy Authoring - writing your organization's policies
- Policy Syntax - the parts of a policy, and the shipped categories and patterns
- Policy Parameters Explained - what the v10.x parameters became in v11
- Policy Hierarchy - the platform's policy root and your organization's
- Tenant Policy API and System Policy API - the v10.x read routes
- Policy Testing - test and validate policies before rollout
