Dashboard API Reference
Base URL: http://192.168.56.13:31880
The OpenAPI description is GET /api/v1/openapi.json, for any logged-in user.
Authentication
Every route requires a Keycloak access token for the dashboard realm client:
Authorization: Bearer <access token>
Reads need the dashboard-viewer or dashboard-admin role, writes need
dashboard-admin. Three routes answer without a token: /health,
/api/v1/cluster/info and /api/v1/apps/public. The backend refuses to start with
any other route that has no role. See security/iam.md
for the full matrix. In the tables below, "—" means any logged-in role.
Health
| Method |
Path |
Auth |
Description |
| GET |
/health |
— |
Backend liveness check |
Pods & Deployments (Control Room)
| Method |
Path |
Auth |
Description |
| GET |
/api/v1/pods |
— |
List pods. Query: ?namespace=5g |
| GET |
/api/v1/pods/{pod}/describe |
— |
Describe a pod. Query: ?namespace=5g |
| POST |
/api/v1/deployments/{deployment}/restart |
✅ Admin |
Rolling restart a deployment. Query: ?namespace=5g |
ConfigMaps
| Method |
Path |
Auth |
Description |
| GET |
/api/v1/configmaps/{name} |
— |
Read a ConfigMap. Query: ?namespace=5g |
| PUT |
/api/v1/configmaps/{name} |
✅ Admin |
Update a ConfigMap. Requires DASHBOARD_ALLOW_CONFIGMAP_WRITE=true |
Topology Map
| Method |
Path |
Auth |
Description |
| GET |
/api/v1/topology |
— |
Pod nodes + OVS bridges with Multus interface metadata. Query: ?namespace=5g |
| GET |
/api/v1/network/nads |
— |
List NetworkAttachmentDefinitions. Query: ?namespace=5g |
| GET |
/api/v1/ovs/bridges/{bridge}/flows |
— |
OVS OpenFlow rules for a bridge (from ovs-ofctl dump-flows) |
Subscriber Management
| Method |
Path |
Auth |
Description |
| GET |
/api/v1/subscribers |
— |
List all Open5GS subscriber records |
| POST |
/api/v1/subscribers |
✅ Admin |
Create a subscriber. Body: subscriber object |
| PUT |
/api/v1/subscribers/{imsi} |
✅ Admin |
Update a subscriber |
| DELETE |
/api/v1/subscribers/{imsi} |
✅ Admin |
Delete a subscriber |
| POST |
/api/v1/subscribers/init |
✅ Admin |
Trigger Ansible subscriber import (reset to defaults) |
UE Monitoring
| Method |
Path |
Auth |
Description |
| GET |
/api/v1/ue/summary |
— |
Prometheus-backed gauges: gNBs, RAN UEs, PDU sessions, registration counts |
| GET |
/api/v1/ue/events |
— |
Log-parsed UE events. Query: ?minutes=10 |
| GET |
/api/v1/ue/active |
— |
Active UE list reconstructed from AMF/SMF logs |
| GET |
/api/v1/ue/pods |
— |
UERANSIM UE pod list |
| POST |
/api/v1/ue/test/ping |
✅ Admin |
Run ping from a UE pod. Body: {pod, namespace, target_ip} |
| POST |
/api/v1/ue/test/iperf |
✅ Admin |
Run iperf3 from a UE pod. Body: {pod, namespace, target_ip, duration} |
Metrics
| Method |
Path |
Auth |
Description |
| GET |
/api/v1/metrics/nodes |
— |
Node-level CPU, memory, disk from Prometheus/Node Exporter |
| GET |
/api/v1/metrics/nf |
— |
Per-NF CPU, memory, restart counts |
RAN
Admin only (reads included). Attach and detach are pieces: POST /api/v1/pieces/ran_attach/run
and POST /api/v1/pieces/ran_detach/run (see Pieces and operations).
| Method |
Path |
Auth |
Description |
| GET |
/api/v1/ran/status |
✅ Admin |
The physical RAN's state: the worker's RAN interface and its link, br-ran and its ports, the RAN network attachment, the AMF's RAN interface, the UPF's route back, the gNB (address, NG Setup, NGAP port), UE and PDU counts, and chain, the five links the RAN page shows (serving, broken with the first broken link and its fix, or detached) |
| GET |
/api/v1/ran/modes/status |
✅ Admin |
The same for the physical RAN, plus the UERANSIM workloads, for the RAN page |
| POST |
/api/v1/ran/modes/ueransim/enable, .../disable |
✅ Admin |
UERANSIM on or off. Not offered by the page: the phase that installs it is not maintained |
| GET, POST, PATCH, DELETE |
/api/v1/ran/ueransim/... |
✅ Admin |
UERANSIM gNBs and UEs (status, defaults, forms, create, change, activate, delete) |
Network Health
| Method |
Path |
Auth |
Description |
| GET |
/api/v1/network/health |
— |
N-interface connectivity results (N2/N3/N4/N6): the last run when it is younger than 25 s, otherwise a new run |
| POST |
/api/v1/network/health/run |
— |
Run the checks now |
Status
| Method |
Path |
Auth |
Description |
| GET |
/api/v1/status/summary |
— |
Overall state for the header: state (ok, warn, error) and problems (area, text, severity). Sources: nodes not Ready; 5G pods Failed, Pending, Running but not ready, or stuck (CrashLoopBackOff, image pull errors); the last network check run if younger than 10 min, with its age (never starts one); the AMF CNI alert. A source that cannot be read is a warn problem; an unreachable Kubernetes API is the single error problem |
Pieces and operations
Admin only. A piece is a named part of a phase playbook (ansible/pieces.yml),
run by the runner on the ansible VM; see
contributing.md.
| Method |
Path |
Auth |
Description |
| GET |
/api/v1/pieces |
✅ Admin |
The registry: per piece title, tier (change or disrupt), changes, stops, takes_s |
| POST |
/api/v1/pieces/{name}/run |
✅ Admin |
Start a piece. Returns {id, state}; a piece already running returns the running one's id. 404 for an unknown piece |
| GET |
/api/v1/operations |
✅ Admin |
The 50 newest runs' records, ?state= to filter (running, done, failed, interrupted). Each has reads_back (its piece has a read-back); a stored failed read-back is returned as failed |
| GET |
/api/v1/operations/retention |
✅ Admin |
The record's limits and what it holds: max_age_days, max_mb, runs, bytes, oldest |
| PUT |
/api/v1/operations/retention |
✅ Admin |
Set the limits. Body {max_age_days, max_mb}, days 1 to 3650, MB 1 to 10240: 400 for a number outside them, 422 for a value that is not a whole number. Returns the same shape as GET. Audit-logged |
| GET |
/api/v1/operations/{id} |
✅ Admin |
One run: steps, the tail of its log, and its read-back (check), made once within 600 s of the end; a failed read-back makes the state failed. 404 for an unknown id |
Isolation
| Method |
Path |
Auth |
Description |
| GET |
/api/v1/isolation/planes |
— |
Plane filter counters per rule over ?window= (default 24h, max 7d): mode, planes, every pair with verdict and packets, drops outside the planes |
| GET |
/api/v1/isolation/planes/samples |
— |
Latest packets the filter logged on the worker, per rule (?limit=, default 5), last 24 h |
| GET |
/api/v1/isolation/policies |
— |
Per isolated namespace: role, allowed sources per app, egress limits, policy names; unlisted namespaces; common sources |
| GET |
/api/v1/isolation/targets |
— |
Sources and destinations (Services with ports) for the flow check |
| POST |
/api/v1/isolation/check |
— |
Evaluate a flow on the live NetworkPolicies without sending it. Body: {"source": "mec", "destination": {"namespace": "5g", "service": "mongodb", "port": 27017}} or {"destination": {"internet": true}} |
Northbound (positioning / CAMARA)
| Method |
Path |
Auth |
Description |
| GET |
/api/v1/northbound/services |
— |
Inventory of the camara/positioning/mec deployments (image, ready replicas, pods) |
| GET |
/api/v1/northbound/adapters |
— |
Live adapter registry from the engine (GET /adapters via the API-server service proxy): per adapter kind, registered_via, last_seen_s_ago, and derived state (live/unreachable/stale) |
| GET |
/api/v1/northbound/contract |
— |
Adapter contract guidance: Measurement schema, Python skeleton, env.contract.yaml template, doc links |
| GET |
/api/v1/northbound/contract/{service} |
— |
Live per-service contract fetched from the service's own /contract (kind, external_origin var, required/recommended/optional env). Degrades to {available: false} when the service exposes none |
| GET |
/api/v1/northbound/config/{service} |
— |
Guided-setup read: the contract plus current state (non-sensitive values; sensitive reported set/unset only, never the value) |
| PUT |
/api/v1/northbound/config/{service} |
✅ Admin |
Guided-setup apply. Body: {values: {VAR: value}}. Routes each var by the contract sensitive flag (Secret vs ConfigMap, both via envFrom), then rolls the deployment |
| DELETE |
/api/v1/northbound/adapters/{name} |
✅ Admin |
Force-remove a stale registry entry (engine DELETE /adapters/{name}). Adapters self-register, so there is no manual register endpoint |
| POST |
/api/v1/northbound/deploy |
✅ Admin |
Deploy a custom adapter image. Requires DASHBOARD_ALLOW_WORKLOAD_CREATE=true. Body: {name, image, port, env[], image_pull_secret?, kind?}. The backend injects the self-registration env so the adapter announces itself to the engine |
| DELETE |
/api/v1/northbound/workloads/{name} |
✅ Admin |
Delete a deploy-from-image adapter (Deployment, Service, Secret) and unregister it |
| GET |
/api/v1/northbound/assets |
✅ Admin |
Asset Identity Map from the gateway (GET /assets); Bearer forwarded |
| PUT |
/api/v1/northbound/assets |
✅ Admin |
Replace the Asset Map (PUT /assets); Bearer forwarded. The engine broadcasts each device from its adapter's capability, so onboarded assets go live without a track-list sync |
| GET |
/api/v1/northbound/assets/discoverable |
✅ Admin |
Gateway discoverable candidates (not yet onboarded) |
| GET |
/api/v1/northbound/assets/{asset_id}/details |
✅ Admin |
Gateway per-asset details |
Edge apps (phase 12)
Operator-deployed application pods in the mec namespace; the local registry lives in the registry namespace. See architecture/edge-apps.md.
| Method |
Path |
Auth |
Description |
| GET |
/api/v1/apps |
— |
Inventory of deployed apps (name, image, replicas, ready, exposed, public URL kelt-<name>.<base>, mec_attached, mec_ip, pods for log streaming) |
| GET |
/api/v1/apps/public |
— |
Unauthenticated (no token even in auth mode): names + public URLs of exposed apps, for the front-door welcome page |
| GET |
/api/v1/apps/updates |
— |
Per app: whether the registry digest for its tag is newer than the running pod's |
| POST |
/api/v1/apps |
✅ Admin |
Deploy an app. Requires DASHBOARD_ALLOW_WORKLOAD_CREATE=true. Body: {name, image, port?, replicas?, env[], image_pull_secret?, expose?, attach_mec?, mec_ip?, udp_ports[]}. Creates a Deployment (+ Service on port 80 when exposed) pinned to the worker; attach_mec adds an N6m interface (n6m-net, or n6m-static when mec_ip is set) so UEs reach it over the user plane |
| PUT |
/api/v1/apps/{name}/image |
✅ Admin |
Retarget a deployed app to a chosen registry tag (date-ordered picker), preserving its other settings. Body: {image}. Requires DASHBOARD_ALLOW_WORKLOAD_CREATE=true |
| DELETE |
/api/v1/apps/{name} |
✅ Admin |
Delete an app (Deployment, Service, config ConfigMap/Secret) |
| GET |
/api/v1/apps/registry-credentials |
✅ Admin |
Local-registry host + basic-auth credentials, so an admin can docker login and push |
| GET |
/api/v1/apps/registry/images |
✅ Admin |
Repos and tags in the local registry, each tag with its build timestamp (newest first) |
| GET |
/api/v1/apps/starter-kit |
✅ Admin |
Zip (README + .env.example + deploy.sh) prefilled with the registry host, for an app developer |
| POST |
/api/v1/apps/provision |
✅ Admin |
Deploy the platform from the UI (phases 12 + 11), streaming progress as NDJSON |
| GET |
/api/v1/apps/gnb/console |
— |
gNB management console state (configured, origin host:port, public URL) |
| PUT |
/api/v1/apps/gnb/console |
✅ Admin |
Expose the gNB web UI at kelt-gnb.<base>. Body: {host, port?}. Registers a selectorless Service+Endpoints gnb reached via the dynamic apps route. Requires DASHBOARD_ALLOW_WORKLOAD_CREATE=true |
| DELETE |
/api/v1/apps/gnb/console |
✅ Admin |
Remove the gNB console Service+Endpoints |
Storage
Node disk usage and the reclaim actions. Sizes are measured by walking the filesystem, so they are cached; the filesystem totals are always live. See modules.md.
| Method |
Path |
Auth |
Description |
| GET |
/api/v1/storage |
— |
Worker disk breakdown: filesystem totals, containerd image layers vs blobs, persistent volumes (total and per claim, with namespace), the in-cluster registry as a subset of those, and journald. ?refresh=true re-walks instead of using the cache |
| GET |
/api/v1/storage/preview |
— |
Estimated saving per reclaim action: unused image count and bytes, and how far journald sits over its cap. Estimates, not promises: shared image layers are counted once per image, so the real saving is usually a little lower |
| POST |
/api/v1/storage/prune-images |
✅ Admin |
Remove image layers no container references. Returns bytes freed and free space after |
| POST |
/api/v1/storage/vacuum-journal |
✅ Admin |
Trim systemd journals to the 500M cap phase 01 configures |
| POST |
/api/v1/storage/registry-gc |
✅ Admin |
Garbage-collect unreferenced blobs in the in-cluster registry. ?dry_run=true (default) reports what would go, with the total size of those blobs, without touching the store |
IAM
Convenience endpoint behind the Identity & Access settings page. See iam.md.
| Method |
Path |
Auth |
Description |
| GET |
/api/v1/iam/master-admin-password |
✅ Admin |
Reveal the Keycloak master admin password (read from .testbed.secrets). Read-only: this credential is automation-owned and auto-generated, surfaced only so an operator can sign in to the master console without opening the host file. Audit-logged |
| GET |
/api/v1/iam/client-secret/{client_id} |
✅ Admin |
Reveal the client secret of a seeded M2M client (camara-gateway, camara-api-demo, dashboard-readonly), read from .testbed.secrets on the host. Admin-only despite being a GET; every reveal is audit-logged. Returns found: false when the key is not in the file (the realm then holds its changeme default) |
| POST |
/api/v1/iam/client-secret/{client_id}/rotate |
✅ Admin |
Rotate that client's secret: generates a random value, writes .testbed.secrets first (the source of truth), then converges Keycloak; for camara-gateway also patches the gateway pod env and rolls it. Audit-logged; returns the new secret. On a half-failure the file already holds the new value and kelt run-phase 08-iam (or rotating again) converges Keycloak to it. See iam.md "Secret rotation" |
WebSocket Endpoints
WebSocket connections are made to the same host on port 31880.
Log streaming
WS /api/v1/ws/logs/{namespace}/{pod}?container=<name>
Streams live container logs. Equivalent to kubectl logs -f. The server closes the connection when the pod disappears or the client disconnects.
Messages (server → client): JSON {line: "...", timestamp: "..."} per log line.
Traffic intensity
WS /api/v1/ws/traffic/intensity
Streams real-time OVS bridge counter deltas every second.
Messages (server → client):
{
"bridges": {
"br-n2": {"rx_packets": 42, "tx_packets": 38, "rx_bytes": 5400, "tx_bytes": 4800},
"br-n3": {"rx_packets": 120, ...},
...
},
"timestamp": "2026-03-11T10:00:01Z"
}
Planned / Stubbed
| Method |
Path |
Description |
| POST |
/api/v1/experiments/run |
Run a test scenario |
| POST |
/api/v1/snapshot/create |
Create a testbed state snapshot |