Skip to main content

Changelog

Full release notes are also on GitHub Releases. v1.5.2 is the latest tag.

v1.5.2 โ€” 2026-09-03 โ€” Routing depthโ€‹

Every routed surface now ranks through one strategy implementation, and the routing layer gains the operator controls a production gateway needs: per-attempt timeouts, a 429 cooldown, a typed context-length failover class, sticky sessions, rule target chains, bounded conditional predicates, attribution headers on every response, and a host-supplied price catalog. Everything ships in the open-source gateway; no configuration key is removed, and the Go API only grows. See Routing for the updated contract.

What's new in v1.5.2โ€‹

  • Attribution headers on every routed surface โ€” X-Gateway-Provider, X-Gateway-Target, X-Gateway-Model and X-Gateway-Attempts on chat (before a stream's first chunk), legacy completions, embeddings, images, rerank, moderations, transcriptions, translations and speech; a failed request names the last target attempted. ferro.routing.attempt is emitted on the request span. See Attribution headers.
  • targets[].timeout bounds one physical attempt inside request_timeout, so a hung primary no longer consumes the whole request budget before a pool mode moves on. Streams are bounded only until the provider answers.
  • 429 cooldown โ€” a target that answers 429 is parked for its Retry-After (five seconds when absent, a minute at most) so the next request does not pay another 429 on it. Process-local; the breaker is untouched.
  • Typed context-length failover โ€” a provider's own statement that the prompt exceeded its context window (the OpenAI-compatible, Anthropic and Gemini envelopes) fails over to a sibling, whose model may have a larger window. Every other 4xx still stops.
  • strategy.sticky: { on: user, ttl: "1h" } under loadbalance and ab-test pins a user to one target or variant with a stateless hash. See Load balance.
  • target_keys: [a, b] on conditions[] and content_conditions[] names an ordered chain for a rule, walked on failover-safe failures and never left. See Conditional.
  • Conditional predicates user, stream, has_tools, and metadata + field reading the single X-Gateway-Metadata request header.
  • strategy.failover_on_status_codes adds upstream statuses to the failover-safe set; 400, 401, 403, 404 and 422 cannot be listed.
  • aigateway.WithCatalog(models.Catalog) hands an embedding host's own price catalog to the gateway in place of the embedded or remote one; aigateway.WithRoutingAttribution reads the attribution back.
  • Embedded dashboard โ€” the strategy panel shows model_map, rule chains and the new predicates.

Behaviour changes in v1.5.2โ€‹

  • A rule that names one target is exact. Under conditional and content-based, an open circuit on the matched target used to borrow a healthy sibling; it now answers 503, and a rule that wants a stand-in lists one in target_keys. On non-chat surfaces content-based routes to the first target that can serve the request, alone.
  • One ranker for every surface. Embeddings, images, rerank, moderation, transcription and speech previously ranked through a second implementation that differed from chat in its random source, unseen-latency order, cost input and unpriced placement. The same config and health now produce the same candidate order everywhere.
  • least-latency keeps learning. Samples are keyed by target and upstream model, expire after five minutes, and one request in ten leads with a sampled runner-up so the leader cannot lock in. A stream's sample is its time to first chunk rather than its whole drain.
  • cost-optimized prices input plus output โ€” the request's completion ceiling or 256 tokens โ€” at the catalog rate for the model's mode on every surface; equal-cost targets draw by targets[].weight, and a negative weight is refused.
  • Validation โ€” an ab_variants[] entry without a label no longer loads; a single strategy with more than one target logs a warning naming the unused targets.

v1.5.1 โ€” 2026-09-01 โ€” Routing reliability correctionโ€‹

A patch that makes the routing strategies truthful about failure, gives one client-facing model name a different upstream ID per provider, and makes each physical attempt observable. Three behaviours an operator may notice change; no configuration keys are removed. See Routing for the updated contract.

What's new in v1.5.1โ€‹

  • targets[].model_map โ€” several providers can serve one visible model name while each receives its own upstream model ID (smart: gpt-4o on OpenAI, smart: claude-sonnet-4-6 on Anthropic). Mapped names participate in routing and /v1/models; pricing uses the mapped ID. See One model name, different upstream IDs.
  • Attempt-level observability โ€” each provider call or local circuit-breaker/concurrency refusal can emit a gateway.routing.attempt event, retries and cross-target failovers included. Attempt events are opt-in per exporter and per custom observability provider; every existing exporter keeps receiving exactly one event per request. See Observability.
  • A/B attribution โ€” attempt and terminal events carry ferro.routing.ab_variant_label, and it stays the variant that was drawn through retries and failover. See A/B test.
  • Keyless strategy end-to-end suite โ€” make test-e2e-strategies exercises every routing mode over the real binary against three scriptable mock upstreams: failover classes, retries and Retry-After, breaker states, weight and variant distributions, model_map on unary and streamed requests, /v1/models and /metrics โ€” with no provider credentials.
  • Install paths recorded โ€” the one-command installer (get.ferrolabs.ai), ferrogw on npm and PyPI, the Homebrew cask and Scoop manifest, and the GoReleaser platform archives landed on main during the v1.4.x line without a changelog entry, as did the README quickstart overhaul; both are now recorded. See Install.

Behaviour changes in v1.5.1โ€‹

  • Pool modes fail over only after a failover-safe failure. fallback, loadbalance, least-latency, cost-optimized and ab-test advance to another target after a transport failure, an attempt that timed out waiting on the target, 408, 429, 5xx, an open circuit, or target saturation. They previously advanced after any failure, so a target answering 400, 401, 404 or 422 was silently covered by a sibling; those responses now reach the client. The request's own cancellation or deadline still stops routing, as does any provider-call failure under single, conditional and content-based.
  • The response model is the routed name โ€” the model the client asked for, after alias resolution โ€” on every surface, streamed chunks included, instead of the identifier the provider reported. Provider calls, pricing and the UpstreamModel of an attempt event still use the mapped upstream model.
  • Ambiguous configurations are rejected at load โ€” duplicate target keys, an empty targets[].virtual_key, duplicate ab_variants[].target_key entries, and duplicated keys in a JSON config no longer load silently.
  • The embedded model catalog is parsed once per process. Every gateway constructed without a reachable remote catalog previously decoded the 3 MB document again (about 90 ms); it now receives its own copy of the parsed catalog in about 3 ms.

Fixed in v1.5.1โ€‹

  • A failure in an after_request plugin is timed and counted as a plugin failure and emits one failed terminal lifecycle event carrying the selected A/B variant, instead of ending without a duration sample or a terminal event.
  • A stream whose upstream had already finished when the client hung up was recorded as a client cancellation about half the time; a completed and billed stream is now always recorded as completed.

v1.5.0 โ€” 2026-08-29 โ€” The gateway is importableโ€‹

A new public run package exposes the ferrogw program to Go code. run.Main() is what cmd/ferrogw now calls; run.Run(ctx, opts...) runs the same server under a caller-owned context and returns startup and listen errors instead of exiting the process, with context cancellation triggering the same graceful shutdown as SIGTERM. A custom binary is a main that blank-imports its plugins and calls run.Main() โ€” the process lane. httpgateway (since v1.4.2) remains the library lane for mounting gateway surfaces behind your own middleware.

The server now binds its listener before it starts observing shutdown, so a cancellation that arrives during startup can no longer leave a listener behind. Existing ferrogw behaviour โ€” commands, flags, exit codes โ€” is unchanged.


v1.4.5 โ€” 2026-08-23 โ€” Security patch: stdio MCP memory boundโ€‹

  • A stdio MCP server can no longer exhaust gateway memory. The stdio transport now applies the same 10 MiB bound as the HTTP transport, measured per JSON-RPC message, so an ordinary conversation of any length is unaffected. A previously working oversized tool result now fails โ€” terminal for that server, not for one call: the transport closes and the registry withdraws the server and its tools. A server with a legitimate reason to return more than 10 MiB should page its results. See MCP.
  • Three dependency advisories cleared โ€” golang.org/x/text v0.39.0, golang.org/x/net v0.56.0, github.com/moby/go-archive v0.3.0. None was reachable from gateway code; govulncheck reports zero vulnerabilities in every category.
  • SECURITY.md names 1.4.x as the supported series.

No breaking changes to configuration or the API.


v1.4.4 โ€” 2026-08-18 โ€” In-flight requests keep their provider priceโ€‹

An alias repointed to a different provider while a request was in flight could price that request against the replacement provider, even though the original served it. Routing now carries the pricing identity captured at provider selection through unary and streaming cost accounting. Attribution is unchanged: responses, metrics, spans and plugin context still name the routing alias.


v1.4.3 โ€” 2026-08-17 โ€” Security patch: Go 1.25.13 and alias pricingโ€‹

  • Go toolchain 1.25.13 โ€” clears six standard-library advisories reachable from gateway code (net/url, html/template, crypto/tls, net/http, encoding/xml, encoding/asn1). No gateway code changes.
  • Dashboard toolchain โ€” nanoid 3.3.18 closes a high-severity advisory in a build-time dependency; nothing shipped in the embedded bundle was affected.
  • Registration aliases are priced correctly. A provider registered under a routing alias (RegisterProviderAs, v1.4.2) was treated as unpriced by cost-optimized ranking and by streaming cost accounting; both now resolve the canonical provider for the catalog lookup. Deployments that register providers under their canonical name are unaffected.

No breaking changes.


v1.4.2 โ€” 2026-08-10 โ€” Registration aliases, an embedding facade, and build provenanceโ€‹

  • Gateway.RegisterProviderAs registers one provider under a distinct routing target, so a deployment can bind several credentials for the same canonical provider. The alias resolves every optional capability through the original provider โ€” streaming, embeddings, images, rerank, moderation, audio, discovery, batch, Responses and pass-through.
  • httpgateway facade โ€” the Files/Batches, Responses and generic pass-through handlers are exposed to embedding applications, which keep their own authentication and tenant middleware while reusing the gateway's provider resolution, credential injection, governance and usage capture.
  • GET /health reports build provenance โ€” version, commit and built alongside provider status (dev / none / unknown for an unstamped local build).
  • The configuration schema โ€” Config and its sub-types, loader and validator โ€” lives in the config package (github.com/ferro-labs/ai-gateway/config) since v1.4.0; for an embedder the migration is a one-line import.

v1.4.1 โ€” 2026-08-07 โ€” Dependency security patchโ€‹

A dependency-security patch for the web toolchain and embedded dashboard โ€” no gateway code changes. Clears all 12 open Dependabot alerts (PR #392).

What's new in v1.4.1โ€‹

  • react-router 8.3.0 โ€” absorbs react-router-dom, whose 7.x line was flagged by scanners. The client routing API is unchanged and browser test suites pass unmodified.
  • React 19.2.8.
  • Seven build/tooling-time packages patched โ€” undici, ip-address, fast-uri, postcss, @hono/node-server, brace-expansion, hono. None of these ship inside the embedded dashboard bundle; npm audit reports zero vulnerabilities.

No behavior changes, no breaking changes.


v1.4.0 โ€” 2026-08-07 โ€” One routing pipeline, endpoint parity, and the embedded dashboardโ€‹

A breaking release (PR #365; merged 2026-08-06, tag and signed release published 2026-08-07). Chat, streaming, embeddings, and image generation had each grown their own copy of retry, circuit breaking, and error classification; this release puts all four on one routing pipeline, so a behavior is either true of every surface or of none. It also widens the native API surface and ships the operations dashboard embedded in the OSS binary โ€” not as a standalone artifact.

What's new in v1.4.0โ€‹

  • One routing pipeline โ€” chat, streaming, embeddings and images now share routeTargets; retry, circuit breaking, concurrency limits, error classification, metrics, and request logging are wired once. See Routing.
  • Embedded dashboard โ€” the operations dashboard is a React SPA built into the binary and served at the gateway's own root, on the same port. One artifact, one origin, no separate container.
  • Native endpoint parity โ€” POST /v1/rerank (Cohere v2 contract), POST /v1/moderations, and POST /v1/audio/{transcriptions,translations,speech} become routed surfaces carrying the full gateway lifecycle (targets, strategy, plugins, circuit breaker, concurrency, metrics, request logging) instead of depending on the generic pass-through.
  • Files and Batches โ€” /v1/files* and /v1/batches* forward to one configured batch_target (501 when unset). Responses API โ€” /v1/responses is now governed and priced, with usage teed from the response body or the terminal SSE event; its stateful sub-routes pin to responses_target.
  • targets[].models โ€” operators can declare models a target serves that neither the catalog nor live discovery see; additive only, wildcards rejected at load.
  • Admin sessions โ€” POST/DELETE /admin/session, session listing, and per-session revocation. The admin key is no longer kept in the browser (24h absolute / 1h idle).
  • New admin surface โ€” GET /admin/audit (durable audit trail), GET /admin/plugins/catalog, GET /admin/logs/stats (p50/p95/p99 latency + TTFT, token split, spend per provider/model). Request-log rows gain duration_ms, ttft_ms, cost_usd, api_key_id.
  • Agentic MCP loops now bill and guard every turn โ€” a budget can stop an overspending loop mid-request, and a loop that fails part-way is still billed for what it spent. See MCP.
  • More surfaces per provider โ€” image generation on gemini, deepinfra, and together; embeddings on azure-foundry; 17 streaming providers get a 120s response-header timeout.
  • Dashboard Tracing page and a fullstack observability demo compose stack under deploy/; license notices ship in every release artifact; container images publish as one multi-platform manifest with an SPDX SBOM attestation.

Breaking changes in v1.4.0โ€‹

  • targets[].retry is now honored under every routing mode, not only fallback โ€” set attempts: 1 on a target to keep the old single-attempt behavior. This is the change most deployments will notice.
  • targets is an allowlist on every surface โ€” a provider that's registered but not listed under targets serves nothing; an unowned model returns 404 model_not_found. Pool modes (fallback, loadbalance, least-latency, cost-optimized, ab-test) advance past a failed target; named modes (single, conditional, content-based) still stop and report. Every mode now skips an open circuit โ€” all targets open returns 503, not 404.
  • Exhausted budget returns 402 insufficient_quota, not 429 โ€” the OpenAI SDKs don't retry 402; 429 stays reserved for rate limiting and concurrency backpressure.
  • Ollama's models variable is renamed FERRO_OLLAMA_MODELS โ€” the old OLLAMA_MODELS name warns this release and is removed next.
  • ADMIN_BOOTSTRAP_KEY and its companions are removed โ€” set MASTER_KEY instead; ferrogw init generates one.
  • /metrics requires the read_only or admin scope; /debug/* requires admin โ€” a valid bearer credential is no longer enough on its own.
  • <PROVIDER>_BASE_URL is the API root used verbatim on every provider (eight providers changed meaning) โ€” a value with a path must include the version segment.
  • Plugin API: Context.Skip is removed โ€” SkipProvider replaces it and skips only the provider call. Every remaining plugin, including after_request, still runs, so a cache hit can no longer disable a guardrail behind it. Pre-1.4.0 third-party plugins do not compile.
  • Container images publish as one multi-platform manifest โ€” the -amd64/-arm64 tags are gone.
  • /dashboard , /dashboard/*, and /logo.png are removed โ€” the embedded SPA at the site root serves unmatched paths (no 410).

See Plugins, Routing, and Provider configuration for the updated contracts.


v1.3.2 โ€” 2026-07-21 โ€” Disclosure and durability patchโ€‹

Folds in disclosure and durability fixes originally scoped as a separate patch โ€” zero breaking changes.

What's new in v1.3.2โ€‹

  • Provider error text is filtered before it leaves the process โ€” a credential echoed by an upstream can no longer reach clients, logs, SSE frames, or exporters. Best-effort pattern matching reduces exposure; it does not eliminate it.
  • A crashed MCP stdio subprocess is detected and its tools withdrawn from the model (stdio only โ€” a dead HTTP MCP server after handshake is not detected).
  • Admin config changes and their audit/history entries are applied and recorded as one serialized operation, so a rollback can no longer target the wrong version.
  • mcp_servers[].required (opt-in, default false) gates /readyz on a server's availability; the /readyz body gains per-server MCP state (the failure reason is deliberately omitted from the unauthenticated endpoint).
  • New MCP observability โ€” gateway_mcp_server_up gauge, gateway_mcp_server_init_failures_total counter, and tracing spans for MCP startup.
  • MCP tools that return an error result are now recorded as errors (metrics/audit/spans), not successes.

v1.3.1 โ€” 2026-07-21 โ€” Seven post-1.3.0 fixesโ€‹

Seven defects from v1.3.0 fixed, zero breaking changes โ€” much of it configuration that was quietly not being applied.

What's new in v1.3.1โ€‹

  • Streaming now honors retry and fallback (start-of-stream only โ€” a begun stream is never replayed); retry backoff is unified across every surface (previously zero outside chat).
  • Embeddings and image generation now follow the configured routing strategy โ€” previously first-capable-provider with no fallback, retry, or timeout โ€” and now emit metrics, cost, tracing spans, and lifecycle events.
  • /v1/completions accepts its documented request shapes (array/token-id prompt, string stop).
  • Circuit breaker made panic-safe โ€” a panicking provider call no longer disables its target until restart; provider lookup order is now stable.
  • stream_options.include_usage is honored; API-key updates no longer apply changes they rejected.
  • Model catalog is now actually downloaded โ€” fetch budget raised from 1s to 10s โ€” plus FERRO_MODEL_CATALOG_TIMEOUT (0 skips the fetch for air-gapped deployments).

No breaking API changes, but two operationally visible effects: which provider serves embeddings/images can shift under non-single strategies (worth a cost/latency/data-residency review), and reported costs move to pricing from the live model catalog rather than the embedded snapshot.

v1.3.0 โ€” 2026-07-20 โ€” MCP stdio transportโ€‹

Adds a second MCP transport and closes an environment-leak risk in the first one.

What's new in v1.3.0โ€‹

  • MCP stdio transport โ€” mcp_servers entries may set command (+ args) instead of url, running any npx/uvx/binary MCP server as a gateway-launched subprocess. Contributed by @gr3enarr0w (PR #121). See MCP.
  • Subprocess environment isolation โ€” an MCP subprocess inherits no gateway environment; only PATH/HOME/LANG/TMPDIR plus that server's own env block reach it.
  • ${VAR} references resolve in a stdio server's env at client construction; GET /admin/config redacts env alongside headers.
  • stderr is drained into the gateway log at debug level, so a startup failure is readable instead of an opaque timeout.
  • Fixed: a misconfigured MCP server no longer disables streaming gateway-wide โ€” activation now keys off discovered tools, not registered servers.
  • Fixed: caller-supplied tool calls are no longer intercepted โ€” client-side OpenAI function calling works with MCP enabled.
  • Fixed: tool calls are capped per turn, npx grandchild processes are reaped via process groups, and heavy stderr writers no longer deadlock.

No breaking changes are formally declared, but one behavior changed: MCP tools are advertised only when the request carries no tools of its own โ€” a request sending its own tools array passes through untouched, unaffected by MCP entirely.


v1.2.0 โ€” 2026-07-14 โ€” Provider capability matrix and plugin failure policyโ€‹

Formalizes provider capability declarations and fixes a dangerous plugin-error contract: a broken plugin no longer masquerades as a clean deny.

What's new in v1.2.0โ€‹

  • GET /v1/capabilities + provider capability matrix โ€” a declarative source of which OpenAI chat parameters each provider forwards, translates, or cannot express, enforced rather than merely advertised.
  • compatibility.on_unsupported_param: warn (default) | drop | reject โ€” unsupported parameters are no longer silently discarded.
  • request_timeout bounds a non-streaming request end-to-end (plugins + provider call + every retry/fallback); streaming is exempt except on the MCP agentic path.
  • targets[].concurrency โ€” per-target in-flight limits with a bounded queue; saturation sheds 429 provider_saturated.
  • /livez and /readyz split from /health for orchestrator gating.
  • Cross-provider conformance suite (test/conformance/) โ€” native-payload fixtures, no network; new providers must add a fixture or a declared exemption.
  • A hung provider now trips its circuit breaker (the gateway attributes the deadline to the provider).

Breaking changes in v1.2.0โ€‹

  • Plugin authors: deny by verdict, not error โ€” a returned error now means the plugin broke and yields 500 plugin_error; Context.Reject is the only way to deny a request. A down rate-limit backend no longer returns 429 and triggers SDK retry storms.
  • Context.Reject is now honored for every plugin type (previously silently discarded for logging/metrics/transform plugins).
  • Transform plugins fail closed on error (previously fail-open).
  • Retries limited to the default retryable set (408/429/5xx) with exponential backoff and full jitter, honoring Retry-After capped at 30s, stopping at the deadline โ€” 400/401 are no longer retried.
  • ${VAR} substitution: only the braced form is a reference; a bare $ is literal data ($100, pa$$w0rd survive); an undefined variable is now a startup error; resolution moved from config load to component construction so secrets never enter config history/rollback.

v1.1.0 โ€” 2026-05-24 โ€” OpenTelemetry tracingโ€‹

Adds opt-in OpenTelemetry tracing. Off by default โ€” a zero-allocation no-op until an OTLP endpoint or exporter is configured.

What's new in v1.1.0โ€‹

  • OpenTelemetry tracing โ€” every request emits a gateway.request span carrying model, token-usage, cost, and routing attributes; plugins and MCP tool calls emit child spans; outbound provider calls are otelhttp-instrumented with W3C traceparent propagation. OTLP over gRPC or HTTP/protobuf. See Observability.
  • Unified trace ID โ€” the OTel trace_id, the structured-log trace_id, and the X-Request-ID header are the same value per request.
  • Privacy levels โ€” observability.tracing.privacy_level: none, metadata (default; redacts email/JWT/AWS keys), or full.
  • Exporter event pathway โ€” registered exporters receive gateway.request.completed/failed events via the new observability.exporters block. Contract and wiring only; vendor bridges ship in the forthcoming ai-gateway-plugins repo.
  • OTLP headers โ€” observability.tracing.headers (with ${ENV} interpolation) and the standard OTEL_EXPORTER_OTLP_HEADERS env var enable authenticated export to managed backends. The endpoint scheme selects transport security (https:// โ‡’ TLS).

The v1.0.0 stability contract is unchanged โ€” tracing is purely additive.


v1.0.1 โ€“ v1.0.10 โ€” Maintenance releasesโ€‹

A series of maintenance releases between the v1.0.0 stable release and v1.1.0:

  • v1.0.10 (2026-05-16) โ€” security maintenance: upgraded grpc, golang.org/x/crypto, and the testcontainers-go dependency chain to clear Dependabot advisories; added govulncheck CI scanning.
  • v1.0.9 (2026-05-14) โ€” toolchain baseline moved from Go 1.24 to Go 1.25. No API or behaviour changes.
  • v1.0.7 โ€“ v1.0.8 โ€” internal refactor: gateway business logic moved from cmd/ferrogw into internal/ packages; integration-test harness completed. No public API changes.

v1.0.0 โ€” 2026-03-24 โ€” Stable releaseโ€‹

v1.0.0 is the first stable release of the Ferro Labs AI Gateway. Starting with this release, the project follows semantic versioning: breaking changes will only occur in major version bumps. The configuration format and OpenAI-compatible API are now part of the stable contract.

What's new in v1.0.0โ€‹

  • MCP streaming support โ€” clients can send stream: true when MCP servers are configured. The gateway resolves all tool calls internally and returns the final answer as SSE. Added in v1.0.0-rc.1.
  • 29 providers โ€” 10 providers added since v0.6.5: Cerebras, NVIDIA NIM, Cloudflare Workers AI, Databricks, Novita AI, Qwen (Alibaba), Moonshot AI, SambaNova, DeepInfra, OpenRouter.
  • 8 routing strategies โ€” content-based and A/B test routing (from v0.8.5) are now stable.
  • Per-key and per-user rate limiting โ€” key_rpm and user_rpm fields in the rate-limit plugin (from v0.8.5).
  • Budget plugin โ€” per-key spend tracking and enforcement (from v0.8.5).
  • Published benchmarks โ€” sub-millisecond p99 overhead at 500 RPS, 100% success rate sustained. See benchmarks.

Stability guaranteesโ€‹

  • The config.yaml schema is stable. Existing configs will continue to work across v1.x releases.
  • The OpenAI-compatible API (/v1/chat/completions, /v1/embeddings, /v1/models, /v1/images/generations) wire format is stable.
  • The admin API (/admin/*) is stable. New endpoints may be added but existing ones will not change in breaking ways.
  • Prometheus metric names and labels are stable.

v0.8.5 โ€” 2026-03-12โ€‹

Content-based routing strategyโ€‹

  • New strategy.mode: content-based selects a provider target based on user-role prompt content
  • Three condition types: prompt_contains (case-insensitive), prompt_not_contains, and prompt_regex (Go regexp)
  • Rules evaluated in declaration order โ€” first match wins; unmatched requests fall back to the first target
  • Regex patterns compiled at startup for zero-cost hot-path matching; invalid patterns surface as a startup error

A/B testing strategyโ€‹

  • New strategy.mode: ab-test splits traffic across two or more named variants using weighted random sampling
  • Each variant carries a label (e.g. "control", "challenger") emitted as the ab_variant structured log field on every routed request
  • Zero-weight variants participate with weight 1 (equal distribution)

Per-key and per-user rate limitingโ€‹

  • Extended rate-limit plugin with key_rpm (requests per minute per API key) and user_rpm (requests per minute per user ID)
  • Rate checks execute in order: global โ†’ per-key โ†’ per-user; request rejected at first exceeded limiter with a distinct reason string

Per-key budget controls pluginโ€‹

  • New budget plugin tracks cumulative USD spend per API key in an in-memory store
  • Register at before_request to reject over-limit keys and after_request to record token costs
  • Two instances sharing the same store_id share accumulated spend data
  • spend_limit_usd: 0 (or unset) means unlimited โ€” spend is tracked without rejection
  • Spend data is in-memory; does not survive process restarts

v0.8.0 โ€” 2026-03-10โ€‹

MCP integration (Phase 1)โ€‹

  • Added mcp_servers configuration block for Model Context Protocol tool servers
  • Gateway injects available tools into every chat completion request automatically
  • Full agentic loop: gateway handles all tool_calls rounds internally, returns final text to client
  • Background MCP initialisation on startup with 60-second timeout; gateway is ready immediately
  • MCPInitDone() channel on Gateway struct for sync when needed
  • Per-server allowed_tools whitelist for access control
  • Per-server max_call_depth limit to prevent infinite loops
  • Environment variable interpolation (${VAR}) in MCP server headers
  • 29 new tests covering MCP lifecycle and agentic loop behaviour
  • Bug fixes: nil-safe circuit breaker map init, empty config array handling, streaming fix for empty delta content

v0.7.0 โ€” 2026-03-08โ€‹

  • Comprehensive regression test suite (50+ end-to-end scenarios)
  • Fixed: race condition in concurrent provider health checks
  • Fixed: weight normalisation with single-target load balancer
  • Fixed: least-latency cold-start selecting excluded targets
  • Fixed: cost-optimized panic on missing catalog entry
  • Fixed: admin API pagination off-by-one on last page

v0.6.6 โ€” 2026-03-07โ€‹

  • Refactored providers/core subpackage; providers_list.go split for clarity
  • All Name* constants re-exported from providers top-level package
  • Dashboard XSS hardening (output encoding on all admin UI fields)
  • Added CORS origin validation warning on startup for wildcard origins
  • Removed 19 deprecated provider shim files

v0.6.5 โ€” 2026-03-07 โ€” 5 new providersโ€‹

  • xAI (Grok) โ€” XAI_API_KEY
  • Azure AI Foundry โ€” AZURE_FOUNDRY_API_KEY + AZURE_FOUNDRY_ENDPOINT
  • Hugging Face โ€” HUGGING_FACE_API_KEY
  • Google Vertex AI โ€” VERTEX_AI_PROJECT_ID (ADC)
  • AWS Bedrock (static credentials) โ€” AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY option
  • Provider subpackage refactor: unified factory pattern across all providers
  • Total providers: 19 (at time of release); total models in catalog: 2,531

v0.6.1 โ€” 2026-03-06โ€‹

  • CI GitHub Actions version bumps
  • Go dependency refresh (net/http, crypto)

v0.6.0 โ€” 2026-03-06 โ€” 5 new guardrail pluginsโ€‹

  • pii-redact โ€” detect and redact PII entities before forwarding
  • secret-scan โ€” block requests containing credentials or high-entropy secrets
  • prompt-shield โ€” score and block prompt injection attempts
  • schema-guard โ€” validate model output against JSON Schema (after_request)
  • regex-guard โ€” block requests matching configurable regex patterns
  • Total built-in plugins: 11 (5 new + 6 existing)
  • All new plugins ship disabled (enabled: false) in config.example.yaml

v0.5.0 โ€” 2026-03-03โ€‹

  • Streaming cost tracking โ€” token usage counted during streamed responses
  • Least-latency strategy โ€” P50 rolling latency tracker, routes to fastest provider
  • Cost-optimized strategy โ€” model catalog cost estimation, routes to cheapest provider
  • Per-target on_status_codes codes list (customise which HTTP status codes trigger retry)
  • CLI overhaul using Cobra โ€” ferrogw-cli with admin, models, and keys subcommands

v0.4.5 โ€” 2026-02-28โ€‹

  • Built-in model catalog with 2,531 model entries (pricing, context window, capabilities)
  • Cost calculator (models.Calculate()) used by cost-optimized strategy
  • /v1/models response enriched with catalog metadata (context window, max tokens, cost)
  • GitHub Actions catalog CI check โ€” fails the build if catalog format is invalid