guide #042

Dashboard v6: Green Cyberpunk + RAM Breakdown

Problem
Dashboard was red-themed, had unused tabs, didn't explain WHY RAM was high.

Solution
Green Matrix CRT theme, boot animation, RAM process breakdown reading /proc/[pid]/status for ALL processes.

Context

The dashboard went through 6 major versions. Version 5 had a red cyberpunk theme, multiple unused tabs (placeholder for features that never shipped), and only showed total RAM — not which processes were consuming it. When the phone was using 850 MB out of 1 GB, there was no way to know where the RAM went without SSHing in and running ps.

Version 6 was a complete redesign with two goals: match the PocketClaw green CRT aesthetic, and show per-process RAM breakdown so optimization targets are immediately visible.

Implementation

All data is read from the /proc filesystem directly — zero shell commands, zero spawned processes:

// RAM and Swap from /proc/meminfo
const meminfo = fs.readFileSync('/proc/meminfo', 'utf8');
const total = parseInt(meminfo.match(/MemTotal:\s+(\d+)/)[1]);
const free = parseInt(meminfo.match(/MemFree:\s+(\d+)/)[1]);
const available = parseInt(meminfo.match(/MemAvailable:\s+(\d+)/)[1]);

// Per-process RSS from /proc/[pid]/status
const procs = fs.readdirSync('/proc')
  .filter(f => /^\d+$/.test(f))
  .map(pid => {
    try {
      const status = fs.readFileSync(`/proc/${pid}/status`, 'utf8');
      const cmdline = fs.readFileSync(`/proc/${pid}/cmdline`, 'utf8')
        .replace(/\0/g, ' ').trim();
      const rss = parseInt(status.match(/VmRSS:\s+(\d+)/)?.[1] || '0');
      return { pid, name: cmdline || pid, rss };
    } catch(e) { return null; }
  })
  .filter(Boolean)
  .sort((a, b) => b.rss - a.rss);

// Uptime from /proc/uptime
const uptime = parseFloat(fs.readFileSync('/proc/uptime', 'utf8').split(' ')[0]);

Process names are cleaned up for readability (Hack #43):

// Clean Android package names for display
name = name
  .replace(/^com\.android\./, "")
  .replace(/^android\.process\./, "")
  .replace(/^com\.motorola\./, "moto.")
  .replace(/^com\.google\.android\./, "goog.")
  .replace(/^com\.pocketclaw\./, "")
  .replace(/^com\.qualcomm\./, "qc.")
  .replace(/^com\.termux\.?/, "termux");

The green CRT theme uses the same color palette as the website: #000A00 background, #00FF41 green, #EE3333 red for warnings.

Verification

# Access dashboard:
curl -s http://localhost:9003/dashboard | head -20
# Expected: HTML with CRT green styling

# Check API endpoint:
curl -s http://localhost:9000/api/status | python -m json.tool | head -10
# Expected: JSON with ram, swap, uptime, processes

# Verify process list includes per-process RSS:
curl -s http://localhost:9000/api/status | grep -o '"rss":[0-9]*' | head -5
# Expected: RSS values for top processes

Gotchas

  • Reading /proc/[pid]/status can fail for kernel threads or short-lived processes — always use try/catch
  • Some processes report 0 KB VmRSS (kernel threads, zombies) — filter these out
  • /proc/meminfo values are in KB, not bytes. Convert for display
  • The per-process breakdown revealed that the WebView launcher was using 216 MB — more than the gateway itself (186 MB). This directly led to the native APK rewrite (Hack #40)

Result

MetricBefore (v5)After (v6)
ThemeRed cyberpunkGreen CRT
RAM infoTotal onlyPer-process breakdown
Data sourceShell commands/proc (zero overhead)
Tabs4 (unused)1 (focused)
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