Skip to content

v9.2.0

Choose a tag to compare

@saurabhjain1592 saurabhjain1592 released this 23 Jun 21:36
· 42 commits to main since this release
0434804

[9.2.0] - 2026-06-23 (read-only MCP posture, tamper-evident audit signing, turnkey SIEM export, compliance-category enforcement)

Additive minor. v9.2.0 hardens governance and audit assurance: a one-config read-only MCP posture, a connector-level database backstop, per-record cryptographic signing of the decision chain (now capturing live traffic) with read-only verification endpoints, a turnkey central-store / SIEM audit exporter, and automatic cross-border transfer_basis stamping on the canonical audit row. Two behavior changes to note (both in Fixed): the seeded RBI / SEBI / MAS-FEAT / EU-AI-Act compliance policies now actually fire on /decide and the gateway (they were silently excluded by a category-spelling mismatch), and a tool/connector response carrying an indirect prompt-injection pattern is now sanitized by default on the response plane (the statement carrying the injection is removed). Everything else is additive and off by default.

Added

  • Read-only MCP enforcement posture: one config flag blocks every write-path MCP call. (Community) Setting MCP_READ_ONLY=true (env, default off) blocks write-intent MCP calls across all connector-execution planes: the check_policy advisory tool, the check-input PEP gate, tools/execute, resources/query, and the gateway pre-check. A method-name verb classifier (classifyMCPCall) handles tool-call intent, and a SQL statement classifier (statementIsWritePath) handles the raw-statement query plane: it masks string literals and comments, rejects stacked statements and dollar-quoted bodies, and catches SELECT ... INTO and EXPLAIN ANALYZE <dml>, failing closed on anything it cannot prove read-only.
  • Connector-level read-only database transaction backstop. (Community) A new per-call base.Query.ReadOnly field lets a connector run a read inside a database-enforced read-only transaction. The PostgreSQL connector honors it by opening BEGIN READ ONLY, so the database itself rejects (SQLSTATE 25006) any write that a statement classifier might miss; it is the durable backstop under the read-only MCP posture above. Byte-identical behavior when the flag is off (no extra round-trip, no transaction). MySQL and Snowflake can adopt the same flag in a follow-up; Cassandra has no read-only-transaction primitive and stays verb-path only.
  • Tamper-evident audit signing + read-only verification endpoints. (Community) Each decision-chain record is now signed with a per-record Ed25519 signature and linked by a prev_hash hash chain with a monotonic chain_seq, appended race-free under an advisory lock (migration core/125). A single record can be verified standalone offline: the response republishes the digest pre-image, record digest, prev_hash, and chain hash so an auditor can recompute and re-verify trusting neither the endpoint nor its digest. Three new read-only endpoints (documented in docs/api/agent-api.yaml): GET /api/v1/audit/chains/{id}/verify (linkage + every signature, with an authorship_proven flag for the strong non-repudiation claim), GET /api/v1/audit/records/{id}/verify (one record standalone), and GET /api/v1/audit/signing-key (publish the current public key). Signing key and retired-key rotation are configured via AXONFLOW_AUDIT_SIGNING_KEY (+ optional AXONFLOW_AUDIT_SIGNING_KEY_ID) and AXONFLOW_AUDIT_VERIFY_KEYS; when no key is set, records are hash-chained but reported honestly as unsigned.
  • Decision-chain signing now captures live traffic. (Community) The production decision-chain tracker is now a writing instance: real /decide, OpenAI-compatible, gateway pre-check, and early-deny decisions are enqueued and signed + hash-chained off the request hot path by async workers (the same instance still backs the verify endpoints, so a record signed here verifies against the keys loaded here). Previously the tracker was instantiated verify-only, so no production decision was ever signed. A /decide redaction signs as approved (an obligation on an allow verdict per the decide → fulfill contract); only the gateway plane records a redacted → modified verdict.
  • Turnkey central-store / SIEM audit exporter. (Community) A new exporter ships each decision to an external store off the decision hot path (non-blocking queue with timeout and circuit breaker), so a denied or redacted decision can land in a customer's SIEM or object store without touching request latency. Enabled via AXONFLOW_AUDIT_SINK=s3 (default empty = disabled; decision path byte-for-byte unchanged when off), with AXONFLOW_AUDIT_S3_BUCKET / AXONFLOW_AUDIT_S3_PREFIX / AXONFLOW_AUDIT_S3_REGION / AXONFLOW_AUDIT_S3_ENDPOINT / AXONFLOW_AUDIT_S3_PATH_STYLE. Shipped, dropped, failed, and skipped records are metered on axonflow_central_store_records_total.
  • Automatic cross-border transfer_basis stamping on the canonical audit row. (Enterprise) The orchestrator now auto-stamps the UU PDP Pasal 56 cross-border transfer basis (and derived data-residency) onto the canonical audit_logs decision row (columns added in migration core/126), resolved per-request, then per-org via AXONFLOW_ORG_TRANSFER_BASIS, then a global AXONFLOW_DEFAULT_TRANSFER_BASIS, so Pasal 56(b) attestation is turnkey rather than hand-stamped.

