Skip to content

Commit 9a142d3

Browse files
committed
init
Signed-off-by: JaredforReal <[email protected]>
1 parent ea0c6ea commit 9a142d3

File tree

16 files changed

+677
-2
lines changed

16 files changed

+677
-2
lines changed

Dockerfile.extproc

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,16 @@ FROM golang:1.24 AS go-builder
5757

5858
WORKDIR /app
5959

60+
ENV GOPROXY=https://goproxy.cn,direct
61+
ENV GOSUMDB=sum.golang.google.cn
62+
6063
# Copy Go module files first for better layer caching
6164
RUN mkdir -p src/semantic-router
6265
COPY src/semantic-router/go.mod src/semantic-router/go.sum src/semantic-router/
6366
COPY candle-binding/go.mod candle-binding/semantic-router.go candle-binding/
6467

65-
# Download Go dependencies (cached layer)
66-
RUN cd src/semantic-router && go mod download
68+
RUN cd src/semantic-router && go mod download && \
69+
cd /app/candle-binding && go mod download
6770

6871
# Copy semantic-router source code
6972
COPY src/semantic-router/ src/semantic-router/

dashboard/README.md

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
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.

dashboard/RISKS.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Dashboard Risks & Mitigations
2+
3+
## 1. Grafana iframe authentication / embedding issues
4+
**Risk:** Grafana may block embedding (X-Frame-Options / login redirect).
5+
**Mitigation:** Set `GF_SECURITY_ALLOW_EMBEDDING=true` and ensure anonymous or proxy auth (future OIDC). Proxy strips frame-busting headers.
6+
7+
## 2. Open WebUI CSP restrictions
8+
**Risk:** OWUI might set restrictive CSP preventing embedding.
9+
**Mitigation:** Proxy sanitizes CSP `frame-ancestors``'self'`; if OWUI sets strict CSP hashes, consider upstream config to relax embedding for dashboard origin.
10+
11+
## 3. Token leakage via iframes
12+
**Risk:** Authorization headers forwarded to upstream frames could be exposed if scripts inside frames enumerate requests.
13+
**Mitigation:** Only forward auth to Router API and Open WebUI if required; never forward to Grafana/Prometheus until proper per-service auth is in place.
14+
15+
## 4. Clickjacking / framing external content
16+
**Risk:** Allowing arbitrary path inputs to Grafana iframe could be abused.
17+
**Mitigation:** Constrain user-provided dashboard paths to begin with `/d/` (future validation) and sanitize input; keep `frame-ancestors 'self'` CSP.
18+
19+
## 5. CORS / mixed-origin surprises
20+
**Risk:** Subresources inside iframes may attempt cross-origin calls failing silently.
21+
**Mitigation:** Keep reverse proxy paths stable; prefer embedding fully proxied origins instead of directly exposing upstream hostnames externally.
22+
23+
## 6. Service discovery drift across environments
24+
**Risk:** Namespace or service name changes break baked-in K8s URLs.
25+
**Mitigation:** Move target URLs to a ConfigMap / Helm values; document override env vars clearly.
26+
27+
## 7. Performance overhead of naive reverse proxy
28+
**Risk:** High-frequency panel refreshes add latency.
29+
**Mitigation:** Use `httputil.ReverseProxy` (already streaming); enable gzip (future), benchmark under load, optionally bypass proxy for static Grafana assets.
30+
31+
## 8. CSP collisions
32+
**Risk:** Upstream CSP modifications may degrade dashboard security or functionality.
33+
**Mitigation:** Maintain allowlist for directives modified; log when CSP is rewritten (future logging hook) and optionally provide strict mode.
34+
35+
## 9. Metrics endpoint redirection
36+
**Risk:** Simple redirect leaks upstream internal URL structure externally.
37+
**Mitigation:** Replace redirect with proxy stream (`/metrics/router` → direct fetch) in hardened mode; redact sensitive labels before exposing aggregated metrics.
38+
39+
## 10. Future OIDC / session fixation complexity
40+
**Risk:** Introducing OIDC later can complicate existing reverse proxy assumptions.
41+
**Mitigation:** Design backend with middleware chain; isolate auth concerns so OIDC handler can wrap existing proxies without rewriting core logic.
42+
43+
## 11. Log noise & PII in headers
44+
**Risk:** Proxy logging might capture sensitive headers or tokens.
45+
**Mitigation:** Add structured logger with header redaction list (Authorization, Cookie) before enabling detailed access logs.
46+
47+
## 12. Multi-tenant expansion
48+
**Risk:** Scaling to multi-tenant contexts requires per-tenant config segregation.
49+
**Mitigation:** From start, keep config viewer read-only and plan `/api/session/context` endpoint to inject tenancy scope for future RBAC.
50+
51+
## 13. Version drift between dashboard and router API
52+
**Risk:** Frontend expects endpoints not present in older router versions.
53+
**Mitigation:** Add a `/api/router/api/v1` capability handshake; feature-detect endpoints before rendering advanced UI elements.
54+
55+
## 14. Large config JSON rendering performance
56+
**Risk:** Extremely large classification configs freeze UI.
57+
**Mitigation:** Add lazy chunking or collapsible sections; enforce size limit with warning (future enhancement).
58+
59+
## 15. Security scanning & supply chain
60+
**Risk:** Distroless image reduces surface but still needs SBOM & vulnerability checks.
61+
**Mitigation:** Add CI step with `trivy` or `grype` scanning dashboard image.
62+
63+
---
64+
This list will evolve as auth (OIDC), aggregation endpoints, and RBAC are implemented.

dashboard/backend/.gitkeep

Whitespace-only changes.

dashboard/backend/Dockerfile

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
FROM golang:1.24 AS builder
2+
WORKDIR /app
3+
# Copy only module files first for layer caching
4+
COPY dashboard/backend/go.mod dashboard/backend/go.sum ./dashboard/backend/
5+
WORKDIR /app/dashboard/backend
6+
RUN go mod download
7+
# Copy source and frontend assets
8+
COPY dashboard/backend/ ./
9+
COPY dashboard/frontend/ /app/dashboard/frontend/
10+
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /app/dashboard-backend main.go
11+
12+
FROM gcr.io/distroless/static:nonroot
13+
WORKDIR /app
14+
COPY --from=builder /app/dashboard-backend /app/dashboard-backend
15+
COPY --from=builder /app/dashboard/frontend /app/frontend
16+
EXPOSE 8700
17+
USER nonroot:nonroot
18+
ENTRYPOINT ["/app/dashboard-backend", "-port=8700", "-static=/app/frontend"]

dashboard/backend/go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/vllm-project/semantic-router/dashboard/backend
2+
3+
go 1.21

0 commit comments

Comments
 (0)