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
8.7 KiB
8.7 KiB
Specification: Boltbot Dashboard
Use
/duy-workflow:execute docs/specs/boltbot-dashboard.spec.mdto 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
-
[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 buildproduces a working static bundle
- Replace the existing Next.js frontend with a Vite + React + TypeScript project at
-
[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.htmlfor client-side navigation - Acceptance: Navigating to
http://localhost:18789/boltbot/dashboardloads the SPA
- Register HTTP routes in the Boltbot plugin to serve the dashboard's
-
[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
ActionReceiptinterface fromextensions/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
- Remove all mock data. Fetch from the real Boltbot API endpoints:
-
[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
-
[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
-
[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
-
[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
-
[REQ-8] Session grouping view
- Group receipts by
sessionKeyto 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
- Group receipts by
-
[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
-
[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 sessionKeycomment 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
- All REQs implemented
pnpm --filter boltbot-dashboard buildsucceeds (228KB JS, 19KB CSS)- Dashboard loads at
/boltbot/dashboardwhen gateway is running - Real API data renders (stats, receipts, detail)
- No Finbro references remain
- 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 hereextensions/boltbot/src/api.ts: Existing HTTP API routes (/boltbot/stats,/boltbot/receipts,/boltbot/receipt)extensions/boltbot/src/receipt-store.ts:ActionReceiptinterface — source of truth for typesextensions/boltbot/src/action-tiers.ts: Tier classification (HIGH/MEDIUM/LOW)extensions/boltbot/src/anomaly.ts: Anomaly detection logicextensions/boltbot/src/stores/local.ts: SQLite receipt storeextensions/boltbot/src/stores/eigenda.ts: EigenDA commitment storeextensions/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 Groteskfont (already loaded in current frontend)
Files to Modify
extensions/boltbot/index.ts— register dashboard static serving routesextensions/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