Docs

AISIX 0.9.0

This release makes the gateway substantially faster and makes agent traffic legible.

Release date: August 13, 2026

This release makes the gateway substantially faster and makes agent traffic legible. The proxy now serves from thread-per-core workers, roughly doubling throughput and halving p99 latency. Caching learned to match requests that mean the same thing rather than only those spelled the same way. A2A calls now record which task they advanced and what they cost, and a verified JWT can stand in for a caller API key, so an identity provider's claims decide which caller a request runs as.

It also completes the Admin API deprecation announced in 0.4.0: the gateway's own Admin API is now read-only, and resources are managed declaratively or through AISIX Cloud.

Behavior Changes

  • The gateway's Admin API no longer writes resources. The admin listener keeps its whole read surface — lists and gets for every resource kind, model status, health, the OpenAPI reference, and the playground — but POST, PUT, and DELETE on /admin/v1/<kind> now return 405 with Allow: GET, and both spellings of the API key rotate route return 404. This completes the deprecation announced in 0.4.0. Manage resources through a resources file (validate with aisix validate --resources <file>, reload with SIGHUP) or by writing to etcd; gateways connected to AISIX Cloud never exposed this listener and are unaffected. To rotate a caller key declaratively, write the same resource id with a new key_hash — the old secret stops authenticating as soon as the write propagates. The published Admin API reference no longer documents write operations. See Resources File.
  • Cached responses are isolated per caller API key by default. A cache policy's new scope field defaults to api_key, so one caller's answers are never served to another. Deployments that relied on environment-wide sharing must set scope: env explicitly. Either way the cache key changes shape, so expect a one-time full miss after upgrading. See Caching.
  • Time to first token is now measured at the first streamed frame of any kind. It previously waited for a frame carrying generated output, so a model that thinks silently before answering recorded the end of its thinking phase — a figure that could exceed the request's own reported latency and could not be compared with what a fronting proxy measures. Time spent thinking remains visible in upstream latency. Dashboards and alerts calibrated against the old figure will shift. See Metrics and Logs.
  • A request id supplied by the caller is now adopted as the gateway's own. The gateway accepts x-aisix-request-id by default: the value is echoed back, recorded in the access log and usage events, and forwarded upstream. Ids must be 1–256 visible-ASCII characters; anything else is ignored and the gateway generates its own id, so a malformed header never fails a request. Add x-request-id to proxy.request_id.accept_headers to honor that convention too, or set the list to empty to restore the previous behavior. Because the id is now caller-controlled it is neither unique nor trusted, and it is never used as a metric label. See Metrics and Logs.
  • Configuration a model kind never reads is now rejected instead of stored and ignored. retries, auto_prompt_caching, and cost on a model group, the generic call settings on an ensemble, and auto_prompt_caching and cost on a semantic router are refused at write time with 400. A resources file carrying one fails to load and names the offending entry. A model already stored with such a setting keeps working: the gateway drops the field and reports it as partially compatible rather than taking the model out of service. In the other direction, semantic routers and embedding models now accept timeout, stream_timeout, and retries, which they previously refused.
  • Several configurations that were silently accepted are now refused. Creating a model with a configuration block belonging to a different kind, referencing a wildcard alias as an ensemble member or judge, as a semantic router target or default, or as a cache policy's target, renaming a referenced model into a wildcard, and pointing a cache policy at a model that does not exist all return 400. Each was previously accepted and then quietly ignored.
  • Traffic through a wildcard model is reported under the wildcard's own name. Metrics, rate-limit buckets, and health tracking now key on the configured wildcard row rather than the alias each caller happened to send, so one model is one identity instead of a new series per spelling. A dashboard filtered on a caller-minted alias will show its series end at this release.
  • Upgrade note. The control plane widens the request_id column on its usage table from uuid to text so a caller's own id can be stored. On a large table this rewrites it — about six minutes per 10 million rows — and the control plane does not serve traffic until it finishes. The Helm chart's startup probe budget has been raised to 30 minutes to accommodate this; if you run your own manifests, raise yours before upgrading.

