v10 → v11 Migration Guide
v11.0.0 replaces the way AxonFlow decides a request. In v10.x two policy engines, one for system policies and one for tenant policies, decided each request, and a newer decision engine ran beside them behind switches that defaulted to off. In v11 that decision engine is the whole system: it decides on the enforcement planes it governs, for every organization, on every deployment and edition, from the first boot.
What that means on upgrade day:
- On a deployment that verifies user identity, user tokens are admitted through the identity plane on every request. A token from an issuer no trust realm declares is refused. This is the change most likely to turn a working integration into refusals, so it comes first below.
- The shipped policies are enabled and observe by default. Most warn or log; a few redact content that is unambiguously sensitive; a short list blocks. Every one can be switched off or re-actioned for your organization.
- There is no mode and no opt-out. The v10.x shadow and compatibility switches are gone, and a deployment that still sets one refuses to start.
- Policies you authored in the legacy tables are not carried forward. On Enterprise you import them; on Community you re-author them.
- The legacy policy write APIs answer
409, naming the typed route to use instead.
Where v11 decides a request differently from v10.x, that is intended. The two engines are different models, v11 makes no backward-compatibility promise between them, and nothing compares them. This guide says where the differences are, so you meet them here rather than in production.
Before you upgrade
- Declare a trust realm for every issuer of the user tokens your clients send, and find the tokens v11 would refuse while you are still on v10.x. See Identity.
- Remove every variable that now refuses boot. See Variables that refuse boot.
- Export or import the policies you authored. See Your own legacy policies.
- Read the shipped posture so no shipped action surprises you. See The shipped policies are enabled.
- Update what reads decision records: SIEM queries, dashboards and alerts keyed on v10.x policy identifiers. See What a decision records.
- Move automation off the legacy policy write APIs. See The legacy policy APIs.
- Remove the detection action variables. They are ignored, with a boot warning. See Detection action variables.
- Take the usual pre-upgrade snapshot, then plan for the migrations, which apply on boot.
Identity: declare a realm for every issuer
In v11, on a deployment that verifies user identity (Enterprise), every request's subject is admitted through the identity plane. Authentication runs first, as before: a bad credential, or a user token whose signature fails, is refused 401. Then a user token must come from a declared trust realm, its claims must bind to the credential that authenticated the request, and it must be current. On Community and Community SaaS a presented user token is ignored.
A token whose issuer no realm declares is refused UNKNOWN_REALM, and so is a token with no issuer. In v10.x at the default setting both were accepted.
What to do, by where your tokens come from
| Your user tokens are | What to do before you upgrade |
|---|---|
minted by AxonFlow (the portal or the API; iss is axonflow-user-token-mint) | Nothing to declare if your tokens come from the mint: a built-in realm covers them. Hand-minted tokens carrying that issuer must carry an exp at least one second after the epoch, email, jti and an org_id equal to the organization of the credential the request authenticates with, or a verifying deployment refuses them with 401 before any policy (#4311). A token whose org_id names another organization is refused ORG_BINDING_MISMATCH at admission. |
| issued by your identity provider (Enterprise) | Configure the provider as your organization's OIDC SSO provider and enable that configuration; its issuer, audience and JWKS URI are required. Each token must carry an iss exactly equal to the configured issuer, an aud that includes the configured audience (a token with no aud is refused AUDIENCE_REJECTED), an RS256 signature and a sub. Disabling the SSO configuration withdraws the realm within about a minute. |
asserted by a trusted upstream (AXONFLOW_TRUST_IDENTITY_HEADERS=true) | Nothing to declare, but assert a stable subject in X-User-ID. An upstream that asserts only X-User-Email is refused SUBJECT_MISSING: an email address is an alias, never an identifier. |
| sent to a Community or Community SaaS deployment | Nothing to declare, because user tokens are ignored there, as they were in v10.x: every request is decided for its client credential and recorded as subject_type: Client. Per-user identity needs Enterprise. |
There is no other way to declare a realm: no realm API and no realm variable. SAML login configures portal sign-in, not a realm for user tokens.
Find the tokens v11 would refuse, while you are still on v10.x
On v10.2.0 or later, set AXONFLOW_IDENTITY_COMPAT_MODE=shadow on the agent and the orchestrator, run representative traffic, and search the logs for identity-plane refusals of credentials the legacy path accepted:
grep '\[IDENTITY-COMPAT\]' agent.log orchestrator.log | grep 'divergence=identity_refused'
Each line names the reason v11 would refuse the request with (identity=DENY/UNKNOWN_REALM, identity=DENY/AUDIENCE_REJECTED …) and, in detail, the issuer, claim or audience responsible. Lines with divergence=identity_indeterminate are outages, such as an unreachable JWKS endpoint or revocation source.
These lines are written only while the mode is shadow or enforce. At the default off there are none, so an empty search proves nothing until you have run shadow. Remove the variable again before you upgrade: in v11 it refuses boot.
What a refusal looks like in v11
- A refused admission is a
denyverdict onPOST /api/v1/decide, and a403on Gateway Mode's pre-check and on Proxy Mode'sPOST /api/request. The reason names the identity plane's code:UNKNOWN_REALM,ORG_BINDING_MISMATCH,AUDIENCE_REJECTED,SUBJECT_MISSING,CREDENTIAL_EXPIRED,CREDENTIAL_REVOKEDand the identity plane's other admission reasons, described under Identity refusals. - When admission cannot reach an answer, for example because a realm's revocation source is unavailable, the request fails closed with
503 subject_unverifiable.
Variables and licences that refuse boot
v11 refuses to start, and names the variable or field, rather than run with a setting that no longer means anything or that would leave the engine unable to decide:
| Set to | Why v11 refuses | What to do |
|---|---|---|
AXONFLOW_DECISION_SHADOW_MODE, AXONFLOW_DECISION_SHADOW_PLANES, AXONFLOW_DECISION_SHADOW_SAMPLE_RATE, AXONFLOW_DECISION_SHADOW_QUEUE_DEPTH, AXONFLOW_DECISION_SHADOW_WORKERS, AXONFLOW_DECISION_SHADOW_MATCH_LOG_EVERY, AXONFLOW_DECISION_SHADOW_REALM, AXONFLOW_DECISION_SHADOW_CONTENT_TARGET: any non-empty value | The decision shadow mode is removed | Remove them |
AXONFLOW_IDENTITY_COMPAT_MODE, AXONFLOW_IDENTITY_COMPAT_ENFORCE_REASONS, AXONFLOW_IDENTITY_COMPAT_AGREEMENT_LOG_EVERY, AXONFLOW_IDENTITY_COMPAT_PATHS: any non-empty value | The identity compatibility mode is removed; identity admission always runs | Remove them |
MCP_STATIC_POLICIES_ENABLED or GATEWAY_STATIC_POLICIES_ENABLED set to false; MCP_STATIC_POLICIES_SKIP_CATEGORIES or GATEWAY_STATIC_POLICIES_SKIP_CATEGORIES set to a list naming a category a plane evaluates; MCP_STATIC_POLICIES_CONNECTORS set to any value | A narrowed detector would leave the engine unable to decide the policies that read it, on every request | Remove them. To silence a shipped policy, disable it for your organization |
A licence carrying neither deployment_id (V3) nor org_id (V2) | Its requests would belong to no organization, so there is nothing for the engine to decide for | Use a licence the licence server issued; it never issues one without |
An empty value boots, so a compose file or template that passes one of these variables through empty needs no change.
On CloudFormation, the stack templates no longer carry the ten parameters behind these switches. Remove them from your parameter overrides before you update the stack: DecisionShadowMode, DecisionShadowPlanes, DecisionShadowSampleRate, DecisionShadowQueueDepth, DecisionShadowWorkers, DecisionShadowMatchLogEvery, IdentityCompatMode, IdentityCompatEnforceReasons, IdentityCompatPaths and IdentityCompatAgreementLogEvery. If you ran the shadow search on a CloudFormation stack, IdentityCompatMode is the parameter you set.
The identity settings admin API no longer carries compat_mode, decision_shadow_mode or decision_shadow_planes: a request that still sends one is refused 400, naming the field. See Identity Plane APIs.
The shipped policies are enabled
AxonFlow ships three groups of policies, and Shipped Policy Posture lists every one with its category, its action on each plane and why:
- 85 platform policies, the 70 pattern-based and 15 condition-based policies v10.x called system policies;
- 22 organization-editable policies, the ones v10.x shipped as tenant policies, including the dangerous-command blocks and the
DROP TABLEandTRUNCATEguards; - 9 detectors that ship disabled and decide nothing in v11.
All of them are enabled from the first boot, and their actions observe rather than intervene by default: warn or log for nearly everything, redact only where the content is unambiguously sensitive, block for a shorter list: dangerous commands and access (among them destructive file-system and shell commands, and credential-file, cloud-metadata and internal-network access), DROP TABLE and TRUNCATE, two SQL-injection patterns, prompt-injection attempts in a request, administrative-table access on the proxy request path, and a few condition-based, country-specific and media policies. An organization that has published nothing runs this set together with the deployment's baseline permissions, so every request is decided from the first boot, including on Community. No request is refused because an organization has not written a policy.
Where a shipped policy behaves differently from v10.x
- One policy, a different action on different planes. In v10.x some platform policies resolved a different action depending on which plane asked. v11 keeps, on each plane, the action v10.x enforced there, and the posture table names the planes for each. The 22 organization-editable policies each apply one action on every plane that runs them:
drop_table_prevention,truncate_prevention,sql_injection_or,sql_injection_unionand the tensys_dangerous_*policies block;pii_ssn_detectionand the foureu_gdpr_*policies redact; and the threeeu_ai_act_*policies record the match (log),eu_ai_act_high_value_transactionfor the reason the next item gives. - An approval with no approvers.
eu_ai_act_high_value_transactionnames an approval, but a shipped policy carries no approver pool for an approval to bind to, so v11 records the match (log). - Approval on the Decision API. A policy that requires approval on
POST /api/v1/decideis refused, with a reason beginningapproval_required, rather than answeredneeds_approval, because approval execution is not wired on that plane. - No environment variable changes an action. See Detection action variables.
- The FinCrime policy pack of the Fraud & Risk add-on ships as a typed policy document in v11.0. A deployment installs it by naming it in
AXONFLOW_POLICY_PACKS, and its ten policies compose onto your organization's root, where the v11 engine decides them: four block and six require approval. Its page describes the pack.
Your own legacy policies
Policies you authored in v10.x, as tenant policies or as your own system-policy rows, segment-scoped ones included, are not carried forward. v11's engine decides from the shipped policies, the organization-editable policies, the deployment's baseline permissions, your organization's activated typed document and its recorded category overrides. It does not read the legacy policy tables, so a rule you wrote there stops deciding when you upgrade, unless you bring it into the typed model and activate it.
- Enterprise: run the operator import, before or at upgrade: capture, dry run, write, then activate what it published. Publishing does not activate, and activation is by a different person from the author. See Importing Legacy Policies into the Typed Model.
- Community: re-author them as a typed policy document; see Typed Policy Authoring.
- Export before v11.1. The legacy read routes stay in v11 as a read-only, deprecated export surface: every response on them carries
Link: </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 them.
Two kinds of organization record do carry forward:
- Category detection-posture overrides (
pii=redact,sqli=block…) reach the engine. A detection-posture override re-actions the platform's policies in its category, on the planes that apply organization overrides (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):piireaches the 18pii-*platform policies,sqlithe 38 SQL-injection platform policies, anddangerous_commandthe four indirect prompt-injection guards. It does not reach the 22 organization-editable policies, among them the dangerous-command,DROP TABLE,TRUNCATEand two SQL-injection blocks, which your organization changes in its own typed policy document. - Per-policy overrides (
/api/v1/system-policies/{id}/overrideand its/api/v1/policy-overridesalias) are legacy routes, deprecated with the rest of their family. The v11 engine does not read them, so they change no decision; existing overrides are imported once at upgrade into the typed per-policy lever, which is how an organization disables, re-enables or re-actions one shipped policy in v11, and the legacy table then becomes read-only. A detection category's platform policies are re-actioned with the detection posture, which the engine reads.
Session overrides (the time-boxed allow-bypass records) are not a lever in v11: the engine has no opt-out, so a session bypass does not silence it.
Configuring policy in v11
- One shipped policy, for your organization: disable it, re-enable it, or change its action to log, warn, redact or block.
- The platform policies of a detection category: record a detection-posture override. It does not reach the organization-editable policies, which your document changes.
- Policies of your own: author a typed policy document through
/api/v1/typed-policiesor the portal's Policy Authoring page. The deployment's baseline permissions compose beside your document, so it narrows what is permitted with constraints and never has to re-declare permissions. A new document starts from the 22 organization-editable policies, and activating it replaces them with your document's content, so keep the ones you want by carrying them; the publish response lists any your document drops. An organization with fewer than two eligible approvers may enable self-approval, which the audit row records. - No variable is needed to author policies.
AXONFLOW_TYPED_AUTHORING_CATALOGis honoured if set. - The portal shows only the new model. The legacy Policies page is removed; Policy Authoring is where policy is written.
The legacy policy APIs
The legacy policy tables are read-only in v11.
- Every write through the legacy policy APIs answers
409 LEGACY_POLICY_WRITE_FROZEN, and the message names the typed route,/api/v1/typed-policies, to use instead. That covers the system-policy and tenant-policy families on both their current and deprecated paths, the unified policy dispatcher, and template apply. - A deployment that still connects to the database as its owner rather than as the application role is not refused by the freeze; its writes are accepted and decide nothing.
- Reads stay as a deprecated export surface until v11.1 removes them; every response on these routes carries
Link: </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.POST …/teston the legacy families is deprecated in v11.0.0 and removed in v11.1.0: it evaluates a legacy row with an engine that no longer decides. To preview a decision with the enforcing engine, use the Agent'sPOST /api/policies/test, which previews what Proxy Mode's request check would decide. - The portal's unified policy reads leave with the portal's legacy Policies page.
The reference pages say which route answers what: System Policy API, Tenant Policy API, Policy Templates API and Unified Policy API.
What a decision records
Every decision now says what produced it, on the wire and on its audit row:
| Field | Meaning |
|---|---|
engine | anchored: the v11 decision engine authored the verdict |
policy_bundle | the digest of the policy bundle that decided: your organization's activated document, or the shipped-set bundle when it has published none |
subject_type | who the decision was evaluated for: a user, or Client for the client credential |
decision_reason | the engine's reason, such as explicit_constraint; on the wire it is carried in reasons |
evaluated_policies carries v11 policy identifiers. Where v10.x returned sys_pii_ssn, v11 returns corpus:static_policies:sys__pii__ssn:warn: the source family, the policy identifier with each underscore doubled, and, for a policy whose action differs by plane, the action. An allow also lists the permission that allowed it. The identifier families are: corpus:static_policies:<name> and corpus:dynamic_policies:<name> for a shipped policy (each underscore doubled, :<action> where the action differs by plane, #<n> where one v10.x row carried several actions); organization_override:<id> for a shipped policy your organization's override re-actioned; baseline.permit.<action> for the deployment's baseline permission; and your own document's policy ids. Update every SIEM query, dashboard, alert and client that matches on a v10.x policy identifier.
The Agent's /health lists the planes it decides on, under decision.enforcing_planes.
Retire what nothing emits any more: the [IDENTITY-COMPAT] log lines, the decision shadow counters and log lines, the v9.19.0 displacement advisory reason on the Decision API's allow verdict, and any dashboard or alert built on them. axonflow_agent_policy_stored_action_displaced_total still counts.
Detection action variables
PII_ACTION, SQLI_ACTION, DANGEROUS_COMMAND_ACTION, SENSITIVE_DATA_ACTION, their MCP_* and GATEWAY_* copies, SQLI_BLOCK_MODE, PII_BLOCK_CRITICAL, DANGEROUS_QUERY_ACTION, HIGH_RISK_ACTION, AXONFLOW_PROFILE and AXONFLOW_ENFORCE no longer set an action. A deployment that still sets one boots, logs one warning per variable and counts it in axonflow_ignored_posture_env_total{name}; the shipped action decides. Replace any posture you relied on with a detection-posture override or a per-policy change. Environment Variables Reference lists them, and Governance Profiles maps each removed profile to its replacement.
On CloudFormation, the AWS Marketplace template no longer carries the four parameters that set these variables: SQLIAction, PIIAction, SensitiveDataAction and DangerousQueryAction. Unlike a variable, a parameter is not ignored: a stack update that still passes one fails, because the template no longer declares it. Remove them from your parameter overrides.
Community deployments
A Community deployment is governed from its first boot with nothing to configure: the shipped policies, and the deployment's baseline permissions, decide every request. Community verifies no per-user identity, so a presented user token is ignored and every request is decided for its client credential (subject_type: Client). The boot refusals above apply on Community exactly as on Enterprise, and an organization can author policies of its own through /api/v1/typed-policies. The scale limits below apply to it.
Community and Evaluation scale limits
v11 enforces the scale limits a Community or Evaluation licence carries. They are read from the signed licence in AXONFLOW_LICENSE_KEY, never from the deployment mode, and Enterprise has none:
| Limit | Community | Evaluation |
|---|---|---|
| Human principals: distinct users admitted | 25 | 75 |
| Service principals: distinct service credentials admitted | 5 | 25 |
| Agent nodes running at once | 1 | unlimited |
| Policies your organization authors | 20 | 50 |
A request that would go over a limit is refused 402 with ERR_TIER_LIMIT_HUMAN_PRINCIPAL, ERR_TIER_LIMIT_SERVICE_PRINCIPAL or ERR_TIER_LIMIT_ORG_ROOT_POLICY, counted in axonflow_tier_limit_refusals_total and recorded on the audit trail. A principal counts from the first time it is admitted and keeps counting; the count is not a trailing window. On Community a second agent node waits up to fifteen minutes for another node's lease to lapse, then refuses to run; a redeployed container is not a second node, because a lease lapses three minutes after its last renewal, and a node that cannot reach the lease store keeps serving. If the admission ledger cannot be reached, a principal the process has already admitted is still decided, and a new one is refused with the reason dependency_unreachable, which is not a limit. An expired licence falls back to the Community limits.
Before you upgrade a Community or Evaluation deployment, count the users and service credentials that call it, the agent nodes it runs and the policies you plan to write. Traffic beyond a limit is refused after the upgrade.
The migrations
This release adds 22 migrations. They run on boot, in order, on a deployment coming from v10.4.x. A deployment coming from an earlier release also crosses every migration between its release and v10.4.0; the v9 → v10 Migration Guide and the intermediate release notes list those.
| Migration | Edition | What it does |
|---|---|---|
core/171 | every deployment | Adds the append-only tier-admission ledger and the node leases behind the Community and Evaluation scale limits (new tables) |
core/172 | every deployment | Makes static_policies and dynamic_policies read-only to the application roles. No data changes |
core/173 | every deployment | Seeds the five sys_media_* media governance policies as migration rows |
core/174 | every deployment | Closes view-based write paths into the legacy policy tables |
core/175 | every deployment | Makes the deployment licence promotion report the row it wrote |
core/176 | every deployment | Adds durable typed-authoring storage: published artifacts, activations and signing keys (new tables) |
core/177 | every deployment | Gives the six sys_sensitive_* policies an explicit stored action per phase |
core/178 | every deployment | Restricts typed-authoring storage to organization-root policy |
core/179 | every deployment | Deletes the inert v10.x sensitive_data_control seed row, only where it is still unmodified |
enterprise/155 | Enterprise | The Enterprise-only origin of core/176's tables; superseded by it |
enterprise/156 | Enterprise | Widens the per-organization decision mode record, which v11 no longer reads |
core/181 | every deployment | Adds typed_policy_audit: one append-only row for every typed-policy publish, promote and rollback, written in the same transaction as the write itself (new table) |
core/183 | every deployment | Imports each organization's legacy per-policy overrides, once, into an unpublished draft. The draft decides nothing until the organization publishes it |
core/184 | every deployment | Admits withdraw as an activation kind and as an audited action, each with its reason |
core/185 | every deployment | Gives the organization template's four SQL keyword patterns a word boundary, so they match whole words only |
enterprise/157 | Enterprise | Drops the per-organization typed-authoring flag. Typed authoring is on by default in v11 and needs no flag |
enterprise/158 | Enterprise | Adds the per-organization self-approval setting, for an organization with fewer than two eligible approvers |
enterprise/159 | Enterprise | The same change as core/184, for the activation ledger enterprise/155 declares |
enterprise/160 | Enterprise | Retires a seed bundle that had been applied outside the migration path, so the rows it wrote are no longer carried |
community-saas/088 | Community SaaS | Adds admin_audit_log, the record behind the audited sqli=block override written for every organization it registers |
community-saas/182 | Community SaaS | Backfills that same audited sqli=block for organizations registered before this release |
industry/banking/402 | banking deployment mode | Retires the banking vertical's seeded policies; the sebi, rbi and mas-feat policy packs replace them |
core/181 has an ordered rollback. If you roll back, roll the binaries back FIRST and only then apply its down file. From this migration on, every typed-policy artifact insert and activation append writes typed_policy_audit in the same transaction, so a binary that expects the table fails every publish, promote and rollback once it is gone. That is the intended failure mode, and it is why the order matters.
What you need to do, by consumer type
Self-hosted operators
- On v10.2.0 or later, run the shadow search for refused tokens against representative traffic.
- Declare a realm for every issuer it names, as in the realm steps.
- Remove every variable in Variables and licences that refuse boot from every agent and orchestrator environment, and the ten CloudFormation parameters from your stack overrides.
- Remove the detection action variables, and on CloudFormation their four parameters from your stack overrides.
- On Enterprise, import your own legacy policies and plan who activates them; on Community, re-author them.
- Take a snapshot, pull the v11.0.0 images and restart the agent and the orchestrator together. The migrations apply on boot.
- After the upgrade, confirm the Agent's
/healthlists the planes it decides on underdecision.enforcing_planes, and that a request you expect to be decided by a shipped policy carriesengine: anchoredand the policy's v11 identifier.
Enterprise administrators
Read Shipped Policy Posture with the people who own your governance posture, decide which shipped policies and detection categories to change for your organization, and activate your imported documents. Remember that activating an organization's own document replaces the 22 organization-editable policies with its own content: carry the ones you want to keep.
Programmatic API consumers
- Move policy writes to
/api/v1/typed-policies; a legacy write answers409. Read the legacy policy APIs only to export, and watch for theirDeprecationandX-AxonFlow-Removed-Inheaders. - Match on v11 policy identifiers in
evaluated_policies. - Expect
engine,policy_bundleandsubject_typein decision responses, anddenyrather thanneeds_approvalfromPOST /api/v1/decidefor a policy that requires approval. - Handle the identity refusals: a
denyor403naming the identity reason, and503 subject_unverifiable.
SIEM, audit and compliance consumers
Rewrite queries keyed on v10.x policy identifiers, add engine and policy_bundle to what you index, and retire anything built on the identity compatibility and decision shadow log lines. A decision's audit row now names the policy bundle that decided, so "which policy produced this verdict" is answered from the record alone.
SDK and plugin users
The v11.0.0 release notes list the client versions for this release. A client application that reads evaluated_policies, or that expects needs_approval from the Decision API, needs the changes under Programmatic API consumers whichever client version it uses.
Getting help
If a request is decided differently after the upgrade, the most useful information for support is:
- The endpoint, the request and the full response, including
engine,policy_bundle,subject_type,reasonsandevaluated_policies. - For an identity refusal: the token's
iss,audandorg_idclaims (not the token itself), and whether the organization has an enabled OIDC SSO configuration. - For a boot refusal: the agent or orchestrator log line naming the variable or licence field.
- For a migration failure: the log line naming the migration.
[email protected] with "v11 migration" in the subject so it routes correctly.
See also
- v11.0.0 Release Notes
- Policy and Identity Control Plane: what decides a request in v11.
- Shipped Policy Posture: every shipped policy and its action.
- Identity Compatibility Mode (removed in v11): the switches v11 removes, and identity admission.
- v9 → v10 Migration Guide: the previous major upgrade.
