Skip to main content

AxonFlow v9.12.1 Release Notes

v9.12.1 is a patch release that fixes a family of read-path defects affecting deployments that run the hardened least-privilege database posture — the request pools connecting as the non-BYPASSRLS axonflow_app_role. On those deployments, a set of database reads was never row-level-security-scoped and silently returned zero rows. Reads under RLS fail quietly (unlike writes, which error), so the symptoms looked like "no data" rather than a failure — the most serious being that tenant-created dynamic policies were not enforced at workflow gates.

Am I affected?​

You are affected only if your database pools actually run as axonflow_app_role — that is, AXONFLOW_DB_USE_APP_ROLE=true with the app-role DSNs provisioned:

  • CloudFormation deployments that completed the two-step app-role provisioning and flipped AppRoleProvisioned=true.
  • Self-hosted deployments whose operator followed the least-privilege provisioning guide (provision-app-role.sh + the app-role environment variables).

You are NOT affected on the defaults: the docker-compose stack, the self-hosted install bundle, and CloudFormation stacks that never flipped AppRoleProvisioned all connect as the table-owning database role, which bypasses row-level security. Their behavior was correct throughout.

If you are affected, upgrade promptly: until you do, custom dynamic policies (for example a "wire transfers over a threshold require approval" rule) are not being enforced at workflow gates, even though creating them returns success.

What was broken on the app-role posture, now fixed​

Tenant dynamic policies were not enforced​

The policy engine's gate-evaluation cache is loaded by a deliberate all-tenants read. On the app-role pool, row-level security filtered that read to zero rows, the cache loaded empty, and every tenant-created dynamic policy stopped being enforced at workflow step gates and the decide plane — a step that should have returned require_approval returned allow. The cache refresh now runs on the platform-admin (BYPASSRLS) pool, and the orchestrator refuses to boot when the app-role gate is on without the platform-admin DSN configured, because silently not enforcing policies is strictly worse than not starting.

Policy create → read-back returned nothing​

A freshly created dynamic policy returned 404 on read-by-id, and the policy list and export came back empty. All policy repository reads are now org-scoped like their writers, system-wide (global) policies stay visible through a dedicated scoped pass, policy exports page correctly (a page-size clamp previously truncated exports to 20 policies), and a policy's version history now verifies parent ownership before returning snapshots.

Portal sessions bounced to the login page​

A valid session cookie answered authenticated: false on the session-check endpoint, so any full page load (hard navigation, refresh, direct link) bounced to /login. The session check now resolves through the same security-definer lookup the auth middleware already used, and logout now genuinely invalidates the server-side session.

Role-gated portal routes denied everyone​

The permission read that every portal RBAC gate funnels through was unscoped, so on an app-role deployment running v9.12.0's role-gated routes, every gated route — policy editing, roles management, SSO configuration — answered 403 for every user, regardless of role. All role and permission reads are now org-scoped. Policy-override lookups (which returned "policy not found") and the agent-side free-tier usage counters are fixed the same way.

Executions were invisible and their status froze​

The executions API and the portal Executions view returned zero rows for tenants with live workflows, and because the execution tracker re-reads a row before updating it, execution status updates silently failed too. Discovery reads (lookup by id) now run on the platform-admin pool with the caller's tenant verified on every result; list and count reads are tenant-scoped; and the execution-history retention job, whose tenant enumeration read zero rows and never purged, now runs on the admin pool.

Migration​

One small migration ships in this release: core/153 backfills the org key onto the migration-seeded global system policy rows so they are visible to scoped reads. It is fast, idempotent, self-testing, and has a down migration. No data is removed and no behavior changes for unaffected deployments.

Also in this release​

  • The /health recommended openclaw plugin version advances to 2.8.4 (the published release carrying the self-hosted endpoint fix and audit hardening). No minimum-version floors change.

Known limitations carried from v9.12.0​

The v9.12.0 known limitation regarding owner backfills on managed-PostgreSQL deployments where the migration role is subject to row-level security remains open in this release; the in-product break-glass owner endpoint is the documented recovery path.

Upgrading​

Standard patch upgrade: pull the v9.12.1 images and restart; migration core/153 applies automatically on boot. App-role deployments: ensure AXONFLOW_DB_PLATFORM_ADMIN_URL is set on the orchestrator — v9.12.1 makes it a boot requirement when the app-role gate is enabled (previously its absence silently degraded policy enforcement). After upgrading, verify with a policy round-trip: create a dynamic policy, confirm it appears in the list, and confirm an over-threshold gate returns require_approval.