Skip to main content

Decision Mode

AxonFlow's existing integration modes -- Gateway Mode, Proxy Mode, and Workflow Control Plane -- are described in Choosing an Integration Mode. Decision Mode is a new integration option on a different axis: instead of your application code calling AxonFlow, your existing gateway infrastructure calls AxonFlow.

Available in v8.1.0+

Decision Mode is available starting with platform v8.1.0. The Decision API (POST /api/v1/decide) is live at all tiers (Community through Enterprise). PEP adapters and Envoy ext_authz integration are planned for a future release.

What Decision Mode is

Many platform teams already run their own gateway infrastructure, often several layers of it: an agent gateway, a connector or MCP gateway, an LLM gateway. For those teams, neither rewriting application code nor routing traffic through a new proxy is attractive.

Decision Mode fits here. AxonFlow runs as a standalone policy decision service. Your existing gateways each make one inline call to AxonFlow per request, receive a verdict (allow, deny, or require approval), and enforce it. AxonFlow is consulted; it is never on the traffic path.

This is the well-established PDP/PEP (Policy Decision Point / Policy Enforcement Point) separation used by policy engines across the industry. It has three properties platform teams care about:

  • Where traffic is required to pass through governed gateways, enforcement does not depend on developer discipline. The gateway is infrastructure that requests pass through by construction. There is no per-application SDK call to omit.
  • One policy brain, every layer. Because every gateway calls the same decision service, the policy hierarchy is resolved centrally and enforced identically at each stage.
  • One end-to-end trace. Because every decision goes through the same service, decisions made at different gateway layers correlate into a single trace, which feeds audit logging.

The Decision API builds on the same policy engine as Gateway Mode's POST /api/policy/pre-check -- same engine, different caller. Decision Mode is additive and framework-neutral. Your existing gateways, routers, and providers stay exactly as they are; AxonFlow is added beside them, not inserted into the path.

Quick start

The Decision API is a single endpoint on the Agent: POST /api/v1/decide (port 8080).

Allow verdict

A clean LLM-stage request passes the policy engine and returns verdict: "allow":

curl -s -X POST http://localhost:8080/api/v1/decide \
-H "Content-Type: application/json" \
-d '{
"stage": "llm",
"caller_identity": {
"gateway_id": "llm-gateway-01",
"tenant_id": "acme-prod"
},
"target": {
"type": "llm",
"model": "gpt-4o",
"provider": "openai"
},
"query": "What is the customer order status?"
}' | jq .
{
"verdict": "allow",
"decision_id": "f81d4fae-7dec-11d0-a765-00a0c91e6bf6",
"trace_id": "0af7651916cd43dd8448eb211c80319c",
"stage": "llm",
"reasons": [],
"obligations": [],
"evaluated_policies": [],
"expires_at": "2026-05-23T10:35:00Z"
}

Deny verdict (SQL injection)

A query containing a SQL injection pattern triggers the built-in SQLi policy and returns verdict: "deny". On deny, the first entry in evaluated_policies is the blocking policy:

curl -s -X POST http://localhost:8080/api/v1/decide \
-H "Content-Type: application/json" \
-d '{
"stage": "tool",
"caller_identity": {
"gateway_id": "mcp-gateway-01",
"tenant_id": "acme-prod"
},
"target": {
"type": "tool",
"tool": "postgres.query"
},
"query": "SELECT * FROM users WHERE id=1 UNION SELECT password FROM credentials"
}' | jq .
{
"verdict": "deny",
"decision_id": "a73e5b1c-2b48-4f2e-a3c4-2e8a3b9f8d1e",
"trace_id": "7b3c8d2e1a4f5069b8c7d6e5f4a3b2c1",
"stage": "tool",
"reasons": ["SQL injection pattern matched"],
"obligations": [],
"evaluated_policies": ["sys_sqli_union"],
"expires_at": "2026-05-23T10:35:00Z"
}

Trace correlation across gateway layers

Pass a W3C traceparent header and the response reuses the same trace_id. This lets multi-layer gateways (agent, MCP, LLM) stitch their decisions into one end-to-end trace:

curl -s -X POST http://localhost:8080/api/v1/decide \
-H "Content-Type: application/json" \
-H "traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01" \
-d '{
"stage": "agent",
"caller_identity": {
"gateway_id": "agent-gateway-01",
"tenant_id": "acme-prod"
},
"target": {
"type": "agent"
},
"query": "Investigate the suspicious payment and draft a summary"
}' | jq .trace_id
"4bf92f3577b34da6a3ce929d0e0e4736"

The trace_id in the response matches the trace-id portion of the inbound traceparent. When no traceparent is provided, AxonFlow mints a fresh 32-hex trace ID.

Auth: in Community mode, no auth header is required. In Enterprise mode, use the same Authorization: Basic <base64> header as Gateway Mode.

Reference PEP adapter

AxonFlow ships a reference adapter that wraps any HTTP-based LLM endpoint with Decision Mode enforcement. The adapter is a Go HTTP middleware (~200 lines) that intercepts every request, calls POST /api/v1/decide, and enforces the verdict before forwarding:

client → adapter (:8888) → your LLM gateway

AxonFlow agent (:8080)
POST /api/v1/decide

