8000b005d64403397999fba526cd0479342a69f7
## What was built Relay operator dashboard at GET /admin/relay (outside /api, clean URL). Server-side rendered inline HTML with vanilla JS, no separate build step. ## Route registration admin-relay-panel.ts imported in app.ts and mounted via app.use() after /api and before /tower. Route not in routes/index.ts (would be /api/admin/relay). ## Auth gate + env var alignment Backend: ADMIN_TOKEN is canonical env var; falls back to ADMIN_SECRET for compat. ADMIN_TOKEN exported as requireAdmin from admin-relay.ts; admin-relay- queue.ts imports it instead of duplicating. Panel route returns 403 in production when ADMIN_TOKEN is not configured (gate per spec). Frontend: prompt reads "Enter the ADMIN_TOKEN". Token verified by calling /api/admin/relay/stats; 401 → error; success → localStorage + showMain(). ## Stats endpoint (GET /api/admin/relay/stats) — 3 fixes from 1st review round: 1. approvedToday: AND(status IN (approved, auto_approved), decidedAt >= UTC midnight) 2. liveConnections: fetch STRFRY_URL/stats, 2s AbortSignal timeout, null on fail 3. Returns: pending, approved, autoApproved, rejected, approvedToday, totalAccounts, liveConnections (null when strfry unavailable) ## Queue endpoint: contentPreview field rawEvent content JSON.parsed and sliced to 120 chars; null on parse failure. GET /api/admin/relay/queue?status=pending used by UI (pending-only, per spec). ## Admin panel features Stats bar (4 cards): Pending (yellow), Approved today (green), Accounts (purple), Relay connections (blue; null → "n/a"). Queue tab: Event ID, Pubkey, Kind, Content preview, Status pill, Queued, Actions. Accounts tab: whitelist table, Revoke (with confirm), Grant form. 15s auto-refresh on queue + stats. Toast feedback on all actions. Navigation: ← Timmy UI, Workshop, Log out. ## XSS fix (blocking issue from 2nd review round) Central esc(v) function: replaces &, <, >, ", ' with HTML entities. Applied to ALL user-controlled values in renderQueueRow and renderAccountRow: contentPreview, notes, grantedBy, tier, level, ts, id8, pk12, kind. onclick handlers use safeId/safePk: hex chars stripped to [0-9a-f] only. Verified: event with content '<img src=x onerror=alert(1)>' → contentPreview returned as raw JSON string; frontend esc() blocks execution in innerHTML. ## TypeScript: 0 errors. Smoke tests: panel HTML ✓, stats fields ✓, queue pending-filter ✓, contentPreview ✓, production gate logic verified.
Description
Timmy Tower World — sovereign AI agent economy
Languages
TypeScript
68.9%
JavaScript
15.5%
Shell
10.6%
HTML
4.5%
CSS
0.4%