|
| 1 | +# Semantic Router Modern Dashboard |
| 2 | + |
| 3 | +Unified dashboard that brings together Configuration Management, an Interactive Playground, and Real-time Monitoring & Observability. It provides a single entry point across local, Docker Compose, and Kubernetes deployments. |
| 4 | + |
| 5 | +## Goals |
| 6 | + |
| 7 | +- Single landing page for new/existing users |
| 8 | +- Embed Observability (Grafana/Prometheus) and Playground (Open WebUI) via iframes behind a single backend proxy for auth and CORS/CSP control |
| 9 | +- Read-only configuration viewer powered by the existing Semantic Router Classification API |
| 10 | +- Environment-agnostic: consistent URLs and behavior for local dev, Compose, and K8s |
| 11 | + |
| 12 | +## What’s already in this repo (reused) |
| 13 | + |
| 14 | +- Prometheus + Grafana |
| 15 | + - Docker Compose services in `docker-compose.yml` (ports: Prometheus 9090, Grafana 3000) |
| 16 | + - Local observability in `docker-compose.obs.yml` (host network) |
| 17 | + - K8s manifests under `deploy/kubernetes/observability/{prometheus,grafana}` |
| 18 | + - Provisioned datasource and dashboard in `tools/observability/` |
| 19 | +- Router metrics and API |
| 20 | + - Metrics at `:9190/metrics` (Prometheus format) |
| 21 | + - Classification API on `:8080` with endpoints like `GET /api/v1`, `GET /config/classification` |
| 22 | +- Open WebUI integration |
| 23 | + - Pipe in `tools/openwebui-pipe/vllm_semantic_router_pipe.py` |
| 24 | + - Doc in `website/docs/tutorials/observability/open-webui-integration.md` |
| 25 | + |
| 26 | +These are sufficient to embed and proxy—no need to duplicate core functionality. |
| 27 | + |
| 28 | +## Architecture (MVP) |
| 29 | + |
| 30 | +- frontend/ (SPA) |
| 31 | + - Tabs: Monitoring, Config Viewer, Playground |
| 32 | + - Iframes for Grafana dashboards and Open WebUI |
| 33 | + - Simple viewer for router config JSON |
| 34 | +- backend/ (Go HTTP server) |
| 35 | + - Serves static frontend |
| 36 | + - Reverse proxy with auth/cors/csp controls: |
| 37 | + - `GET /embedded/grafana/*` → Grafana |
| 38 | + - `GET /embedded/prometheus/*` → Prometheus (optional link-outs) |
| 39 | + - `GET /embedded/openwebui/*` → Open WebUI (optional) |
| 40 | + - `GET /api/router/*` → Router Classification API (`:8080`) |
| 41 | + - `GET /metrics/router` → Router `/metrics` (optional aggregation later) |
| 42 | + - Normalizes headers for iframe embedding: strips/overrides `X-Frame-Options` and `Content-Security-Policy` frame-ancestors as needed |
| 43 | + - Central point for JWT/OIDC in the future (forward or exchange tokens to upstreams) |
| 44 | + |
| 45 | +## Directory layout |
| 46 | + |
| 47 | +``` |
| 48 | +dashboard/ |
| 49 | +├── frontend/ # UI for configuration, playground, monitoring |
| 50 | +│ ├─ Monitoring (iframe Grafana) |
| 51 | +│ ├─ Config Viewer (fetch /api/router/config/classification) |
| 52 | +│ └─ Playground (iframe Open WebUI) |
| 53 | +├── backend/ # Go proxy, auth, thin API |
| 54 | +│ ├─ /embedded/grafana → Grafana |
| 55 | +│ ├─ /embedded/prometheus → Prometheus |
| 56 | +│ ├─ /embedded/openwebui → Open WebUI |
| 57 | +│ └─ /api/router/* → Semantic Router API |
| 58 | +├── deploy/ |
| 59 | +│ ├── docker/ # Docker Compose setup for the dashboard |
| 60 | +│ ├── kubernetes/ # K8s manifests (Service/Ingress/ConfigMap) |
| 61 | +│ └── local/ # Local/dev launcher |
| 62 | +└── helm-chart/ # (optional) Helm chart for dashboard |
| 63 | +``` |
| 64 | + |
| 65 | +## Environment-agnostic configuration |
| 66 | + |
| 67 | +The backend exposes a single port (default 8700) and proxies to targets defined via environment variables. This keeps frontend URLs stable and avoids CORS by same-origining everything under the dashboard host. |
| 68 | + |
| 69 | +Required env vars (with sensible defaults per environment): |
| 70 | + |
| 71 | +- `DASHBOARD_PORT` (default: 8700) |
| 72 | +- `TARGET_GRAFANA_URL` |
| 73 | +- `TARGET_PROMETHEUS_URL` |
| 74 | +- `TARGET_ROUTER_API_URL` (router `:8080`) |
| 75 | +- `TARGET_ROUTER_METRICS_URL` (router `:9190/metrics`) |
| 76 | +- `TARGET_OPENWEBUI_URL` (optional; enable playground tab only if present) |
| 77 | +- `ALLOW_IFRAME_EMBED` (default: true; backend will remove/override frame-busting headers) |
| 78 | + |
| 79 | +Recommended upstream settings for embedding: |
| 80 | + |
| 81 | +- Grafana: set `GF_SECURITY_ALLOW_EMBEDDING=true` and prefer `access: proxy` datasource (already configured) |
| 82 | +- Open WebUI: ensure CSP/frame-ancestors allows embedding, or rely on dashboard proxy to strip/override; configure Open WebUI auth/session to work under proxied path |
| 83 | + |
| 84 | +## URL strategy (stable, user-facing) |
| 85 | + |
| 86 | +- Dashboard Home: `http://<host>:8700/` |
| 87 | +- Monitoring tab: iframe `src="/embedded/grafana/d/<dashboard-uid>?kiosk&theme=light"` |
| 88 | +- Config tab: frontend fetch `GET /api/router/config/classification` |
| 89 | +- Playground tab: iframe `src="/embedded/openwebui/"` (rendered only if `TARGET_OPENWEBUI_URL` is set) |
| 90 | + |
| 91 | +## Deployment matrix |
| 92 | + |
| 93 | +1) Local dev (router and observability on host) |
| 94 | + |
| 95 | +- Use `docker-compose.obs.yml` to start Prometheus (9090) and Grafana (3000) on host network |
| 96 | +- Start dashboard backend locally (port 8700) |
| 97 | +- Env examples: |
| 98 | + - `TARGET_GRAFANA_URL=http://localhost:3000` |
| 99 | + - `TARGET_PROMETHEUS_URL=http://localhost:9090` |
| 100 | + - `TARGET_ROUTER_API_URL=http://localhost:8080` |
| 101 | + - `TARGET_ROUTER_METRICS_URL=http://localhost:9190/metrics` |
| 102 | + - `TARGET_OPENWEBUI_URL=http://localhost:3001` (if running) |
| 103 | + |
| 104 | +2) Docker Compose (all-in-one) |
| 105 | + |
| 106 | +- Reuse services defined in root `docker-compose.yml` |
| 107 | +- Add dashboard and optional Open WebUI services in `dashboard/deploy/docker/compose.yml` |
| 108 | +- Env examples (inside compose network): |
| 109 | + - `TARGET_GRAFANA_URL=http://grafana:3000` |
| 110 | + - `TARGET_PROMETHEUS_URL=http://prometheus:9090` |
| 111 | + - `TARGET_ROUTER_API_URL=http://semantic-router:8080` |
| 112 | + - `TARGET_ROUTER_METRICS_URL=http://semantic-router:9190/metrics` |
| 113 | + - `TARGET_OPENWEBUI_URL=http://openwebui:8080` (if included) |
| 114 | + |
| 115 | +3) Kubernetes |
| 116 | + |
| 117 | +- Install/confirm Prometheus and Grafana via existing manifests in `deploy/kubernetes/observability` |
| 118 | +- Deploy dashboard in `dashboard/deploy/kubernetes/` |
| 119 | +- Configure the dashboard Deployment with in-cluster URLs: |
| 120 | + - `TARGET_GRAFANA_URL=http://grafana.<ns>.svc.cluster.local:3000` |
| 121 | + - `TARGET_PROMETHEUS_URL=http://prometheus.<ns>.svc.cluster.local:9090` |
| 122 | + - `TARGET_ROUTER_API_URL=http://semantic-router.<ns>.svc.cluster.local:8080` |
| 123 | + - `TARGET_ROUTER_METRICS_URL=http://semantic-router.<ns>.svc.cluster.local:9190/metrics` |
| 124 | + - `TARGET_OPENWEBUI_URL=http://openwebui.<ns>.svc.cluster.local:8080` (if installed) |
| 125 | +- Expose the dashboard via Ingress/Gateway to the outside; upstreams remain ClusterIP |
| 126 | + |
| 127 | +## Security & access control |
| 128 | + |
| 129 | +- MVP: bearer token/JWT support via `Authorization: Bearer <token>` in requests to `/api/router/*` (forwarded to router API) |
| 130 | +- Frame embedding: backend strips/overrides `X-Frame-Options` and `Content-Security-Policy` headers from upstreams to permit `frame-ancestors 'self'` only |
| 131 | +- Future: OIDC login on dashboard, session cookie, and per-route RBAC; signed proxy sessions to Grafana/Open WebUI |
| 132 | + |
| 133 | +## Extensibility |
| 134 | + |
| 135 | +- New panels: add tabs/components to `frontend/` |
| 136 | +- New integrations: add target env vars and a new `/embedded/<service>` route in backend proxy |
| 137 | +- Metrics aggregation: add `/api/metrics` in backend to produce derived KPIs from Prometheus |
| 138 | + |
| 139 | +## Implementation milestones |
| 140 | + |
| 141 | +1) MVP (this PR) |
| 142 | + |
| 143 | +- Scaffold `dashboard/` (this README) |
| 144 | +- Backend: Go server with reverse proxies for `/embedded/*` and `/api/router/*` |
| 145 | +- Frontend: minimal SPA with three tabs and iframes + JSON viewer |
| 146 | +- Compose overlay: `dashboard/deploy/docker/compose.yml` to launch dashboard with existing stack |
| 147 | + |
| 148 | +2) K8s manifests |
| 149 | + |
| 150 | +- Deployment + Service + ConfigMap with env vars; optional Ingress |
| 151 | +- Document `kubectl port-forward` for dev |
| 152 | + |
| 153 | +3) Auth hardening and polish |
| 154 | + |
| 155 | +- Env toggles for anonymous/off |
| 156 | +- OIDC enablement behind a flag |
| 157 | +- Metrics summary endpoint |
| 158 | + |
| 159 | +## Try it (proposed) |
| 160 | + |
| 161 | +Local with existing observability: |
| 162 | + |
| 163 | +1. Start Prometheus/Grafana on host network: |
| 164 | + |
| 165 | + - `docker compose -f docker-compose.obs.yml up -d` |
| 166 | + |
| 167 | +2. Start router natively or with Compose |
| 168 | +3. Start dashboard backend (port 8700) with env vars above |
| 169 | +4. Open `http://localhost:8700` |
| 170 | + |
| 171 | +### Docker Compose unified run (after adding dashboard overlay) |
| 172 | + |
| 173 | +``` |
| 174 | +docker compose -f docker-compose.yml -f dashboard/deploy/docker/compose.yml up --build |
| 175 | +``` |
| 176 | + |
| 177 | +The overlay builds the dashboard image using `dashboard/backend/Dockerfile` and exposes it at `http://localhost:8700`. |
| 178 | + |
| 179 | +### Rebuild only dashboard after code changes |
| 180 | + |
| 181 | +``` |
| 182 | +docker compose -f docker-compose.yml -f dashboard/deploy/docker/compose.yml build dashboard |
| 183 | +docker compose -f docker-compose.yml -f dashboard/deploy/docker/compose.yml up -d dashboard |
| 184 | +``` |
| 185 | + |
| 186 | +### Notes on Dockerfile |
| 187 | + |
| 188 | +- Multi-stage build (Go → distroless) defined in `dashboard/backend/Dockerfile`. |
| 189 | +- Standalone Go module in `dashboard/backend/go.mod` isolates dependencies. |
| 190 | +- Frontend static assets baked into the image under `/app/frontend`. |
| 191 | + |
| 192 | +### Grafana embedding |
| 193 | + |
| 194 | +- Root `docker-compose.yml` now sets `GF_SECURITY_ALLOW_EMBEDDING=true` for iframe usage. |
| 195 | +- If you need stricter policies, remove the flag and authenticate Grafana separately; the dashboard proxy will still sanitize frame headers but Grafana may block unauthenticated panels. |
| 196 | + |
| 197 | +## Notes |
| 198 | + |
| 199 | +- The website/ (Docusaurus) remains for documentation. The dashboard is a runtime operator/try-it surface, not docs. |
| 200 | +- We’ll keep upstream services untouched and do all UX unification at the proxy + SPA layer. |
0 commit comments