guide #049

3-Page Dashboard (STATUS / KEYS / LOGS)

Problem
Single dashboard page insufficient as PocketClaw grew. No way to view logs or manage keys without SSH.

Solution
Extended hijack.js HTTP interceptor to serve 3 pages with tab navigation and CRT theme.

Context

As PocketClaw matured, a single status page wasn't enough. Changing an API key required SSH access and manual file editing. Reading gateway logs meant tailing files over SSH. For a device meant to run autonomously, all management should be accessible from a web browser.

The dashboard was extended from 1 page to 3 pages, plus 4 API endpoints, all served from hijack.js within the same Node.js process.

Implementation

Routes added to the HTTP interceptor:

RoutePageFunction
/dashboardSTATUSServices, RAM bar, swap, top processes, uptime
/keysKEYSView masked keys, edit, test connectivity
/logsLOGSReal-time gateway logs, color-coded errors
/api/statusJSONStatus data
/api/keysJSONKey list (GET) / save (POST)
/api/keys/testJSONTest key validity
/api/logsJSONLog buffer + lazy load history

All pages share a common CRT-themed layout with tab navigation:

function getTabNav(active) {
  return `
    <nav style="display:flex;gap:8px;margin-bottom:24px">
      <a href="/dashboard" ${active==='status'?'class="active"':''}>STATUS</a>
      <a href="/keys" ${active==='keys'?'class="active"':''}>KEYS</a>
      <a href="/logs" ${active==='logs'?'class="active"':''}>LOGS</a>
    </nav>
  `;
}

All pages auto-refresh every 2-3 seconds via fetch() to their respective API endpoints:

// Auto-refresh pattern used by all 3 pages:
setInterval(async () => {
  const res = await fetch('/api/status');
  const data = await res.json();
  updateDOM(data);
}, 3000);

Verification

# Test all 3 pages:
curl -s http://localhost:9000/dashboard | grep "STATUS"
# Expected: STATUS tab active

curl -s http://localhost:9000/keys | grep "KEYS"
# Expected: KEYS tab active

curl -s http://localhost:9000/logs | grep "LOGS"
# Expected: LOGS tab active

# Test API endpoints:
curl -s http://localhost:9000/api/status | head -1
curl -s http://localhost:9000/api/keys | head -1
curl -s http://localhost:9000/api/logs | head -1
# Expected: JSON responses for each

Gotchas

  • All HTML is inlined in hijack.js — no external files, no build step, no static file serving
  • The log buffer keeps the last 1000 lines in memory. Older logs are read from the log file on demand (lazy loading)
  • Key values are masked in the GET response (only last 4 chars shown). Full values are only accessible when editing
  • The /api/keys POST endpoint writes directly to the env file and updates process.env without gateway restart
  • Auto-refresh at 2-3 second intervals creates ~20-30 requests/minute. On a single-core ARM32, this is noticeable but acceptable

Result

MetricBeforeAfter
Dashboard pages13
API endpoints14
SSH required forKeys, logsNothing (web UI)
Management overheadManualBrowser-based
Continue reading
guide
Pocket AI complete guide
Running self-hosted AI on portable hardware
guide
Edge AI hardware buyer's guide 2026
Pi 5 vs Mini PC vs Mac Mini
report
Self-hosted AI landscape 2026
Quarterly state of the ecosystem
section
Pocket AI hardware hub
All portable hosts reviewed
section
Agent tracker
Live stats on every agent
newsletter
Thursday digest
Weekly summary in your inbox