ProductDocumentationExamplesBlogRoadmapGitHubGet Started
Available

Operator UI

The /ui dashboard, REST endpoints, auth, and how to enable it with the coordinator.

The operator UI is a server-rendered web app served by the krishiv-ui crate (Askama templates + axum + vendored JS — no CDN, no third-party fetches). It is co-located with the coordinator.

Where it lives

When the coordinator is started by krishiv local or krishiv cluster:

TopologyURL
Local (krishiv local start)http://127.0.0.1:2002/ui (default)
Bare-metal cluster (krishiv cluster start)http://<http-addr>/ui (default 127.0.0.1:2002)
Kubernetes (operator CRD)Service krishiv-coordinator, port 2002, path /ui

Pages

PathPurpose
/uiMain jobs table with live updates (vendored JS, no WebSocket — 5 s poll).
/ui/healthCoordinator and executor health.
/ui/metricsScheduler-specific metrics in human-readable form.
/ui/submitSubmit-job form (used by krishiv submit workflows).

REST API

All endpoints return JSON. Pagination via ?limit=&offset= on list endpoints.

EndpointReturns
GET /api/v1/jobs?limit=&offset=List of JobSummary with id, name, state, row counts, age.
GET /api/v1/jobs/{job_id}Job detail with stages and tasks.
GET /api/v1/jobs/{job_id}/checkpointsList of valid checkpoint epochs.
GET /api/v1/executorsList of executors with slots used / total, lost count, last heartbeat.
GET /api/v1/queuesNamespace quota snapshot.
GET /api/v1/openapi.jsonOpenAPI 3.1 spec of the management API.
GET /metricsPrometheus text format.

Auth

Set KRISHIV_UI_TOKEN=<token> to require a Bearer token on the UI and all /api/v1 endpoints except /healthz. /healthz stays anonymous so liveness probes work. The CLI and daemons pass KRISHIV_COORDINATOR_BEARER_TOKEN for management calls.

Fail-closed: in production mode (KRISHIV_PRODUCTION=1), starting the coordinator without a UI token is a hard error.

Security headers

Every response carries:

  • Content-Security-Policy: script-src 'self' — no inline scripts, no third-party CDN.
  • X-Content-Type-Options: nosniff
  • X-Frame-Options: DENY — no clickjacking via iframe.

Co-location with the coordinator

The UI is built as an axum router. The coordinator spawns it:

let ui = UiState::from_shared_coordinator(shared).with_ui_bearer_token(env::var("KRISHIV_UI_TOKEN").ok());
let router = coordinator_http_router(shared).merge(krishiv_ui::router(ui));
axum::serve(listener, router).await?;

The same router serves /healthz, /readyz, /metrics, the /api/v1/* management surface, and /ui.

Embedding in another app

You can mount the UI under a sub-path of your own axum app:

let app = Router::new()
    .nest("/krishiv", krishiv_ui::router(ui_state))
    .route("/healthz", get(my_health));

See also