Fixed

  • Indirect prompt-injection is now governed on the response / tool-output plane. (Community, behavior change) The four indirect prompt-injection patterns (instruction-override, role-reassignment, system-prompt exfiltration, and template/bracket markers; migration 116) were seeded phase='request', so evaluateOutputPolicies never evaluated them: a malicious instruction returned in a connector free-text field (for example a back-office CRM note) re-entered the model's context ungoverned, even though the response plane already covered SQL-injection, PII, and sensitive-data. Migration core/128 flips those four sys_dangerous_injection_* policies to phase='both' and sets their previously-NULL action_response (the request-plane action stays block), and a new EnabledSecurityDangerousCategories helper folds the security-dangerous category into the response evaluation set (the phase flip alone is inert, because filterByCategories would otherwise drop the category). Only the injection patterns are promoted; the dangerous-command patterns (migration 059: reverse shell, /etc/passwd, eval(), credential-file access) stay request-only by design, since matching a command description against connector output would hard-block benign data. The default response-plane action is redact (sanitize): the sentence/line/clause containing the injection is removed (JSON-aware, preserving valid structure and sibling fields), so no injectable instruction reaches the model, while surrounding legitimate data passes through, overridable per-org to warn or block via the detection-posture override; the input plane still blocks. The outcome is recorded as a canonical plane=mcp response audit row. Behavior change: a tool/connector response carrying one of these injection patterns now has the injection-bearing statement removed by default.

  • Seeded compliance policies now enforce on /decide and the gateway. (Community, behavior change) The seeded RBI, SEBI, MAS-FEAT, and EU-AI-Act compliance policies stored drifted category spellings (rbi_compliance, sebi_compliance, mas_feat_compliance, eu_ai_act_compliance) that did not match the canonical category constants (compliance-rbi, compliance-sebi, compliance-masfeat, compliance-euaiact). Because the decision filter matches category exactly, those rows were silently excluded and the policies never fired. The industry seeds are canonicalized at source for fresh deployments, and a forward-fix migration (core/127, plus core/014 for EU) realigns existing deployments. Behavior change: deployments seeded with these compliance packs will start enforcing them on /decide and the gateway.

  • Matched policies surface the policy name, not an opaque UUID. (Community) The orchestrator's active-policy cache is keyed by policy_id to avoid cross-tenant name collisions, but ListActivePolicies copied that key into the policy's Name, so matched_policies reported a UUID. It now reads the stored policy name.

  • The OJK cross-border export reads the canonical audit row. (Enterprise) The Indonesian OJK cross-border export is repointed to read the canonical audit_logs decision row carrying the stamped transfer_basis, consolidating it onto the same source as the rest of the compliance exporters.

Security

  • golang.org/x/net and golang.org/x/crypto bumped to clear Trivy HIGH advisories (x/net 0.52 → 0.56, x/crypto 0.49 → 0.53) across the flagged modules, restoring a green repo-wide vulnerability scan. Dependency-only; no code or behavior change.

Full release notes: https://docs.getaxonflow.com/docs/releases/v9-2-0
Migrations are additive and apply automatically on startup.