ai-gateway

ferro-labsApache-2.0活跃维护

Go 编写的高性能 AI 网关,统一接入 30+ 家 LLM 服务商,内置缓存、护栏、A/B 测试和成本控制,可替代 LiteLLM 与 Kong AI Gateway。

Unified AI Gateway for 30+ LLMs (OpenAI, Anthropic, Bedrock, Azure etc) with Caching, Guardrails, A/B test & cost controls. Go-native Fastest & Scalable AI Gateway LiteLLM & Kong AI Gateway alternative.

v1.6.0发布于 2026-10-30 11:05 UTC287star +6 / 7 天40fork同步于 2026-11-11 00:00 UTC

怎么上手

由 README 提炼,执行前请确认
curl -fsSL https://get.ferrolabs.ai | sh
ferrogw init
export GATEWAY_CONFIG=./config.yaml
export MASTER_KEY=fgw_your-master-key
ferrogw serve
硬件要求按 README 说明

README 未说明显存或 GPU 要求;它是 Go 单静态二进制,无需外部服务,基础内存约 32 MB,可跑在普通服务器或容器上。

有 Docker 镜像
下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

Go 写的 OpenAI 兼容 LLM 网关,用一个 API 路由 30 家模型供应商

Go 编写的高性能 AI 网关,统一接入 30+ 家 LLM 服务商,内置缓存、护栏、A/B 测试和成本控制,可替代 LiteLLM 与 Kong AI Gateway。

核心能力

  • 提供 8 种路由策略,含 fallback、负载均衡、成本优化与 A/B 测试
  • 内置 11 个护栏插件,覆盖 PII 脱敏、注入防护与限流预算
  • 以 OpenAI 兼容接口统一转发 30 家供应商的聊天与流式请求
  • 内置 MCP 客户端,可注入工具并驱动 tool_calls 循环
  • 编译进二进制的运维控制台,查看流量、花费与请求日志

容易踩的坑

ferrogw init 只打印一次 master key 且不落盘,需自行保存;服务仅在设置 GATEWAY_CONFIG 时才读取配置文件;用 go install 构建的二进制不带控制台,访问 / 只返回占位页。

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