New Features

  • The proxy serves from thread-per-core workers. Each worker has its own runtime, listener, and upstream connection pool, so a request is accepted, dispatched, and answered on a single thread instead of being handed between threads twice. On four cores this raises throughput by 54–88% depending on concurrency and roughly halves p99 latency, cutting system calls per request from 11.9 to 5.0. It is on by default on Linux. Two settings apply at startup: proxy.thread_per_core and proxy.workers, which defaults to the parallelism available to the process and follows a cgroup CPU limit. Below roughly four client connections per worker the kernel spreads connections unevenly and this mode is slower than the shared runtime; set proxy.thread_per_core: false for very low concurrency.
  • Semantic caching. A cache policy can now match on meaning: a request that misses exact matching is embedded and served from the nearest stored entry at or above a cosine similarity threshold you set. Only fully textual requests take that path — anything carrying images, audio, or tool calls always matches exactly. Entries can live in each gateway's memory or, with backend: redis, be shared across replicas using Redis vector search. Sharing requires Redis 8 or later, or the search module; the gateway checks at startup and, when it is missing, keeps serving exact matches and says so in the log rather than failing traffic. A policy can also be emptied without deleting it. See Semantic Caching.
  • JWT claim mappings. A new resource resolves verified OIDC claims to an existing caller API key, so an identity provider can decide which caller a request runs as without provisioning a key per user. Rules are evaluated in priority order and the first whose claim conditions all hold selects the key; the request then inherits that key's model and tool access, rate limits, and budget unchanged. Conditions support exact string matches and array membership over nested claim paths. A token that matches no rule is denied. Usage events record the subject, the provider, and the mapping that matched — note that the subject is an end-user identifier and reaches any configured observability exporter. See Claim Mappings.
  • A2A calls are now observable at the protocol level. Every call records its operation, task id, context id, and final task state, with both wire vocabularies normalized so one figure is not split in two. Streamed calls additionally record time to first event, how many events arrived, and whether the caller was still listening at the end, so a caller that hangs up mid-task is no longer counted as a success. Because the protocol carries no usage block, the gateway estimates tokens from the message text that passed through and marks them as estimated; cost is left at zero, since what an agent charges is not something the gateway can know. Metrics are sliced by agent and operation. See Agent Gateway.
  • The provider's own response id is now recorded. It appears in the access log and in a dedicated log line emitted once per upstream attempt, which covers streamed responses and retried or failed-over attempts that a single access-log line structurally cannot. The Dashboard shows it on the log detail panel, so a provider-side problem can be traced to the exact upstream call without joining records by hand.
  • Rate-limit policies accept a day window, counted against the UTC day. See Rate Limit Policies.
  • The Dashboard's request log is split by gateway surface. LLM, MCP, and A2A traffic each get their own tab with the filters that can actually narrow it, rather than one feed with filters that apply to only part of it.
  • The Dashboard's dropdowns are searchable. Every selector that can grow long — models, provider keys, API keys — now filters as you type instead of requiring a scroll through the full list.
  • The Dashboard reports gateway configuration compatibility. When a setting you save is not understood by every gateway in the environment, the Dashboard says so at save time and on the data-planes view, naming the field and the version that introduced it, instead of leaving the setting silently inert on older gateways.

Improvements

  • Beyond thread-per-core, the per-request path was reworked throughout: a jemalloc allocator and link-time optimization, fast paths that skip machinery a zero-configuration deployment does not need, a per-worker metrics cache, TCP_NODELAY on downstream connections, cached upstream endpoint URLs, and a single configuration snapshot load per request.
  • A rate-limit condition on a model now matches the entry the caller addressed as well as the one the request was dispatched to, so a condition naming a model group applies to every request addressed to that group. Previously only the dispatched target was compared, and such a condition could never match.
  • A semantic router now honors its members' own access rules when it selects a route, falling through to another target instead of dispatching to a member the caller is not permitted to use.
  • Per-model rate limits now apply to every model kind, and the Dashboard offers the rate-limit form on every one.
  • A cache policy's target is now validated when the policy is created, and renaming a model that a policy references updates the reference.

Fixes

  • An invitation that has expired no longer keeps its email address reserved, so the same person can be invited again. Pending invitations still block a duplicate.
  • Creating a resource with a boolean field explicitly set to false now stores false. A column default was substituting true for the zero value, so an OIDC provider created as disabled was stored as enabled and the API reported it as enabled.
  • Post-authentication refusals on the realtime endpoint are now attributed to the caller that was resolved, instead of being recorded without an identity.
  • Gateways that have stopped sending heartbeats no longer count toward the Dashboard's configuration-compatibility banner, so a decommissioned instance cannot make a healthy fleet look partially incompatible.
  • A gateway built from source without version stamping reports a placeholder version; that placeholder is now treated as unknown rather than as a real old version, so such a fleet no longer warns forever about features it may well support.