# Mission Control Dashboard — Product Requirements Document

> Local LLM management dashboard for macOS development machines.
> Last updated: Feb 19, 2026
>
> See also: [DASHBOARD_REVIEW.md](DASHBOARD_REVIEW.md) · [DASHBOARD_ROADMAP.md](DASHBOARD_ROADMAP.md)

---

## 1. Overview

A dark-themed, real-time dashboard built with Next.js 16 for managing a local LLM stack (Ollama + Whisper.cpp) on macOS Apple Silicon. Designed as a developer tool — no authentication, no cloud dependencies, runs entirely on localhost.

### Key Capabilities

- **Ollama management** — model listing, load/unload, pull/delete, VRAM monitoring
- **Streaming prompt interface** — send prompts with real-time NDJSON streaming, token metrics
- **Chat mode** — multi-turn conversations with system prompt, chat bubble UI
- **Model comparison** — side-by-side prompt responses from two models
- **System monitoring** — chip, RAM, disk, uptime, memory sparklines, Ollama disk footprint
- **Whisper.cpp integration** — binary/model discovery, audio transcription test
- **Extraction service health** — server-side proxy to check extraction-service (port 4005)
- **Ollama logs viewer** — collapsible terminal-style panel with color-coded log levels
- **Model management UX** — search/filter, tags, auto-load preferred model, prompt history
- **Theme support** — dark/light toggle with localStorage persistence

---

## 2. Tech Stack

| Component  | Technology                            |
| ---------- | ------------------------------------- |
| Framework  | Next.js 16 (App Router)               |
| Language   | TypeScript (strict)                   |
| React      | 19                                    |
| Styling    | TailwindCSS v4                        |
| Icons      | Lucide React                          |
| Validation | Zod                                   |
| Theme      | Dark default — ByteLyst design tokens |
| Port       | 3100 (dev)                            |

---

## 3. Architecture

```
dashboard/
├── docs/
│   ├── DASHBOARD_PRD.md           ← This file
│   ├── DASHBOARD_REVIEW.md        ← Bug/improvement audit (39 items, all complete)
│   └── DASHBOARD_ROADMAP.md       ← Next-wave implementation plan (N1–N15)
├── src/
│   ├── app/
│   │   ├── page.tsx               ← Main dashboard (1,885 lines, client component)
│   │   ├── layout.tsx             ← Root layout (dark theme, system fonts)
│   │   ├── error.tsx              ← React Error Boundary
│   │   ├── globals.css            ← Design tokens, CSS utilities, skeleton, light theme
│   │   ├── components/
│   │   │   ├── StatusDot.tsx      ← Pulsing online/offline indicator
│   │   │   ├── ProgressBar.tsx    ← Colored progress bar with percentage
│   │   │   └── Sparkline.tsx      ← SVG sparkline chart for time-series data
│   │   ├── lib/
│   │   │   ├── types.ts           ← TypeScript interfaces (OllamaData, SystemData, etc.)
│   │   │   ├── format.ts          ← formatBytes, formatUptime utilities
│   │   │   └── ollama-config.ts   ← Shared OLLAMA_URL constant
│   │   └── api/
│   │       ├── ollama/
│   │       │   ├── route.ts       ← REST proxy (list, load, unload, pull, delete, show)
│   │       │   ├── stream/route.ts ← Streaming generate proxy (NDJSON)
│   │       │   ├── chat/route.ts  ← Streaming chat proxy (multi-turn)
│   │       │   ├── pull/route.ts  ← Streaming pull with progress events
│   │       │   └── logs/route.ts  ← Ollama server log reader
│   │       ├── whisper/
│   │       │   ├── route.ts       ← Whisper binary + GGML model discovery
│   │       │   └── transcribe/route.ts ← Audio transcription via whisper-cli
│   │       ├── system/route.ts    ← System info (chip, RAM, disk, brew) with TTL cache
│   │       └── extraction/
│   │           └── health/route.ts ← Extraction service health proxy
├── package.json
├── next.config.ts
├── tsconfig.json
└── postcss.config.mjs
```

**19 source files, ~2,930 lines total.**

---

## 4. Running

```bash
cd __LOCAL_LLMs/dashboard
npm install          # first time only
npm run dev -- -p 3100
```

Open: **http://localhost:3100**

