Observability and Operations
What the server tells you about itself, where, and how to feed it into the monitoring you already run. Everything here is on by default and local; exporting is the opt-in.
1The Operate sections#
The admin console's Operate group is the live view:
| Section | What it answers |
|---|---|
| Dashboard | Is it healthy right now: active inferences, requests, jobs, queue depth, loaded models, storage, and server controls |
| Requests | Every API call, attributed to its key: route, status, timing; filter and drill into one request |
| Logs | The server log, live, with level filtering |
| Alerts | Conditions that deserve attention, stated in plain words |
| Metrics | Rates and latencies over time |
| Jobs | Long-running work (indexing, downloads, training) with progress and outcomes |
Every Operate section is THIS node's view: the registries and the stores behind them (request trail, metrics, telemetry, forensics, logs) are per node, and the admin footer names the node serving the page, so behind a load balancer you always know whose story you are reading; the fleet-wide picture is the export path in section 4 (Scaling Out).
Two quieter records complete the picture: the activity log notes every operator-relevant
change (a key minted, a grant changed, a setting saved), and after an abnormal exit the
dashboard shows an unexpected-restart report reconstructing what the dying process recorded,
so a crash is a lead instead of a mystery. Alerts are evaluated on the node's own clock every
few seconds, whether or not a dashboard is open; each new alert is also a line of the activity
log (source alert) and, on a farm, part of the node's heartbeat summary.
2Attribution#
Requests are attributed to the API key that made them, so "who is generating this load" is a filter, not an investigation. Per-key use counts and last-used timestamps surface in the Access section beside each key; the anonymous local caller shows as such.
Statistics come in two tiers. Everything in the Operate sections is one node's own and its head
says which node; on a farm each node also writes a sixty-second rollup (requests per second,
error share, saturation, slots, queue depth, resident models, alerts) into its heartbeat row,
and the Farm page reads every node's row from the shared identity store
(Farm Operations). Two trails exist: the activity journal is this
node's own, and the security trail is domain-wide, one table every node appends to, bounded by
Security:TrailRetentionDays (365 by default, 0 keeps every entry) and swept once an hour by
the one node holding the retention lease.
3Health for machines#
Three routes, one job each, all answering GET and HEAD:
GET /healthis liveness for uptime probes, without authentication ceremony:200while the process is alive, with the product version, the engine version and a boot id that changes on every start (so a client that asked for a restart can prove it happened). It stays200while the node warms or drains, so it never decides routing.GET /lmkit/v1/health/readyis readiness for any prober, anonymous and status-only:200withreadyonce the inference backend is initialized and every configured warmup model is loaded,503withnot_readyuntil the backend is up,warmingwhile the lineup loads, anddrainingthe moment a graceful stop begins. No counts, no model names, no version: it exposes nothing a health check on a bare URL should not see, which is why a platform health check, a balancer probe or a HEAD-only monitor gates traffic on it.GET /lmkit/v1/readyis the same decision with the detailed body, behind a bearer token because it exposes runtime state:status(the same four words),backend_initialized,loaded_models(count),loaded_model_names,active_inferences,queue_depth,draining,node(this node's tag, the oneX-LMKit-Nodecarries),version,mode(single_node, orfarmwhen the node shares an identity database with its siblings),slots_saturation(section 4 defines it),fleet_signal(present only on a fleet member, naming why: Farm mode, managed configuration, a shared identity store) and, only while warmup gates readiness,warmup_pendingandwarmup_failed.
GET /lmkit/v1/search/status answers the same honestly for the Search feature, per caller
(see the Access Model).
4Exporting to your stack#
The Telemetry export section wires the server into external monitoring; both paths carry the same measurements the built-in panels show:
- OpenTelemetry (OTLP): push traces and metrics to a configured endpoint (a collector, or any OTLP-speaking backend).
- Prometheus: enable the scrape endpoint and point your Prometheus at it.
The first-party gauges include the one an autoscaler acts on,
lmkit_inference_slots_saturation: busy completion slots over total completion slots across
the node's resident chat models (embedding pools excluded), from 0 to 1, falling back to
active completions over the completion ceiling before any pool exists. It is one number with
four readers, this gauge, the dashboard's slots tile, the slots_saturation field of
/lmkit/v1/ready and the heartbeat row a farm reads, so they never disagree. Pressure shows there BEFORE a queue forms;
lmkit_inference_queue_depth and lmkit_inference_active sit beside it. Three gauges exist
for a fleet dashboard: lmkit_build_info (always 1, with version, engine and node
labels, so version skew across nodes is one query), lmkit_ready_state (1 when the node
answers ready, 0 otherwise, with the status word as a label) and lmkit_model_resident (one
series per resident model, labelled model). Every gauge is per node: a fleet autoscaler
averages the saturation across nodes (the KEDA example in
Running in Containers does exactly that), and exporting is how the
per-node panels become one fleet-wide view. Four queries give the farm view:
avg(lmkit_inference_slots_saturation) for the load an autoscaler acts on,
count by (version) (lmkit_build_info) for version skew, sum(lmkit_ready_state) for how many
nodes take traffic, and count by (model) (lmkit_model_resident) for where each model lives. The
one record that is already domain-wide without any export is the security source of the
audit history: on a shared identity store, sign-ins, account changes and credential events
from every node land in the database, and that source reads the whole domain
(Operator Accounts).
Exporting is off by default: local observability costs nothing and sends nothing.
5Stated plainly#
- The console answers "what is happening" on this node without any external dependency; the footer names the node.
- Every request has an owner; every operator change has a record; every crash leaves a report.
/healthis liveness;/lmkit/v1/health/readytells the truth about warming and draining to any prober, and/lmkit/v1/readyadds the detail behind a bearer.- External monitoring is one section away, in the two formats the ecosystem actually speaks, and it is how a fleet reads itself as one.