67 docs indexed
The obleth Next.js dashboard: what it shows, how to configure it, and how it connects to the Management API.
The control plane is a Next.js dashboard that consumes the obleth Management API exclusively — it never touches Postgres, Redis, or ClickHouse directly. All domain logic, validation, and audit logging is in one place (the Management API), and the dashboard is just a UI.
| Environment | URL |
|---|---|
| Docker Compose (dev) | http://localhost:3002 |
| Kubernetes (Helm default) | http://localhost:3000 (via port-forward) or your Ingress host |
In the Docker Compose stack, the dashboard uses dev-admin-token by default. Change it by setting OBLETH_ADMIN_TOKEN and OBLETH_ADMIN_BASE_URL in the control-plane environment.
Sign-in is email and password. A break-glass admin is seeded on first boot from
DASHBOARD_ADMIN_EMAIL and DASHBOARD_PASSWORD; the admin token is held
server-side and never entered in the browser. OIDC single sign-on is optional
and layered on top — see Dashboard SSO, which also
covers reaching the dashboard from a host other than BETTER_AUTH_URL.
The sidebar groups pages into four sections:
| Group | Pages |
|---|---|
| Overview | Overview |
| Operations | Fairshare, Models, Playground, Request Logs, Reports |
| Access | Tenants, API Keys, Users |
| Configuration | Recipes, MCP Servers, Settings, Audit |
Deleting a key, tenant, model, endpoint, MCP server, or recipe template opens a confirmation dialog that states the consequence before anything is removed.
The landing page answers "is anything wrong, and what do I do about it" first:
That footer poll is one aggregate call —
GET /api/v1/overview/summary — returning
tenant, model, and key counts alongside request, token, and cost totals for the
window, instead of downloading the full tenant, key, and model lists to count
them.
Benchmark and health-probe traffic is excluded from these figures by default (see Synthetic tenants).
PATCH /api/v1/tenants/{id}/weight)PUT /api/v1/tenants/{id}/quota)A tenant can be flagged synthetic on its Controls tab (or with
PUT /api/v1/tenants/{id}/synthetic). The gateway stamps its requests
request_type = "benchmark", which usage and cost reads exclude by default —
so a load run cannot bury a model's real numbers — and benchmark traffic never
enters the permanent daily rollup. Pass include_internal=true on a usage query
to see it. obench flags the fixture tenants it seeds, and the reserved internal
identity behind in-dashboard model tests and benchmarks is synthetic too.
GET /api/v1/usage/keys/summary.
Keys with no traffic in the window show "Never". For a single key's exact
last-used time across full retention, call
GET /api/v1/keys/{id}/usage.chat, embedding, audio_transcription, audio_speech, or image); the form reveals the cost fields that apply to that modality (cost_per_image, cost_per_character) and shows a type badge on each rowstatic / tuned), and status0.00000008) instead of scientific notation, so they stay readable and editable0 = model opted out)Show benchmark toggleapi_base + api_key flow) and Slurm provisioned (Apptainer image + launch command; no static endpoint). Slurm-provisioned models show a Provisioning tab with replica state, target count, and spec editing. Non-Slurm models never show this tab. See Slurm Provisioning.The Models page has Import and Export buttons for managing routes in bulk — useful for backups, moving a model set between environments, or seeding a fresh deployment.
Export downloads a JSON file (obleth-models-<date>.json) containing every registered route. Server-assigned fields (id, timestamps) and the upstream api_key are intentionally omitted, so the export is safe to share and never leaks secrets.
Import accepts the obleth models template only — a file with a top-level models: list, in either YAML or JSON. Foreign formats (LiteLLM model_list, Kubernetes ConfigMaps) are not accepted. After you pick a file, the dashboard shows a preview that diffs the file against the current registry before anything is written:
model_name and updated in place)N models in file / X new / Y to updateOn import, routes are matched by model_name. Fields omitted from a model entry fall back to existing values on update, or to sane defaults on create. Per-model failures (e.g. a rejected api_base) are collected and shown individually in the result banner rather than aborting the whole import.
Required fields per model: model_name, upstream_model, api_base. Everything else is optional:
# obleth models import template
models:
- model_name: qwen3-235b-a22b-instruct-2507
description: Qwen3 235B A22B instruct
model_type: chat # chat | embedding | audio_transcription | audio_speech | image
upstream_model: asuair/qwen3-235b-a22b-instruct-2507
api_base: https://openai.rc.asu.edu/v1
api_key: sk_1234 # optional; omit to leave unset / unchanged
input_cost_per_token: 0.000000071
output_cost_per_token: 0.0000001
context_window: 262144
admission_weight: 100
max_in_flight: # blank = no cap
supports_function_calling: true
supports_system_messages: true
supports_response_schema: true
supports_tool_choice: true
enabled: true
tags: [general, long-context]
boons: [] # e.g. [vision, structured_output]; gateway capabilities granted to a model that lacks them natively
tool_servers: [] # registered MCP servers whose tools this model may use (gateway tool loop)
# Non-chat routes set model_type and the cost field for their modality:
- model_name: qwen3-embedding-8b
model_type: embedding
upstream_model: Qwen/Qwen3-Embedding-8B
api_base: https://openai.rc.asu.edu/v1
input_cost_per_token: 0.00000001
enabled: true
- model_name: sdxl
model_type: image
upstream_model: stabilityai/stable-diffusion-xl-base-1.0
api_base: https://openai.rc.asu.edu/v1
cost_per_image: 0.02 # billed × n
enabled: true
- model_name: qwen3-tts
model_type: audio_speech
upstream_model: Qwen/Qwen3-TTS
api_base: https://openai.rc.asu.edu/v1
cost_per_character: 0.000015 # billed per input character
enabled: true
Routing tags use the fixed auto-router vocabulary: coding, general, reasoning, math, vision, long-context, fast, creative. Only chat routes participate in auto routing. See Multi-modal Models for the modality fields.
Note: By default, internal cluster
api_basehosts (e.g.*.svc.cluster.local) are allowed — private/LAN addresses are permitted for local-first deployments. Link-local and cloud-metadata targets are always blocked. If you run strict SSRF mode (OBLETH_BLOCK_PRIVATE_NETWORKS=1), add the pod CIDR toOBLETH_ALLOWED_PRIVATE_CIDRS(e.g.10.0.0.0/8); blocked hosts surface as per-model errors in the import result banner.
in_flight, queued, served_tokens, share_scoreA workspace for exercising models through the gateway from the dashboard. Choose one model to chat with, or add up to four and compare their answers to the same prompt side by side — each keeps its own conversation context. System prompt and generation settings are per session, sessions are stored in the browser, and a session can be exported. Requests go through the normal data plane as the reserved internal identity, so every boon configured on the model fires exactly as it would for a real client, and the traffic stays out of usage statistics.
Dashboard accounts, not tenants. Approve users who signed in through SSO, assign
each a role (admin or user), and link them to a tenant. New SSO users have
no access until an admin does this. See
Dashboard SSO.
Admin-authored *.recipe files that deploy as managed models — see
Slurm Provisioning.
A live, per-request view of traffic as it lands in the ClickHouse usage ledger.
Unlike Overview and Reports, which read aggregates, Request Logs returns
individual rows — one per completed request (successes and rejections like 429/403)
with a timestamp, latency, cost, and tenant/key attribution.
success / error), Traced only (show only requests with recorded spans)request_id UUIDColumns shown: Time, Type, Status, Model, Session, Request ID, Cost, Energy (Wh, when energy accounting is enabled), Tokens, TTFT, Duration, Team, Key. Team and key names are resolved from Postgres; the ledger itself stores only UUIDs. The detail view adds the request's energy cost and CO₂.
Request type is derived from the API path (chat, embedding, audio,
image, completion, responses, rerank, moderation, or other).
Session ID groups related requests when the client supplies one. obleth checks, in order:
x-obleth-session-id or x-session-id headersession_id in the JSON bodymetadata.session_id in the JSON bodyuser in the JSON body (OpenAI convention)Values are trimmed and capped at 200 characters. If none are present, the Session column is empty.
Requests that were traced (i.e. the key or tenant had tracing_enabled set)
show a small trace icon in the row. Clicking any row opens the Request Detail
panel, which shows:
auth_resolve →
"Auth", boon:tool_loop:iter:0 → "Iter 0", etc.).To enable tracing, toggle tracing_enabled on the relevant tenant (Tenants page)
or API key (API Keys page). See Per-request span tracing.
The page calls GET /api/v1/usage/logs
via the control-plane BFF (/api/live/usage/logs). Raw rows are retained for the
configured retention window (default 180 days); the permanent usage_daily rollup
behind Reports is unaffected.
Long-range usage analytics backed by the permanent usage_daily rollup, so the
Reports page keeps full history even after the raw ledger has been pruned to its
retention window. It calls GET /api/v1/usage/daily.
cost_usd) and key name by default alongside the token, latency, and energy columns (Energy (kWh), CO₂ (g), Energy cost (USD))Tabbed: Alerts, Routing (auto-router and model boons, including the gateway tool loop), Compression, Energy, Data, Slurm, Assistant, and About.
usage_daily rollup behind Reports is unaffected| Variable | Purpose |
|---|---|
OBLETH_ADMIN_BASE_URL | Base URL of the Management API (e.g. http://localhost:9180) |
OBLETH_ADMIN_TOKEN | Admin bearer token for all Management API calls |
DASHBOARD_ADMIN_EMAIL | Email for the break-glass admin (sign-in is email-based) |
DASHBOARD_PASSWORD | Break-glass admin password (>= 8 chars) |
DASHBOARD_SESSION_SECRET | Session cookie signing secret (≥32 chars; change in production) |
DATABASE_URL | Postgres URL for the auth tables (same DB as the gateway) |
BETTER_AUTH_URL | External, browser-facing dashboard URL; the origin logins are checked against and the host in OIDC redirect URIs |
TRUSTED_ORIGINS | Optional comma-separated origins trusted for login in addition to BETTER_AUTH_URL (* trusts all — private networks only) |
OBLETH_PROXY_BASE_URL | Data-plane URL used by the Playground and Gateway Chat (default http://localhost:8080) |
OIDC_PROVIDERS | Optional JSON array enabling OIDC SSO — see the Dashboard SSO guide |
In the Docker Compose stack, these are configured via deploy/docker/.env.