Prerequisites: Ollama running (`ollama serve` or `brew services start ollama`).

---

## 5. API Routes

### Ollama

| Method | Route                | Purpose                                        |
| ------ | -------------------- | ---------------------------------------------- |
| GET    | `/api/ollama`        | List models + running models (status, VRAM)    |
| POST   | `/api/ollama`        | Load, unload, pull, delete, show model actions |
| POST   | `/api/ollama/stream` | Streaming generate (NDJSON proxy)              |
| POST   | `/api/ollama/chat`   | Streaming chat (multi-turn, NDJSON)            |
| POST   | `/api/ollama/pull`   | Streaming pull with progress events            |
| GET    | `/api/ollama/logs`   | Read last 100 lines of Ollama log files        |

### System & Services

| Method | Route                     | Purpose                                  |
| ------ | ------------------------- | ---------------------------------------- |
| GET    | `/api/system`             | Chip, RAM (vm_stat), disk, brew, uptime  |
| GET    | `/api/whisper`            | Whisper binary + model discovery         |
| POST   | `/api/whisper/transcribe` | Upload audio → whisper-cli → text        |
| GET    | `/api/extraction/health`  | Proxy to extraction-service :4005/health |

### Key Implementation Details

- **Zod validation** on all POST bodies (model names, actions)
- **AbortSignal timeout** (5s) on all Ollama fetch calls
- **TTL caching**: static system info (5 min), Ollama disk usage (60s)
- **Eager cache warming** on module load for system_profiler data
- **execFile** (not exec) for all shell commands — prevents injection
- **Server-side proxy** for extraction health — avoids browser CORS errors

---

## 6. Dashboard UI Layout

### Header

- App icon + title ("Local LLM Mission Control")
- Machine info subtitle (chip, RAM, OS — dynamic)
- Last refresh timestamp + manual refresh button
- Dark/light theme toggle (localStorage persistence)

### Top Stats Row (4 cards)

1. **Ollama** — online/offline status with pulsing dot, server URL
2. **Models** — count + total disk size, number loaded in RAM
3. **Whisper** — installed/not found, model count
4. **Memory** — used/total with color-coded progress bar + sparkline history

Skeleton shimmer cards shown during initial load.

### Main Grid (3 columns)

**Left (2/3) — Ollama Models:**

- Search/filter bar (by name, family, quantization)
- Pull model input with streaming progress bar
- Per-model cards showing: name, disk size, parameters, quantization
- Running models: green highlight, LOADED badge, VRAM usage, expiry time
- Actions: Load, Unload, Prompt, Delete
- Expanded details: digest, modified date, family, modelfile viewer
- Model tags (coding, chat, fast, vision, reasoning) — localStorage
- Auto-load preferred model (star toggle) — localStorage

**Right (1/3):**

- **System** — chip, cores, RAM bar, disk bar, uptime, Ollama disk footprint
- **Extraction Service** — health status from extraction-service (port 4005)
- **Whisper.cpp** — status, binary list, models, transcription test (file upload → text)
- **Brew Packages** — name + version for ollama, whisper-cpp, ffmpeg

### Prompt Modal (overlay)

- Textarea with Cmd+Enter to send
- Chat mode toggle — multi-turn conversation with system prompt
- Prompt history dropdown (last 20, localStorage)
- Streaming response display with token metrics (tok/s, total, latency)
- Model comparison — quick-compare buttons to run same prompt on another model
- Side-by-side response display when comparing
- Copy response button

### Ollama Logs Panel (below grid)

- Collapsible terminal-style panel
- Color-coded log levels (error=red, warn=yellow, info=default)
- Fetches via server-side `/api/ollama/logs`

### Keyboard Shortcuts

| Shortcut    | Action             |
| ----------- | ------------------ |
| `Cmd+Enter` | Send prompt        |
| `Escape`    | Close modal        |
| `R`         | Refresh dashboard  |
| `/`         | Focus model search |
| `?`         | Show shortcuts     |

---

## 7. Design Tokens

ByteLyst design system colors:

