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
169 lines
8.7 KiB
Markdown
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
|