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.