openclaw/docs/specs/boltbot-dashboard.spec.md
duy 58e556a2d7 feat(boltbot): add audit dashboard — Vite + React SPA served from gateway
Adds a dark-themed receipt audit dashboard at /boltbot/dashboard:
- Vite + React + TypeScript SPA with Tailwind CSS
- Stats summary (total actions, tier breakdown, anomaly count)
- Receipt list with tier/anomaly filtering, offset pagination, 10s polling
- Slide-out receipt detail with accordion sections (hashes, EigenDA, TEE)
- Session grouping view (receipts grouped by sessionKey)
- Gateway serves static files via registerHttpRoute with path traversal
  protection (resolve+startsWith), security headers (nosniff, DENY)
- WCAG-compliant: dialog focus management, keyboard navigation,
  aria-pressed/expanded/selected, semantic table roles, contrast AA
2026-01-29 15:12:32 -08:00

169 lines
8.7 KiB
Markdown

# Specification: Boltbot Dashboard
> Use `/duy-workflow:execute docs/specs/boltbot-dashboard.spec.md` to implement.
## Goal
Rebuild the Boltbot audit dashboard as a Vite + React static SPA served from the gateway at `/boltbot/dashboard`, wired to the real Boltbot API, with Boltbot branding.
## Requirements
1. **[REQ-1] Vite + React SPA scaffold**
- Replace the existing Next.js frontend with a Vite + React + TypeScript project at `extensions/boltbot/dashboard/`
- Configure `base: '/boltbot/dashboard/'` so all assets resolve correctly when served from the gateway
- Use Tailwind CSS for styling (dark theme, matching current design)
- Build output: `extensions/boltbot/dashboard/dist/`
- Acceptance: `pnpm --filter boltbot-dashboard build` produces a working static bundle
2. **[REQ-2] Gateway static file serving**
- Register HTTP routes in the Boltbot plugin to serve the dashboard's `dist/` directory at `/boltbot/dashboard/*`
- Catch-all returns `index.html` for client-side navigation
- Acceptance: Navigating to `http://localhost:18789/boltbot/dashboard` loads the SPA
3. **[REQ-3] Real API integration**
- Remove all mock data. Fetch from the real Boltbot API endpoints:
- `GET /boltbot/stats``{ total, byTier: { low, medium, high }, anomalyCount }`
- `GET /boltbot/receipts?limit=50&offset=0``{ receipts: ActionReceipt[] }`
- `GET /boltbot/receipt?id=<uuid>``{ receipt: ActionReceipt }`
- TypeScript types must match the real `ActionReceipt` interface from `extensions/boltbot/src/receipt-store.ts`:
- `id, timestamp, sessionKey, tier, toolName, argumentsHash, resultHash, success, durationMs, anomalies: string[], daCommitment?: string`
- Use SWR with 10-second polling for stats and receipts
- Acceptance: Dashboard displays real receipts from the running gateway
4. **[REQ-4] Boltbot branding**
- Replace Finbro logo and branding with "Boltbot" text/logo
- Keep the layout shell (header + sidebar) but rebrand all instances
- Header: "Boltbot" logo/text, remove user dropdown (no auth yet)
- Sidebar: Dashboard (active), Audit Log, Sessions (links can be non-functional placeholders)
- Acceptance: No Finbro references remain. "Boltbot" appears in header and page title
5. **[REQ-5] Stats summary**
- Display cards showing: total action count, count per tier (low/medium/high), anomaly count
- Color-coded: low=green, medium=yellow, high=red
- Skeleton loading state while fetching
- Acceptance: Stats cards render with real data from `/boltbot/stats`
6. **[REQ-6] Receipt list with filtering**
- Table rows: toolName, tier (color badge), relative timestamp, success (icon), anomaly indicator
- Offset-based pagination: "Load more" fetches next 50 (offset += 50), appends to list
- Client-side filters:
- Tier: multi-select (low/medium/high)
- Anomaly toggle: show only receipts where `anomalies.length > 0`
- Filters compose (e.g. "high tier with anomalies")
- Preserve scroll position and filters across 10-second polls
- Acceptance: Filtering, pagination, and polling all work without losing state
7. **[REQ-7] Receipt detail panel**
- Click a row to open a slide-out detail panel (right side)
- Default view (always visible):
- toolName, tier badge, success/failure badge
- Relative timestamp + full ISO 8601
- Duration (human-readable, e.g. "142ms")
- sessionKey
- Anomaly labels (each as a distinct colored label; empty = "Clean")
- Collapsible accordion sections (default collapsed):
- **Hashes**: argumentsHash, resultHash (full 64-char hex, copy button)
- **EigenDA Verification**: daCommitment hex (copyable), link to verify on-chain if present, "Unverified" if absent
- **TEE Attestation**: placeholder section — text "TEE attestation verification coming soon"
- Dismiss via close button, Escape key, or backdrop click. Preserves list scroll/filters.
- Acceptance: Detail panel opens with all fields, accordions expand/collapse, copy works
8. **[REQ-8] Session grouping view**
- Group receipts by `sessionKey` to show per-conversation audit trails
- UI: a toggle or tab to switch between "All Receipts" (flat list) and "By Session" (grouped)
- Each session group shows: sessionKey, receipt count, latest timestamp, tier breakdown
- Clicking a session group expands to show its receipts (same row format as flat list)
- Acceptance: Receipts are correctly grouped by sessionKey
9. **[REQ-9] Error handling**
- If a fetch fails, show an inline error on the affected section (stats or receipts). Don't break the rest.
- API returns `{"error": "not_found"}` (404) or `{"error": "missing_id"}` (400) for bad receipt lookups — display a user-friendly message.
- Acceptance: Intentionally failed requests show error state without crashing
10. **[REQ-10] Auth stub**
- No authentication implemented
- All sessions visible (operator view for now)
- Add a `// TODO: Telegram OAuth — filter receipts by authenticated user's sessionKey` comment in the data-fetching layer
- Acceptance: Comment exists, no auth code
## Design Decisions
| Decision | Choice | Rationale |
|----------|--------|-----------|
| Framework | Vite + React + TypeScript | Static SPA, no SSR needed. Fast builds, small bundle. |
| Serving | Gateway registerHttpRoute | Same origin, single container, no CORS. |
| Data fetching | SWR | Already used in current frontend. Polling + caching built in. |
| Pagination | Offset-based | Matches real API (`?limit=50&offset=0`). |
| Styling | Tailwind CSS (dark theme) | Matches existing design. |
| Auth | Deferred | Stub only. Telegram OAuth planned for future. |
| Advanced detail | Accordion sections | Hashes, EigenDA, TEE info collapsed by default. |
## Progress
| ID | Status | Notes |
|----|--------|-------|
| REQ-1 | COMPLETED | Vite + React + TS scaffold at dashboard/ |
| REQ-2 | COMPLETED | dashboard-serve.ts registered in index.ts |
| REQ-3 | COMPLETED | SWR hooks fetch real API, response shapes matched |
| REQ-4 | COMPLETED | Boltbot branding, no Finbro references |
| REQ-5 | COMPLETED | StatsCards with skeleton/error states |
| REQ-6 | COMPLETED | ReceiptList with tier/anomaly filters, pagination |
| REQ-7 | COMPLETED | ReceiptDetail with accordions (hashes, EigenDA, TEE) |
| REQ-8 | COMPLETED | SessionView groups by sessionKey |
| REQ-9 | COMPLETED | Inline errors per section |
| REQ-10 | COMPLETED | Auth TODO comment in hooks.ts |
## Completion Criteria
- [x] All REQs implemented
- [x] `pnpm --filter boltbot-dashboard build` succeeds (228KB JS, 19KB CSS)
- [ ] Dashboard loads at `/boltbot/dashboard` when gateway is running
- [ ] Real API data renders (stats, receipts, detail)
- [x] No Finbro references remain
- [x] No mock data remains
## Edge Cases
| Case | Expected Behavior |
|------|-------------------|
| No receipts yet | Empty state message: "No actions recorded yet" |
| All receipts are low tier (not logged) | Stats show 0, empty receipt list with explanation |
| daCommitment absent | Detail shows "Unverified — no DA commitment" |
| API unreachable | Inline error per section, previous data preserved |
| Very long anomaly list | Scroll within anomaly label area |
| Receipt deleted between list and detail fetch | Use list data (already loaded), no re-fetch needed |
## Technical Context
### Key Files
- `extensions/boltbot/index.ts`: Plugin entry — add dashboard route registration here
- `extensions/boltbot/src/api.ts`: Existing HTTP API routes (`/boltbot/stats`, `/boltbot/receipts`, `/boltbot/receipt`)
- `extensions/boltbot/src/receipt-store.ts`: `ActionReceipt` interface — source of truth for types
- `extensions/boltbot/src/action-tiers.ts`: Tier classification (HIGH/MEDIUM/LOW)
- `extensions/boltbot/src/anomaly.ts`: Anomaly detection logic
- `extensions/boltbot/src/stores/local.ts`: SQLite receipt store
- `extensions/boltbot/src/stores/eigenda.ts`: EigenDA commitment store
- `extensions/boltbot/dashboard/` *(to be created)*: Vite + React SPA
### Patterns to Follow
- Moltbot uses ESM (`"type": "module"`) throughout
- Plugin HTTP routes use `api.registerHttpRoute(method, path, handler)`
- Existing API responses use `{ receipts: [...] }`, `{ receipt: {...} }`, `{ total, byTier, anomalyCount }`
- Dark theme with Tailwind: bg-neutral-950, border-neutral-800, text-neutral-100
- Use `Space Grotesk` font (already loaded in current frontend)
### Files to Modify
- `extensions/boltbot/index.ts` — register dashboard static serving routes
- `extensions/boltbot/package.json` — add dashboard build script + devDependencies (vite, react, tailwind)
### Files to Create
- `extensions/boltbot/dashboard/` — entire Vite + React SPA (vite.config.ts, index.html, src/*, etc.)
### Files to Delete
- `extensions/boltbot/frontend/` — remove the existing Next.js app entirely