Skip to main content
multimind dashboard serves a self-hosted, single-page AI governance dashboard over the artifacts the SDK already produces: the compliance audit trail (AuditLog JSONL), the cost log (CostTracker JSONL), a static shadow-AI inventory scan of your project, and a guardrails config file that the guard proxy loads at startup. Everything is read from local files; the dashboard makes no external calls and nothing leaves the machine. It requires the gateway extras:

Layout

Eight views, hash-routed (#/overview, #/audit, …):
  • Overview — stat tiles (PII detections, blocked requests, total spend, models in use, budget when configured), a 30-day spend line chart, and a PII-by-type bar chart. All-time and last-24h numbers side by side.
  • Audit Trail — the parsed audit JSONL, newest first, filterable by PII type and blocked/allowed status, paginated. Blocked rows are highlighted. Records contain types, counts, and hash tags only — never raw PII.
  • Costs — chargeback table per tag with share bars, plus per-model totals (calls, input/output tokens, cost).
  • Inventory — the scan_project shadow-AI report grouped by provider, with a red banner when hardcoded API keys are found. Results are cached in-process; the Rescan button forces a fresh scan.
  • Guardrails — a no-code form (redaction strategy, block-on checkboxes for the nine PII types, budget ceiling, output scanning) that saves a validated guardrails.json. After a save the view shows the server’s apply_hint, and an “Apply to the running proxy” card holds a proxy URL input (default http://127.0.0.1:8400, remembered in the browser’s localStorage) and a Reload proxy button that POSTs /v1/guardrails/reload on that URL. Failures (proxy not running, wrong URL, or the browser blocking the cross-origin call) are shown inline with the equivalent curl command.
  • Scan Text — paste text, pick a strategy (detect only, mask, hash, remove), and POST /api/scan-text returns a findings table (type, start, end, matched text), per-type counts, and the redacted text when a strategy was chosen. Nothing is logged.
  • Evidence Report — download buttons for GET /api/evidence-report as HTML or Markdown (opened in a new tab). The Overview also links here.
  • Compliance — reads GET /v1/compliance/dashboard and GET /v1/compliance/alerts for an organization id: score, passed / failed / unverifiable counts, a per-check score trend, recent issues, and alerts. It also has a “Record a check” form that POSTs /v1/compliance/monitor with the organization fields and the text as metadata.text, shows the raw result, and refreshes the metrics. These routes are served by the MultiMind API (multimind backend, default http://127.0.0.1:8080), not by the dashboard process. The view has a backend URL input (defaults to MULTIMIND_API_BASE when set, otherwise http://127.0.0.1:8080) and an organization id input, both remembered in localStorage. When the backend is unreachable the view says so and tells you to start multimind backend. Compliance history lives in the backend’s memory, so it resets when that process restarts.
Calls from the dashboard page to the proxy or the backend are cross-origin browser requests. Both servers answer them only when their CORS allow-list includes the dashboard origin, e.g. MULTIMIND_CORS_ORIGINS=http://127.0.0.1:8501 multimind backend and MULTIMIND_CORS_ORIGINS=http://127.0.0.1:8501 multimind serve (see configuration). Without it the browser blocks the call and the view shows a curl fallback. If MULTIMIND_API_KEY is set for the dashboard, the page sends it as X-API-Key on these calls too. The frontend is dependency-free: vanilla ES modules, hand-rolled inline SVG charts, system font stack, light and dark themes via prefers-color-scheme.

Quickstart

Two terminals:
Then open http://127.0.0.1:8501. python -m multimind.dashboard works too (configured via the env vars below). Note: multimind serve writes its cost log to costs.jsonl by default (--costs-log / MULTIMIND_PROXY_COSTS_LOG). The costs view reads any CostTracker(jsonl_path=...) log, including ones from track_costs-wrapped models in your own code; pass its path with --costs-log.

Configuration

Constructor args / CLI flags override environment variables: Programmatic use:

Endpoint reference

Interactive docs at /docs (Swagger UI). All endpoints return JSON except GET /api/evidence-report (a Markdown/HTML download); only PUT /api/guardrails writes anything.

Guardrails workflow

The Guardrails view writes a JSON file with this schema:
multimind serve writes the cost log (costs.jsonl by default) so the Costs view has rows after proxied calls. To apply a guardrails change to a running multimind serve process:
  1. Edit and save in the dashboard (or PUT /api/guardrails).
  2. POST /v1/guardrails/reload on the proxy. The reload endpoint re-reads the path in the proxy’s MULTIMIND_DASHBOARD_GUARDRAILS env var (default guardrails.json in the proxy’s working directory), not the --config path, so keep the two pointing at the same file. Otherwise restart with multimind serve --config guardrails.json, which is what the dashboard’s save message suggests.
--config loads the file via ProxySettings.from_file: budget_max_cost maps to the proxy budget, file values override MULTIMIND_PROXY_* env vars, and explicit CLI flags override the file. Reload keeps the process host, port, cost log, and audit log. Budget status on the Overview compares the configured ceiling against all logged spend in the costs log. The proxy’s own Budget enforcement is per session, so treat the dashboard number as reporting, not enforcement.

Security

  • Binds 127.0.0.1 by default; only local processes can reach it.
  • Read-only over local artifacts: the sole write path is the guardrails file, and the server never calls out to any network service.
  • The underlying logs contain PII types, counts, and hash tags — never raw PII, prompts, or responses.
  • Read endpoints have no authentication. The one write endpoint (PUT /api/guardrails) can be gated by setting MULTIMIND_DASHBOARD_API_KEY; callers must then send a matching X-API-Key header. For team access, keep the default localhost bind and put a reverse proxy with TLS and auth (e.g. nginx with basic auth or your SSO proxy) in front; do not expose the port directly.