Skip to content

The console

For administrators

A map of the console at /console: which screen answers which question, and which page explains it.

The console is the React admin app the gateway serves at /console. Everyone who signs in sees two personal screens; administrators also get an Admin button beside their name, which opens a landing page with a card per screen. Each card says what the screen decides, not what it is called.

For everyone

Screen What it shows
Overview where you stand this period, your quotas and the API keys you spend with (create and revoke them here; a person without the API-keys capability is told so instead of being offered Create)
Your usage what you spent, and what the figures rest on. Administrators have it too: their own spend is not administration

Money is shown to three decimal places and never rounds a real amount down to zero (it reads < €0.001). Administrators can switch to full precision with the Exact figures toggle.

Administration

I want to… Screen Read
see what the deployment spent, by group, person, model, provider or day, and chart it over time Usage Accounting and quotas
list every request, one line each and never its content, filter it and export it Requests The request log
set a ceiling — the group's total or each member — see what it has consumed, check a counter against the ledger (Quota health, with Reconcile) or read a rule's reset history (resetting itself is done under Quota usage) Quotas Quotas, Group rules: the total, or each member, Quota health and reconcile
see each user's quotas: which apply, how much of each they have used, the limit and when it resets, and reset any quota on the record, with or without a reason: one user's count of a per-user quota, a user's own quota, or a shared total for everyone it counts Quota usage Quotas
decide what is stripped from prompts before a provider sees them, and for whom Redaction Redaction
add a provider, its credentials and what it reports Providers Providers and plugins
change what models are on offer, who may use them and what they cost, and name a model as your clients should show it Models Three cost figures, A model's name and its display name
choose the search backends, set what a search costs per request (0 for billed out of band), and see how many searches each group spent Web search Surfaces, Web search: a price per request
offer a model under a name of your own, and change what that name serves without any client changing Routing Model aliases
add, disable, reset or merge a person Users Bundled accounts, Identity
see at a glance who may use only the chat Users (the Chat only badge) Chat-only people
decide who belongs together, what a group may use, and who holds coding agents or API keys Groups (the two Capabilities toggles) Groups, Chat-only people
map or dismiss the group names your identity provider reports Groups, Groups from your identity provider Mapping the provider's groups
configure mail, read the identity provider, decide who may become a user, set what new groups start with, or turn a capability on or off for every group Settings Identity
see what happened to a person's account, and who did it Users → Edit → Activity The audit trail
see what administrators changed in models, prices, aliases, grants, providers and quota rules, and who did it Audit log The administrator audit log

Two things on the Settings screen are worth knowing before you look for them. The identity provider is read-only: it is set in .env and rewritten at every start (One provider, set in the environment), so the console shows it and offers User sync… only for an external provider (User sync). What the console does edit is the provisioning policy: who may come to exist, which claim names their groups, what an IdP group means here, and what new groups start with (Chat-only people).

The Models screen's Discover button reads a provider's catalogue and offers its models for import. A model the provider lists without a price is listed too, marked No price yet: tick it and a price field opens on the row — per million tokens, per page for an OCR model, or per minute of audio for a transcription model — and typing one there is what makes the row importable; the import records it in the price history as a price entered by hand. A chat row also takes optional cache read and cache write rates; left empty, cached tokens are billed at the input rate. If the provider (or the community file) turns out to publish a price after all, theirs is the one that is kept and the result says your figure was not used. See Importing models from a provider's catalogue.

A model's Features (on the model page, and when you add one by hand) are what it can do. Images in tool results is the gateway's own: it lets the chat show the model page images that come back from a tool, for example the slides of a document, as users of a vision model expect. It is available only when the model accepts images (Accepts: image), and it starts ticked for every model that does: for models that already existed when it arrived, for models imported from a provider's catalogue, and for a model you add by hand once you tick image input. Turn it off for a model that ignores or mishandles images placed after a tool result. The chat reads the setting from supported_features on GET /v1/models, and the change is recorded in the audit log. Removing it from a model stays removed: the catalogue is read only when a model is created.

Reasons are optional. Every form that offers one (resetting a quota, merging users, a redaction rule, the sign-in policy) labels the field Reason (optional); a reason you give is kept in the audit log.

A non-administrator who follows a bookmarked admin link is told they are not one, rather than shown a blank page; the API refuses them regardless.

The administrator audit log

The Audit log screen (GET /api/admin/audit, administrators only) lists what administrators changed in configuration, newest first: when, who (their email at the time), the action, the target, and an expandable before and after for the fields that changed. It is separate from the identity audit trail, which records who exists and who may sign in.

Recorded actions: model.create|update|delete (including models imported from a catalogue), price.create, alias.create|update|delete, grant.group.add|remove and grant.user.add|remove (only when a grant really changed, so a repeated request records nothing), provider.create|update|delete and limit.create|update|delete (quota rules). Each event is written in the same transaction as the change, so a change that fails leaves no event.

Credentials are never recorded. A provider's API key shows only as api_key: set, rotated or cleared; its extra headers show as header names, with extra_headers_values: rotated when only a value changed; any credential embedded in a base URL is stripped. Aliases still also write to the identity events, as before.

Filters: from and to (a day, inclusive), administrator (their id, or part of their email), action prefix (grant. lists every grant change) and target type. Rows are kept indefinitely: there is no retention setting or purge.

Translations

The console's words live in one catalogue, apps/console/messages/en.json: every button, heading, notice and placeholder on every screen is read from there, and none is written in the screen's own file. English is the only language that ships; the machinery exists so that adding one is a file, not a change to twenty screens.

Adding a language is three steps:

  1. Copy messages/en.json to messages/<locale>.json (say, messages/de.json) and translate the values. The keys and the file's shape stay exactly as they are; a {placeholder} or a {#tag}…{/tag} inside a value is syntax, not text, and must survive the translation where it is.
  2. Add the locale to locales in apps/console/project.inlang/settings.json.
  3. Run pnpm --filter @llmp/console i18n:check. It fails on a key the code uses that the new file is missing, and on a message whose placeholders do not match the English one, before anything ships.

The language a reader gets is picked in order: an explicit choice stored in the browser (localStorage["console.locale"]), then the browser's own preferred language if the catalogue has it, then English. Setting the key by hand — say, localStorage.setItem("console.locale", "de") in the console's devtools — is how you try a translation that has no menu yet.

Finding what is not yet translated is what en-XA is for. It is a pseudo-locale generated from the English catalogue (pnpm --filter @llmp/console i18n:pseudo): every message comes back accented and wrapped in brackets, with the placeholders untouched. Set localStorage["console.locale"] to en-XA, reload, and walk the screens: a string still reading as plain English is one the catalogue does not carry yet. It is a developer tool — no reader can arrive at it by accident, and no menu offers it.

pnpm --filter @llmp/console i18n:check is the guard, in CI with the console's own checks: it fails on a key used in code that en.json does not have, on en-XA.json being out of date with en.json, and on a screen that has been converted carrying English text in its JSX again.