5 个版本v1.6.0
  1. v1.6.0最新正式版2026-10-30 · 2026-10-30 11:05 UTC

    [1.6.0] — 2026-10-30

    Added

    • pkg/cache.Fingerprint exposes versioned, namespace-scoped request keys for exact-match completion caches, using the response-cache plugin's encoder.
    • mcp.NewClientWithOptions supports a caller-supplied HTTP client, response-size limit and initialization identity. The existing NewClient signature and defaults remain unchanged. Constructors copy headers and client configuration. A custom client with no CheckRedirect retains the default redirect refusal; an explicitly supplied redirect policy is preserved.

    Changed

    • Cache fingerprints now use a v1: prefix and an opaque namespace. Existing in-memory entries may miss after the encoding change; no storage migration is required. TTL, capacity, credential isolation and request hooks are unchanged.
    • Fingerprints describe completion content. RoutingMetadata, StreamOptions and ClientStreamOptions are excluded. Applications must scope caches by routing configuration and handle stream framing and usage options separately. A fingerprint is not an authorization decision or a semantic-similarity score.
    • Future changes to encoded fields, including newly included request fields, require a new fingerprint version prefix and intentionally invalidate old keys.

    Fixed

    • Response-cache entries now include routing metadata in their namespace, so conditional routing cannot reuse an answer from a different metadata-selected target for the same prompt and credential. Invalid UTF-8 in routing metadata or credential IDs bypasses caching because JSON encoding would otherwise collapse distinct byte sequences into the same namespace.
    • Supplying an MCP HTTP client solely to customize its transport no longer enables automatic redirects. An unset redirect policy refuses redirects, preventing credential replay to redirect targets.
    • Requests with values that cannot be JSON encoded bypass the response cache. Previously, serialization error messages were hashed as request content, allowing distinct invalid requests to share a cache entry.

    Contributors

    Thanks to everyone who shipped this release:

    • release: prepare v1.6.0 (#461) — @MitulShah1

    Full changelog: https://github.com/ferro-labs/ai-gateway/blob/v1.6.0/CHANGELOG.md

  2. v1.5.112026-10-27 · 2026-10-27 10:06 UTC
    查看更新说明

    [1.5.11] — 2026-10-27

    Security fixes. No API or configuration changes. Building the gateway, or a module that imports it, now needs Go 1.26.10 or later.

    Security

    • Go 1.26.9 → 1.26.10, closing GO-2026-6809: crypto/x509 did not distrust a set of compromised intermediate certificates. The gateway verifies a certificate chain on every upstream TLS connection, so a provider endpoint presenting a chain through one of them was accepted. (#458)
    • Dashboard build and test tooling refreshed in web/: vitest and @vitest/coverage-v8 3.2 → 4.1.12 (path traversal in @vitest/mocker; removes tinypool and its two prototype-pollution advisories), vite 6.4.3 → 6.4.5, body-parser 2.3 → 2.4 and fast-uri 3.1.9. None of these ship in the bundle; the built dashboard is byte-identical to v1.5.10.

    Changed

    • The minimum Go version is 1.26.10 (go.mod, CI, release builds and deploy/Dockerfile). A module importing the gateway picks it up through GOTOOLCHAIN=auto, or fails to build on an older toolchain if that is off.
    • The embedded fallback model catalog (models/catalog_backup.json), used when the live catalog cannot be fetched, is refreshed from 2,542 to 2,947 models. (#394)

    Fixed

    • Requests to Responses-only models (gpt-5-codex, gpt-5.1-codex, codex-mini and the rest of the catalog's responses mode) and to legacy completions models (completion mode, e.g. gpt-3.5-turbo-instruct) are priced again. The model catalog began classifying these models by endpoint contract on 2026-08-22, and the cost calculator had no arm for either mode, so every such request was logged, traced and metered as unpriced at $0 and cost-optimized routing treated the models as unpriced. Both modes now bill the same token fields as chat.

    Contributors

    Thanks to everyone who shipped this release:

    • fix: apply v1.5.11 security fixes (#459) — @MitulShah1

    Full changelog: https://github.com/ferro-labs/ai-gateway/blob/v1.5.11/CHANGELOG.md

  3. v1.5.102026-10-18 · 2026-10-18 08:57 UTC
    查看更新说明

    [1.5.10] — 2026-10-18

    Security fixes and one guardrail fix. No API changes. Building the gateway, or a module that imports it, now needs Go 1.26.9 or later.

    Security

    • Go 1.25.13 → 1.26.9 and golang.org/x/net v0.58.0 → v0.60.0, closing GO-2026-6599, GO-2026-6600, GO-2026-6603, GO-2026-6605, GO-2026-6607, GO-2026-6608, GO-2026-6609, GO-2026-6610, GO-2026-6611, GO-2026-6612, GO-2026-6613 and GO-2026-6617 (HTTP/1 and HTTP/2 server and client handling in net/http, crypto/tls, net/textproto, html/template). Go 1.25 is out of support, so these have no 1.25 fix.
    • OpenTelemetry v1.43/v1.44 → v1.45.0, closing GO-2026-6505 (GHSA-8wmf-6v46-5gfg: exporter config logging could leak endpoint URLs).
    • Dashboard build dependencies refreshed in web/package-lock.json: proxy-addr, source-map-js, undici, js-yaml, ip-address, hono, fast-uri, qs, postcss-selector-parser, brace-expansion and @modelcontextprotocol/sdk. All are build or test tooling and never ship in the bundle; the built dashboard is unchanged.

    Changed

    • The minimum Go version is 1.26.9 (go.mod, CI, release builds and deploy/Dockerfile). A module importing the gateway picks it up through GOTOOLCHAIN=auto, or fails to build on an older toolchain if that is off.

    Fixed

    • word-filter now refuses to load when blocked_words is present but is not a list of strings: an explicit null, a scalar written where a list belongs, a map, or a list with a non-string item. Such a value used to load as an empty blocklist, so the guardrail registered and let every request through while startup and ferrogw validate reported it healthy. The plugin now implements ValidateConfig, so ferrogw validate reports the same error. An absent key still configures no words. Run ferrogw validate against your config before upgrading. (#447, thanks @shaurya416)
    • deploy/Dockerfile copies the run package, so a source-built image (make docker-build, deploy/compose.dev.yaml, render.yaml) builds again. It had failed with an unresolved import since v1.5.0. Release images are built from deploy/gateway.release.Dockerfile and were not affected.

    Contributors

    Thanks to everyone who shipped this release:

    • fix: apply v1.5.10 security fixes and validate word-filter config (#455) — @MitulShah1

    Full changelog: https://github.com/ferro-labs/ai-gateway/blob/v1.5.10/CHANGELOG.md

  4. v1.5.92026-09-18 · 2026-09-18 09:48 UTC
    查看更新说明

    [1.5.9] — 2026-09-18

    Added

    • The gateway emits one gateway.guardrail.match observability event per guardrail match, through the same Provider.RecordEvent fanout a request's terminal event uses, whatever action the match resolved to (block, warn, log, redact). A guardrail running in a non-blocking mode is now observable: a host can record what a log or warn rule would have blocked, which the block-only signal could not show. The event names the plugin, its instance id, the resolved action, the stage, and whether a guardrail denied the request at that stage — a before_request match reports how the request fared on the way in, and a guardrail rejecting the response later does not change it, which is why every event names its stage. A rate limiter or a budget denying the same request leaves it true: those are not guardrail verdicts, and it is never a claim that the provider call succeeded. It carries no matched text or offending value, keeping the gateway's no-leak posture. A non-matching plugin emits nothing, and a plugin that matches emits one event per resolved action per run rather than one per message, so a long conversation does not multiply identical events. The events are opt-in, on the same terms as routing-attempt events: an exporter implements observability.GuardrailMatchExporter, and an embedder's own provider implements observability.GuardrailMatchRecordingProvider. Everything written against one event per request keeps seeing exactly one, which is also why match events are not delivered to Gateway.AddHook hooks.

    • A plugin config entry accepts an optional id — an opaque, operator-supplied label for that instance. Several instances of one plugin can be configured (one rule each), and until now a decision named only the plugin type, so an operator could report that "some pii-redact rule fired" but not which one. A block's plugin.RejectionError now carries the id of the instance that produced it. The gateway treats the id as opaque and never interprets it, and it stays out of the instance identity used for multi-stage sharing (that is name plus config), so one instance listed across stages carries one id. ferrogw validate rejects an id claimed by two different instances, and one instance listed across stages under two different ids. An empty id keeps today's behaviour. A plugin registered by value rather than by pointer has no identity to key on and carries no id.

    • gateway.WithoutEnvExpansion() construction option disables ${VAR} substitution for plugin configs and MCP server headers/env. With it set, a ${NAME} reaches the plugin or MCP client verbatim and the process environment is never read for substitution. Off by default — expansion stays on — so an existing install is unchanged. It is defence-in-depth for a host that builds gateway instances from config it did not author, where a ${...} in that config would otherwise resolve against the host's own environment.

    Fixed

    • ferrogw validate and ferrogw doctor now refuse a guardrail whose rules cannot act at its configured stage — for example a regex-guard whose rules all apply_to: output listed at before_request, or the reverse. Such a plugin derives its supported stages from its compiled rules, so validate checked them on an instance that had not been initialised and reported the config healthy while serve refused it at startup. Validate now compiles the rules (when the config block carries no ${VAR} reference) before the stage check, so the refusal arrives before the deploy rather than as a failed start. A block carrying a reference keeps today's behaviour, since its rules cannot be compiled before the environment is available.

    Contributors

    Thanks to everyone who shipped this release:

    • feat: observable guardrail decisions, plugin instance ids, and an env-expansion off switch (#446) — @MitulShah1
  5. v1.5.82026-09-16 · 2026-09-16 14:51 UTC
    查看更新说明

    [1.5.8] — 2026-09-16

    Added

    • piiredact.Detect and promptshield.Detect — pure functions that report, by name, which built-in PII entity types or injection categories occur in a string. They run the same patterns the plugins enforce and never return matched text, so an embedding policy layer can ask the same question with the same answer.
    • pii-redact accepts action: log: detect and record, without denying or rewriting. The observe-only mode every other content guardrail already has.
    • secret-scan reports Anthropic keys under their own anthropic_key kind instead of openai_key. The key was already caught; the audit trail now names the right vendor, and kinds: ["openai_key"] no longer covers a vendor it does not name.
    • secret-scan gains seven curated kinds, each matched on structure alone: gitlab_token, npm_token, huggingface_token, sendgrid_key, twilio_key (the API key; an account SID is an identifier and is left alone), azure_storage_key and slack_webhook.

    Fixed

    • The least-latency end-to-end check tolerates normal scheduling variance while still failing a strategy that has stopped preferring the fast target.

    Contributors

    Thanks to everyone who shipped this release:

    • feat(plugin): export detectors and add a pii-redact log action (#441) — @MitulShah1

    Full changelog: https://github.com/ferro-labs/ai-gateway/blob/v1.5.8/CHANGELOG.md

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    High-performance AI gateway in Go. Route LLM requests across 30 providers via a single OpenAI-compatible API.

    Deploy on Railway: SQLite Deploy on Railway: PostgreSQL Deploy to Render: PostgreSQL

    Go Go Reference codecov License GitHub Stars CI Code Scanning Ask DeepWiki Artifact Hub Docs Discord

    📖 Documentation: docs.ferrolabs.ai

    🔀 30 providers, 2,500+ models — one API ⚡ 13,925 RPS at 1,000 concurrent users (v1.0.0 benchmark) 📦 Single static binary, no external services required, 32 MB base memory


    Quick Start

    Under two minutes from install to first response.

    Platform / toolInstall
    macOS, Linuxcurl -fsSL https://get.ferrolabs.ai | sh
    Windowsirm https://get.ferrolabs.ai/install.ps1 | iex
    Homebrewbrew install ferro-labs/tap/ferrogw
    Scoopscoop bucket add ferrolabs https://github.com/ferro-labs/homebrew-tap then scoop install ferrogw
    npmnpm install -g ferrogw
    Pythonuv tool install ferrogw
    Dockerdocker run -p 8080:8080 ghcr.io/ferro-labs/ai-gateway:latest
    Gogo install github.com/ferro-labs/ai-gateway/cmd/ferrogw@latest — builds from source, without the dashboard
    Debian, RPM, Alpine.deb, .rpm and .apk packages on the releases page

    Then go from nothing to a served request:

    export OPENAI_API_KEY=sk-your-key     # ferrogw init detects this and writes the matching target
    ferrogw init                          # writes config.yaml, prints your master key
    export GATEWAY_CONFIG=./config.yaml   # the server reads a config file only when this is set
    export MASTER_KEY=fgw_your-master-key # the key ferrogw init printed
    ferrogw serve                         # starts the server on :8080
    

    ferrogw init prints the master key once and never writes it to disk — save it yourself, in your .env file or a secret manager.

    Docker runs the server itself, so there is no ferrogw init to print a master key — choose your own. Pass both variables by name so their values stay off the command line, where ps would show them:

    export OPENAI_API_KEY=sk-your-key
    export MASTER_KEY=fgw-any-strong-secret-you-choose
    docker run -p 8080:8080 -e OPENAI_API_KEY -e MASTER_KEY ghcr.io/ferro-labs/ai-gateway:latest
    

    Passing them by name keeps the values out of process arguments only — they are still readable in the container's environment and through docker inspect, so use Docker secrets or your platform's secret storage in production.

    First request

    export MASTER_KEY=fgw_your-master-key   # the key ferrogw init printed, in whichever shell you curl from
    
    curl http://localhost:8080/v1/chat/completions \
      -H "Authorization: Bearer $MASTER_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "model": "gpt-4o-mini",
        "messages": [{"role": "user", "content": "Hello from Ferro Labs AI Gateway"}]
      }' | jq
    

    Every release is signed with keyless cosign and ships an SPDX SBOM — verification steps are in SECURITY.md.


    Why Ferro Labs AI Gateway

    AI gateways differ most under load, in throughput, latency and memory. Ferro Labs AI Gateway is written in Go from the ground up for real-world throughput — a single binary that routes LLM requests with predictable latency and minimal resource usage.

    FeatureFerro LabsLiteLLMBifrostKong AI
    LanguageGoPythonGoGo/Lua
    Single binary✅❌✅❌
    Providers30100+20+10+
    MCP support✅❌✅❌
    Response cache✅✅✅❌ (paid)
    Guardrails✅✅❌❌ (paid)
    OSS licenseApache 2.0MITApache 2.0Apache 2.0
    Managed cloudComing Soon✅✅✅

    Features

    CapabilityWhat it doesReference
    🔀 Routing8 strategies — single, fallback, load balance, least latency, cost-optimized, content-based, A/B test, conditional — with per-target retry, failover, and model aliasesDocs →
    🔌 30 providersChat and streaming everywhere; embeddings, images, rerank, moderations, speech-to-text, text-to-speech, and batch where the vendor offers themDocs →
    🛡️ Guardrails & pluginsEleven built in — word filter, regex guard, PII redaction, secret scanning, prompt-injection shield, response schema validation, token/message limits, response cache, rate limiting, per-key budgets, request logging — and the plugin framework is public for writing your ownDocs →
    🎯 Capability matrixOne declarative record of which OpenAI parameters each provider forwards, translates, or cannot express, served by GET /v1/capabilitiesDocs →
    🤖 MCPConnects to stdio and Streamable HTTP tool servers, injects their tools into chat completions, and drives the agentic tool_calls loop itselfDocs →
    📊 ObservabilityOpenTelemetry tracing and Prometheus metrics, one trace ID across logs and spans — a zero-allocation no-op until enabledDocs →
    🖥️ DashboardOperations console compiled into the binary and served at / — traffic, spend, provider health, request logs, audit trailDocs →

    Dashboard

    Every release binary serves a built-in operations console at / — same port as the API, compiled in with go:embed, no second image and no second origin. Sign in with your MASTER_KEY or any admin / read-only key and it reads the live gateway: traffic, spend, provider health, routing, plugins, request logs, and the audit trail.

    The bundle is produced by the release pipeline, so a binary built with go install is the one exception: it answers / with a placeholder instead. Any other install method above ships the console.

    Overview, Analytics (latency/TTFT/cost percentiles), Providers, Routing, Plugins, a Playground over the real routing path, Tracing, Request Logs, the Audit Trail, Configuration with history/rollback, and scoped API key management.

    Run the gateway and open http://localhost:8080. To see it filled like the recording above, bring up the self-contained demo stack:

    make up-fullstack   # gateway + Postgres + Jaeger + Prometheus + Grafana + mock upstream + load generator
    # then open http://localhost:8080
    

    Build and development details are in web/README.md.


    Documentation

    The root README is the overview; each subsystem keeps its own reference beside its code:

    ReferenceCovers
    providers/README.mdThe 30 providers, the per-provider endpoint matrix, and every /v1/* surface
    config/README.mdConfig loading, validation, ${VAR} secrets, declared models, trusted proxies
    internal/strategies/README.mdAll 8 routing strategies and their failure semantics
    plugin/README.mdThe plugin framework and the eleven built-in plugins
    mcp/README.mdMCP tool servers, transports, the subprocess trust boundary, readiness
    observability/README.mdTracing setup, managed backends, emitted attributes, privacy levels, exporters
    deploy/README.mdDockerfiles, Compose files, the fullstack demo stack
    web/README.mdDashboard development and the embed contract
    AGENTS.mdThe complete operator/developer reference: architecture, every env var, request flow
    SECURITY.mdReporting, security posture, release verification
    CONTRIBUTING.mdBranch strategy, commit conventions, provider/plugin checklists

    Examples

    Integration examples for common use cases are in ferro-labs/ai-gateway-examples:

    ExampleDescription
    basicSingle chat completion to the first configured provider
    fallbackFallback strategy — try providers in order with retries
    loadbalanceWeighted load balancing across targets (70/30 split)
    with-guardrailsBuilt-in word-filter and max-token guardrail plugins
    with-mcpLocal MCP server with tool-calling integration
    embeddedEmbed the gateway as an HTTP handler inside an existing server

    Configuration

    One YAML/JSON file, named by GATEWAY_CONFIG, drives routing, guardrails, MCP tools, and observability:

    strategy:
      mode: fallback              # 8 modes — see internal/strategies/README.md
    
    targets:
      - virtual_key: openai       # an allowlist: only listed providers are routable
        retry: { attempts: 3 }    # per target, honoured under every routing mode
        concurrency: { max_concurrency: 32, queue_size: 1000 }
      - virtual_key: anthropic
    
    aliases:
      fast: gpt-4o-mini
    
    plugins:                      # guardrails first, then cache — see plugin/README.md
      - name: word-filter
        type: guardrail
        stage: before_request
        enabled: true
        config: { blocked_words: ["password", "secret"] }
    
    mcp_servers:                  # tool servers — see mcp/README.md
      - name: search
        url: https://mcp.example.com/mcp
        headers: { Authorization: "Bearer ${SEARCH_TOKEN}" }
    

    ${VAR} references (braced form only) resolve when a component is constructed, never at file load — so secrets are never stored, served by GET /admin/config, or restored by a rollback. A bare $ is data; an undefined variable is an error.

    The full annotated reference with every option is config.example.yaml / config.example.json, and the schema guide is config/README.md. ferrogw validate checks a file without starting the server.

    Key environment variables

    VariablePurpose
    MASTER_KEYBootstrap and break-glass admin credential (generated by ferrogw init); give each operator their own key from POST /admin/keys for day-to-day use
    GATEWAY_CONFIGPath to config YAML/JSON
    GATEWAY_ENVSet to production to enable production-mode safety guards: it refuses to start on ALLOW_UNAUTHENTICATED_PROXY=true or a * entry in CORS_ORIGINS, and warns when per-IP rate limiting is off, pprof is mounted, or the API key store is in-memory
    PORTServer port (default: 8080)
    ALLOW_UNAUTHENTICATED_PROXYSet to true to disable proxy-route auth (dev only; blocked when GATEWAY_ENV=production)
    CORS_ORIGINSComma-separated allowed CORS origins; cross-origin is denied when unset. Matched literally — there is no wildcard, so list each origin explicitly
    TRUSTED_PROXIESCIDRs of trusted reverse proxies; forwarded headers are honored only from these (default: loopback). See config/README.md
    <PROVIDER>_BASE_URLPoints a provider at a proxy, self-hosted server, or regional endpoint. It is the API root, used verbatim — write it exactly as the vendor documents it, version segment included (https://api.groq.com/openai/v1); a bare host resolves to the provider's own version segment

    See AGENTS.md for the full environment variable reference including provider API keys, store backends, and OTel settings.


    Observability

    See everything your gateway does — every request, what it cost, how long it took, which provider served it, and which guardrails ran. Ferro Labs AI Gateway ships first-class OpenTelemetry tracing and Prometheus metrics out of the box, and stays at a zero-allocation no-op until you turn it on. Point it at Jaeger, Grafana, New Relic, LangSmith, Datadog, or Honeycomb — anything that speaks OTLP — and every request emits a gateway.request span carrying GenAI semantic conventions (gen_ai.*) plus ferro.* extensions for cost, routing, MCP tool calls, and stream timings. The same trace ID threads your logs, spans, and the X-Request-ID response header. Requests can name the end user and session they belong to (user, X-User-ID, X-Session-ID, or W3C baggage), and that identity rides the request span, every event, and request-log rows.

    📈 Full observability guide → observability/README.md — managed-backend setup, endpoint & transport rules, every emitted attribute, privacy levels, and exporter plugins.

    Bring up the gateway wired to a full monitoring stack — Prometheus, Grafana, and Jaeger — driven by generated traffic, in one command:

    make up-fullstack   # then open Grafana at http://localhost:3000
    

    Enable tracing with one variable (or the observability: config block — endpoint, protocol, sampling, privacy, headers are all documented in the guide):

    export OTEL_EXPORTER_OTLP_ENDPOINT=localhost:4317
    ferrogw serve
    

    CLI

    ferrogw is a single binary — no separate CLI tool required.

    CommandDescription
    ferrogwStart the gateway server (default)
    ferrogw serveStart the gateway server (explicit)
    ferrogw initFirst-run setup — generate master key and config
    ferrogw validateValidate a config file without starting
    ferrogw doctorCheck environment (API keys, config, connectivity)
    ferrogw statusShow gateway health and provider status
    ferrogw versionPrint version, commit, and build info
    ferrogw admin keys listList API keys
    ferrogw admin keys create --name <name>Create an API key (--scope, --expires-in)
    ferrogw admin logs statsShow request log statistics
    ferrogw pluginsList registered plugins

    Global flags available on all subcommands: --gateway-url, --api-key, --format (table/json/yaml).


    Deployment

    Local development

    export OPENAI_API_KEY=sk-your-key
    export MASTER_KEY=fgw_your-master-key
    export GATEWAY_CONFIG=./config.yaml
    make build && ./bin/ferrogw
    

    Railway & Render

    The deploy buttons at the top of this README provision either platform: Railway with SQLite on a volume (point API_KEY_STORE_DSN, CONFIG_STORE_DSN and REQUEST_LOG_STORE_DSN at paths under /data) or PostgreSQL, and Render from the repo's render.yaml Blueprint, which generates MASTER_KEY and wires the store DSNs to a managed Postgres automatically.

    Docker Compose

    Three Compose files in deploy/ follow the standard override pattern — a shared base, a dev override that builds from source, and a prod override with a pinned tag, health check, and resource limits. Run everything from the repository root:

    make up             # dev: builds from source
    IMAGE_TAG=v1.4.5 CORS_ORIGINS=https://your-domain.com make up-prod
    make down           # tears down either
    

    One container serves both the API and the dashboard — no second image, no second origin. Provider keys go in a repository-root .env or the environment. deploy/README.md has the full reference, including a self-contained PostgreSQL pairing and the fullstack observability stack.

    Kubernetes via Helm

    helm repo add ferro-labs https://ferro-labs.github.io/helm-charts
    helm repo update
    helm install ferro-gw ferro-labs/ai-gateway \
      --set env.OPENAI_API_KEY=sk-your-key
    

    Helm charts: github.com/ferro-labs/helm-charts | ArtifactHub


    Migrate to Ferro Labs AI Gateway

    The gateway is OpenAI-compatible, so for a client already using an OpenAI-compatible SDK, migration — from another gateway or from calling a provider directly — is a base_url and api_key change: point the SDK at the gateway and present a gateway-issued credential in place of the provider's own. A client with its own API, such as LiteLLM's completion() below, also moves to the OpenAI SDK.

    From LiteLLM

    Before (LiteLLM):

    from litellm import completion
    
    response = completion(
        model="gpt-4o",
        messages=[{"role": "user", "content": "Hello"}]
    )
    

    After (Ferro Labs AI Gateway):

    from openai import OpenAI
    
    client = OpenAI(
        base_url="http://localhost:8080/v1",
        api_key="your-ferro-api-key",
    )
    
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": "Hello"}],
    )
    

    Provider API keys move to environment variables (OPENAI_API_KEY, ANTHROPIC_API_KEY, …); the model list becomes targets + aliases in config.yaml.

    Why migrate from LiteLLM:

    • 14x higher throughput at 150 concurrent users (2,447 vs 175 RPS)
    • 23x less memory at peak load (47 MB vs 1,124 MB under streaming)
    • Single binary — no Python environment, no pip, no virtualenv
    • Predictable latency — p99 stays under 65 ms at 150 VU vs LiteLLM's timeouts at the same concurrency

    From Portkey

    The code change is the same one line — Ferro Labs uses the standard OpenAI SDK with no custom headers in self-hosted mode.

    Why migrate from Portkey:

    • Fully open source — no per-request pricing, no log limits
    • Self-hosted — the gateway runs in your infrastructure, and prompts reach only the providers you configure
    • No vendor lock-in — Apache 2.0 license
    • MCP support — Portkey self-hosted lacks native MCP
    • FerroCloud (coming soon) for teams that want a managed service

    Performance

    Every figure in this section comes from one run: Ferro Labs v1.0.0, measured 2026-03-23. Benchmarked against Kong OSS, Bifrost, LiteLLM, and Portkey on GCP n2-standard-8 (8 vCPU, 32 GB RAM) using a 60ms fixed-latency mock upstream — results reflect gateway overhead only. Later releases have not been re-measured; reproduce against the version you intend to run using the commands below.

    Throughput comparison — Ferro Labs vs Kong, Bifrost, LiteLLM, Portkey across 150–1,000 VU

    VURPSp50p99Memory
    5081361.3ms64.1ms36 MB
    1502,44761.2ms63.4ms47 MB
    3004,89061.2ms64.4ms72 MB
    5008,01461.5ms72.9ms89 MB
    1,00013,92568.1ms111.9ms135 MB

    At 1,000 VU: 13,925 RPS, p50 overhead 8.1ms, memory 135 MB. Against the live OpenAI API, the gateway itself adds 25 microseconds p50 in a typical plugin configuration, and 2 microseconds with no plugins enabled.

    Full methodology, raw results, and flamegraph analysis: ferro-labs/ai-gateway-performance-benchmarks (make setup && make bench reproduces it).


    FerroCloud

    FerroCloud — the managed version of Ferro Labs AI Gateway with multi-tenancy, analytics, and cost governance — is coming soon.

    👉 Join the waitlist at ferrolabs.ai


    SDKs

    Official client libraries for the Ferro Labs AI Gateway — and the standard OpenAI SDK works unchanged: point base_url at http://your-gateway:8080/v1.

    SDKInstallRepository
    Pythonpip install ferrolabsferro-labs/ferrolabs-python-sdk
    TypeScriptnpm install ferrolabsferro-labs/ferrolabs-typescript-sdk
    from ferrolabs import FerroClient
    
    client = FerroClient(
        base_url="http://localhost:8080/v1",
        api_key="your-ferro-api-key",
    )
    
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": "Hello"}],
    )
    
    import { FerroClient } from "ferrolabs";
    
    const client = new FerroClient({
      baseURL: "http://localhost:8080/v1",
      apiKey: "your-ferro-api-key",
    });
    
    const response = await client.chat.completions.create({
      model: "gpt-4o",
      messages: [{ role: "user", content: "Hello" }],
    });
    

    Contributing

    We welcome contributions. New providers go in this OSS repo only — never in FerroCloud. See CONTRIBUTING.md for branch strategy, commit conventions, and PR guidelines.


    Community


    License

    Apache 2.0 — see LICENSE.