| Token                | Hex       | Use               |
| -------------------- | --------- | ----------------- |
| `--bg-canvas`        | `#06070A` | Page background   |
| `--bg-elevated`      | `#0E1118` | Modal background  |
| `--surface-card`     | `#121725` | Card backgrounds  |
| `--surface-muted`    | `#1A2335` | Muted areas       |
| `--text-primary`     | `#EFF4FF` | Main text         |
| `--text-secondary`   | `#A5B1C7` | Descriptions      |
| `--text-tertiary`    | `#6C7C98` | Hints, timestamps |
| `--accent-primary`   | `#5A8CFF` | Primary actions   |
| `--accent-secondary` | `#2EE6D6` | Secondary accent  |
| `--success`          | `#34D399` | Online, loaded    |
| `--warning`          | `#F59E0B` | Warning state     |
| `--danger`           | `#FF6E6E` | Offline, errors   |
| `--purple`           | `#A78BFA` | Whisper, disk     |

Light theme overrides defined in `globals.css` under `.light` class.

---

## 8. Data Flow

```
Browser (page.tsx)
  ├── GET /api/ollama          → Ollama :11434/api/tags + /api/ps
  ├── POST /api/ollama/stream  → Ollama :11434/api/generate (NDJSON)
  ├── POST /api/ollama/chat    → Ollama :11434/api/chat (NDJSON)
  ├── POST /api/ollama/pull    → Ollama :11434/api/pull (NDJSON progress)
  ├── GET /api/ollama/logs     → ~/.ollama/logs/ or macOS unified logging
  ├── GET /api/system          → system_profiler, vm_stat, df, du, brew
  ├── GET /api/whisper         → which whisper-cli, ls models dirs
  ├── POST /api/whisper/transcribe → whisper-cli -f <audio> -m <model>
  └── GET /api/extraction/health   → extraction-service :4005/health
```

Auto-refresh: every 15 seconds (paused during streaming/pull).
Request deduplication: `fetchingRef` guard prevents parallel fetchAll calls.

---

## 9. LocalStorage Persistence

| Key              | Type        | Purpose                                |
| ---------------- | ----------- | -------------------------------------- |
| `promptHistory`  | JSON array  | Last 20 prompts with model + timestamp |
| `modelTags`      | JSON object | User-defined tags per model name       |
| `autoLoadModel`  | string      | Preferred model to auto-load           |
| `dashboardTheme` | string      | `"dark"` or `"light"`                  |

---

## 10. Security Model

- **Local-only**: Dashboard runs on localhost:3100, no auth required
- **No CORS headers**: All API routes are same-origin
- **Input validation**: Zod schemas on all POST bodies; model names validated via regex
- **Shell safety**: All subprocess calls use `execFile` (never string interpolation)
- **External calls proxied**: Extraction health check goes through server-side API route, not direct browser fetch

This is documented in code at `page.tsx` (S3 comment).

---

## 11. Changelog

### v3 (Feb 19, 2026) — Sprints 6–7

- Chat mode with multi-turn conversations and system prompt (F4)
- Model comparison side-by-side UI (F5)
- Memory sparklines with ring buffer (F7)
- Ollama logs viewer with color-coded levels (F8)
- Dark/light theme toggle with localStorage (F10)
- Whisper transcription test with file upload (F12)
- Responsive mobile layout improvements (F13)
- Model tags/labels with localStorage (F14)
- Extraction service health panel (F15)
- Auto-load preferred model (F16)
- CSS utility classes replacing inline styles (CQ2)
- Skeleton shimmer loading UI (CQ5)
- Zod validation on API responses (CQ6)
- Eager cache warming for system_profiler (P5)

### v2 (Feb 19, 2026) — Sprints 1–5

- Fixed 11 bugs (hardcoded specs, streaming escape, auto-refresh, toast IDs, etc.)
- Streaming pull with progress bar (F1)
- Token/s metrics after generation (F6)
- Component extraction (CQ1) — StatusDot, ProgressBar, types, format, config
- Error boundary (CQ4)
- Shared ollama-config (CQ3)
- Model search/filter (F2), prompt history (F3), keyboard shortcuts (F11)
- Modelfile viewer (F9)
- Request deduplication + cache TTLs (P1–P3)
- Input validation (S1) + execFile (S2)

### v1 (Feb 19, 2026) — Initial

- Ollama status, model list, load/unload, basic prompt
- System info (chip, RAM, disk, brew)
- Whisper.cpp discovery
- Auto-refresh every 15 seconds
- Dark theme with ByteLyst design tokens
