Skip to main content

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

  1. 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.
  2. Remove every variable that now refuses boot. See Variables that refuse boot.
  3. Export or import the policies you authored. See Your own legacy policies.
  4. Read the shipped posture so no shipped action surprises you. See The shipped policies are enabled.
  5. Update what reads decision records: SIEM queries, dashboards and alerts keyed on v10.x policy identifiers. See What a decision records.
  6. Move automation off the legacy policy write APIs. See The legacy policy APIs.
  7. Remove the detection action variables. They are ignored, with a boot warning. See Detection action variables.
  8. 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 areWhat 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 deploymentNothing 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.

The default prints nothing

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 deny verdict on POST /api/v1/decide, and a 403 on Gateway Mode's pre-check and on Proxy Mode's POST /api/request. The reason names the identity plane's code: UNKNOWN_REALM, ORG_BINDING_MISMATCH, AUDIENCE_REJECTED, SUBJECT_MISSING, CREDENTIAL_EXPIRED, CREDENTIAL_REVOKED and 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 toWhy v11 refusesWhat 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 valueThe decision shadow mode is removedRemove them
AXONFLOW_IDENTITY_COMPAT_MODE, AXONFLOW_IDENTITY_COMPAT_ENFORCE_REASONS, AXONFLOW_IDENTITY_COMPAT_AGREEMENT_LOG_EVERY, AXONFLOW_IDENTITY_COMPAT_PATHS: any non-empty valueThe identity compatibility mode is removed; identity admission always runsRemove 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 valueA narrowed detector would leave the engine unable to decide the policies that read it, on every requestRemove 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 forUse 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 TABLE and TRUNCATE guards;
  • 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_union and the ten sys_dangerous_* policies block; pii_ssn_detection and the four eu_gdpr_* policies redact; and the three eu_ai_act_* policies record the match (log), eu_ai_act_high_value_transaction for the reason the next item gives.
  • An approval with no approvers. eu_ai_act_high_value_transaction names 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/decide is refused, with a reason beginning approval_required, rather than answered needs_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" and X-AxonFlow-Removed-In: v11.1, and from the v11.0.0 release a Deprecation header (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): pii reaches the 18 pii-* platform policies, sqli the 38 SQL-injection platform policies, and dangerous_command the four indirect prompt-injection guards. It does not reach the 22 organization-editable policies, among them the dangerous-command, DROP TABLE, TRUNCATE and two SQL-injection blocks, which your organization changes in its own typed policy document.
  • Per-policy overrides (/api/v1/system-policies/{id}/override and its /api/v1/policy-overrides alias) 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-policies or 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_CATALOG is 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" and X-AxonFlow-Removed-In: v11.1, and from the v11.0.0 release a Deprecation header (RFC 9745) dated to the release. POST …/test on 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's POST /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:

FieldMeaning
engineanchored: the v11 decision engine authored the verdict
policy_bundlethe digest of the policy bundle that decided: your organization's activated document, or the shipped-set bundle when it has published none
subject_typewho the decision was evaluated for: a user, or Client for the client credential
decision_reasonthe 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:

LimitCommunityEvaluation
Human principals: distinct users admitted2575
Service principals: distinct service credentials admitted525
Agent nodes running at once1unlimited
Policies your organization authors2050

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.

MigrationEditionWhat it does
core/171every deploymentAdds the append-only tier-admission ledger and the node leases behind the Community and Evaluation scale limits (new tables)
core/172every deploymentMakes static_policies and dynamic_policies read-only to the application roles. No data changes
core/173every deploymentSeeds the five sys_media_* media governance policies as migration rows
core/174every deploymentCloses view-based write paths into the legacy policy tables
core/175every deploymentMakes the deployment licence promotion report the row it wrote
core/176every deploymentAdds durable typed-authoring storage: published artifacts, activations and signing keys (new tables)
core/177every deploymentGives the six sys_sensitive_* policies an explicit stored action per phase
core/178every deploymentRestricts typed-authoring storage to organization-root policy
core/179every deploymentDeletes the inert v10.x sensitive_data_control seed row, only where it is still unmodified
enterprise/155EnterpriseThe Enterprise-only origin of core/176's tables; superseded by it
enterprise/156EnterpriseWidens the per-organization decision mode record, which v11 no longer reads
core/181every deploymentAdds 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/183every deploymentImports each organization's legacy per-policy overrides, once, into an unpublished draft. The draft decides nothing until the organization publishes it
core/184every deploymentAdmits withdraw as an activation kind and as an audited action, each with its reason
core/185every deploymentGives the organization template's four SQL keyword patterns a word boundary, so they match whole words only
enterprise/157EnterpriseDrops the per-organization typed-authoring flag. Typed authoring is on by default in v11 and needs no flag
enterprise/158EnterpriseAdds the per-organization self-approval setting, for an organization with fewer than two eligible approvers
enterprise/159EnterpriseThe same change as core/184, for the activation ledger enterprise/155 declares
enterprise/160EnterpriseRetires a seed bundle that had been applied outside the migration path, so the rows it wrote are no longer carried
community-saas/088Community SaaSAdds admin_audit_log, the record behind the audited sqli=block override written for every organization it registers
community-saas/182Community SaaSBackfills that same audited sqli=block for organizations registered before this release
industry/banking/402banking deployment modeRetires 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

  1. On v10.2.0 or later, run the shadow search for refused tokens against representative traffic.
  2. Declare a realm for every issuer it names, as in the realm steps.
  3. 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.
  4. Remove the detection action variables, and on CloudFormation their four parameters from your stack overrides.
  5. On Enterprise, import your own legacy policies and plan who activates them; on Community, re-author them.
  6. Take a snapshot, pull the v11.0.0 images and restart the agent and the orchestrator together. The migrations apply on boot.
  7. After the upgrade, confirm the Agent's /health lists the planes it decides on under decision.enforcing_planes, and that a request you expect to be decided by a shipped policy carries engine: anchored and 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 answers 409. Read the legacy policy APIs only to export, and watch for their Deprecation and X-AxonFlow-Removed-In headers.
  • Match on v11 policy identifiers in evaluated_policies.
  • Expect engine, policy_bundle and subject_type in decision responses, and deny rather than needs_approval from POST /api/v1/decide for a policy that requires approval.
  • Handle the identity refusals: a deny or 403 naming the identity reason, and 503 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:

  1. The endpoint, the request and the full response, including engine, policy_bundle, subject_type, reasons and evaluated_policies.
  2. For an identity refusal: the token's iss, aud and org_id claims (not the token itself), and whether the organization has an enabled OIDC SSO configuration.
  3. For a boot refusal: the agent or orchestrator log line naming the variable or licence field.
  4. For a migration failure: the log line naming the migration.

[email protected] with "v11 migration" in the subject so it routes correctly.

See also