Skip to main content

The embedded dashboard

Every Ferro Labs AI Gateway binary serves a built-in operations console. As of v1.4.0 it is a React single-page app compiled into the binary with go:embed (web/ builds the bundle, internal/webui serves it) and mounted at the site root on the gateway's own port โ€” the same origin as the API. There is no second container, no separate web image, no /dashboard path, and no GATEWAY_BASE_URL: the SPA answers every path the API router does not, so an unmatched route lands on the console.

Sign in and it reads the live gateway โ€” traffic, spend, providers, routing, plugins, request logs, and the audit trail โ€” over the same /admin/* endpoints the CLI uses.

Accessing the dashboardโ€‹

Start the gateway and open its root in a browser:

ferrogw                 # starts the server on :8080 by default
# then open
open http://localhost:8080/

For a self-hosted deployment, that is http://your-gateway/ on whatever host and port you run it on โ€” the console lives wherever the API does.

Sign in with a key, browse with a sessionโ€‹

The gateway has no user accounts; a key is the identity. The login screen takes your MASTER_KEY or any admin / read_only API key and exchanges it for a short-lived session via POST /admin/session. The credential is sent exactly once and never stored in the browser โ€” only the returned session token reaches storage. That is the whole point of the exchange: a master key sitting in browser storage cannot be revoked or expired, but a session can.

Sessions are 24h absolute / 1h idle. They are listable and revocable from the API Keys page (backed by GET /admin/sessions, DELETE /admin/sessions/{id}), so a single lost device can be signed out without disturbing every operator, and DELETE /admin/sessions signs everyone out at once. POST /admin/session is throttled per source address to blunt credential guessing.

tip

Give each operator their own admin-scoped key from the API Keys page and keep MASTER_KEY for bootstrap and break-glass. Sign-ins and key changes are recorded in the audit trail against the acting credential โ€” a shared key names itself and answers nothing.

The pagesโ€‹

The left navigation maps one-to-one onto the gateway's admin surface:

PageWhat it shows
OverviewAt-a-glance health: routable targets, recent traffic, and spend, read from the live gateway.
AnalyticsTraffic and failure series, token split (prompt vs completion), p50 / p95 / p99 request latency and TTFT, and spend ranked per provider and per model โ€” all from GET /admin/logs/stats.
ProvidersThe registered providers, their configured surfaces, and per-target circuit state (polled from /health).
Routing StrategyThe active routing mode and its targets.
PluginsThe plugins this instance has configured, described against the catalog the build ships (GET /admin/plugins/catalog) so a card can never name a setting that does not exist.
Request LogsOne row per request, filterable by model, provider, and credential. It resolves each row's api_key_id back to the key name (via GET /admin/keys), marks a key revoked or expired since it served, and shows duration_ms, ttft_ms, and cost_usd. The credential itself is never displayed.
AuditThe durable audit trail from GET /admin/audit โ€” credential changes, sign-ins (accepted and denied), and log purges โ€” filterable by action, actor, and outcome.
ConfigurationThe live config with version history and rollback. When a config store is configured the history is durable; changes and their audit entries are applied as one serialized operation, so a rollback cannot target the wrong version.
API KeysCreate, scope, and revoke API keys, and manage active dashboard sessions.
PlaygroundSend chat completions through the real routing path โ€” the same targets, plugins, and strategy a production request takes.
TracingThe request-trace view added in v1.4.0, pairing with the fullstack observability demo stack.

To see the console filled the way the marketing recording shows it, bring up the self-contained demo stack, which drives traffic through the gateway:

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

What it is not yetโ€‹

The dashboard is an honest read of the gateway, not a versioned control plane. Three limits are worth knowing before you lean on it:

warning
  • It rides the unversioned /admin/* API. A stable, versioned /admin/v1 contract (Admin API v1) is future scope (targeted for v1.6.x), so treat these endpoints as internal โ€” they can change between releases.
  • Provider status means registration, not health. The Providers page tells you a provider is configured and whether its circuit is open, not that a live probe reached the upstream.
  • A read_only session can still invoke inference through the Playground. A dedicated inference:invoke permission does not exist yet; a read-only key can send Playground requests, and each is billed by the upstream provider like any other completion.