The adapter:

  • Extracts model and user message from the OpenAI-shaped request body.
  • Calls the Decision API with stage: "llm" and the configured gateway identity.
  • On allow: forwards the request to the downstream LLM, propagates traceparent for end-to-end tracing.
  • On deny: returns a structured JSON error with decision_id, trace_id, and reasons (not a bare 403).
  • On Decision API failure: applies the configured fail-open or fail-closed posture.

The adapter is configurable via environment variables (AXONFLOW_ENDPOINT, AXONFLOW_GATEWAY_ID, AXONFLOW_FAIL_OPEN) and can also be used as a Go library:

import adapter "github.com/getaxonflow/axonflow/examples/integrations/decision-mode-adapter"

handler := adapter.Middleware(adapter.Config{
AxonFlowEndpoint: "http://axonflow:8080",
GatewayID: "my-llm-gateway",
FailOpen: false,
}, yourDownstreamHandler)

A Docker Compose PoC harness is included that runs the full round-trip (agent + adapter + mock LLM). See the adapter source and README for setup instructions.

Request and response

Endpoint: POST /api/v1/decide on the Agent (port 8080)

For the full OpenAPI schema, see Agent API Endpoints.

Request body

FieldTypeRequiredDescription
stagestringYesWhich gateway layer is calling: llm, tool, or agent.
caller_identityobjectNoGateway-asserted identity. In Enterprise mode, the auth-derived identity is authoritative; body values must match or the request is rejected with 403.
caller_identity.gateway_idstringNoIdentifier for the calling gateway instance (for audit).
caller_identity.org_idstringNoOrganization scope.
caller_identity.tenant_idstringNoTenant scope.
targetobjectNoWhat the gateway is about to call.
target.typestringNollm, tool, or agent.
target.modelstringNoModel name (when type=llm).
target.providerstringNoProvider name (when type=llm).
target.toolstringNoTool name (when type=tool).
querystringYesThe content to evaluate against the policy engine.
user_tokenstringNoEnd-user identity token, when the gateway has one to forward.
contextobjectNoArbitrary key-value metadata for future policy use.

Response body (HTTP 200)

FieldTypeDescription
verdictstringallow, deny, or needs_approval.
decision_idstringUnique identifier for this decision (UUID).
trace_idstringW3C-compatible 32-hex trace identifier. Reuses inbound traceparent when present.
stagestringEchoes the request stage.
reasonsstring[]Human-readable reasons (populated on deny).
obligationsobject[]PEP-side requirements attached to an allow verdict (e.g. {"type": "redact_pii", "detail": "..."}).
evaluated_policiesstring[]Policies that matched. On deny, the first entry is the blocking policy.
expires_atstringISO 8601 timestamp. The verdict is valid until this time (default: 5 minutes). PEP adapters may cache the result until expiry.

Error responses

StatusMeaning
400Invalid request body, missing stage, or missing query.
403caller_identity does not match the authenticated identity (Enterprise mode).
503Circuit breaker is active. The body carries verdict: "deny" as a fail-closed default. A Retry-After header is included when available. PEP adapters should apply their configured fail-open or fail-closed posture.

Trace correlation

Decision Mode is designed for multi-layer gateway architectures where a single user request crosses several enforcement points. Trace correlation ties those decisions together.

Pass a traceparent header on each call:

  1. The agent gateway calls POST /api/v1/decide with traceparent: 00-<trace-id>-<span-id>-01.
  2. AxonFlow returns the same trace_id in the response.
  3. The agent gateway propagates the traceparent downstream.
  4. The MCP gateway and LLM gateway each call POST /api/v1/decide with the same traceparent.
  5. All three decisions share one trace_id in the audit trail.

When AXONFLOW_OTEL_ENDPOINT is configured, each decision emits an OpenTelemetry span on the axonflow.agent.decision tracer for end-to-end observability. See examples/integrations/otel-tracing/ in the AxonFlow repository for a local setup with Jaeger.

How it differs from existing modes

Gateway Mode, Proxy Mode, and WCP describe who owns the LLM call and how the application talks to AxonFlow. Decision Mode describes who calls AxonFlow's policy engine:

  • In Gateway Mode, your application code calls AxonFlow's pre-check API.
  • In Decision Mode, your infrastructure gateway calls AxonFlow's decision API.

These are different axes. They can coexist: a large organization can use Gateway Mode for services where deep, context-rich checks matter, and Decision Mode at its gateway layers for enterprise-wide enforcement -- all against the same policy hierarchy.

Decision Mode across multiple gateways

Decision Mode architecture: a Policy Decision Point beside your existing gateway, applied across three gateway layers -- agent, MCP, and LLM -- calling one decision service.

The diagram shows Decision Mode applied to a three-layer gateway architecture. A request crosses the agent gateway, the connector (MCP) gateway, and the LLM gateway in turn. Each gateway calls the same AxonFlow decision service, which resolves the policy hierarchy and returns a verdict plus a shared trace identifier. The result is consistent enforcement at every stage and a single, correlated audit trail, without changing the application or the gateways' routing.

When to use Decision Mode

Consider Decision Mode when:

  • You already operate your own gateway layers (agent gateway, MCP gateway, LLM gateway) and want centralized policy enforcement without per-application SDK integration.
  • You have many engineering teams behind those gateways, so per-application integration does not scale and is not auditable.
  • You answer to a security, risk, or compliance function that wants enforcement to be structural, not discretionary.

If you do not already run your own gateway infrastructure, Gateway Mode or Proxy Mode will serve you better and faster.