Skip to main content

Command Palette

Search for a command to run...

How We Built Dashboard Lite for miniPC Bundles (Flask, Zero Dependencies)

Published
3 min read

How We Built Dashboard Lite for miniPC Bundles (Flask, Zero Dependencies)

Background

TechsFree runs an internal management dashboard — let's call it FullDash. Over time, it grew: task management, node control, family calendar, scheduler... The Flask backend hit 7,000+ lines. The frontend SPA crossed 3,800 lines.

Then came this request:

"When we sell miniPCs, we want to bundle a dashboard as a value-add service. But we don't need internal company features. Keep it simple, generic, and clean."

In other words: strip out all the TechsFree-specific logic and make something any user can run.


What to Keep, What to Cut

We listed every FullDash feature and asked: "Does a random user need this?"

FeatureKeep?Reason
Project tasksGeneric enough
Node management (local only)Useful for the bundled miniPC
Family calendarCompany/personal-specific
Daily work schedulerInternal only
Claude usage displayService-dependent
Timeline viewInternal only
Multi-node managementLite version handles only the local machine

Two features survived. Clean.


Rewrite from Scratch vs. Fork-and-Delete

We considered two approaches: copy FullDash and delete what we didn't need, or write fresh.

We chose the rewrite. The reason: FullDash's code wasn't written with deletion in mind. Its conditionals and references are deeply tangled. Cutting one thing risks silently breaking another, and verifying that nothing broke is expensive.

Server side: Flask backend went from 7,000+ lines to under 500 — and still covers all required functionality. Most of the bloat was logic that should have been separated from the start.

Frontend: A single 37KB HTML file. Zero dependencies, no CDN. Since it's meant to be bundled with a physical device, it needed to work offline.


One-Command Install with install.sh

bash install.sh 8099

This runs:

  1. Python and dependency check
  2. systemd service file generation
  3. Service enable and start
  4. Startup verification

A user who receives the miniPC opens a terminal, runs this command, and the dashboard starts automatically on every boot. Service name: dashboard-lite. Logs: journalctl -u dashboard-lite.

The service file is generated dynamically inside the script, using realpath to avoid hardcoded paths:

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SERVICE_FILE="/etc/systemd/system/dashboard-lite.service"

cat > "$SERVICE_FILE" << EOF
[Unit]
Description=OpenClaw Dashboard Lite
After=network.target

[Service]
ExecStart=/usr/bin/python3 $SCRIPT_DIR/server.py
WorkingDirectory=$SCRIPT_DIR
Restart=always
Environment=LITE_PORT=$PORT

[Install]
WantedBy=multi-user.target
EOF

Auto-Detecting OC_PATH

The node management feature reads OpenClaw's local config (openclaw.json). The config file location varies by install environment.

def detect_oc_path():
    candidates = [
        os.environ.get("OC_PATH"),
        os.path.expanduser("~/.openclaw"),
        "/home/openclaw/.openclaw",
        "/opt/openclaw",
    ]
    for path in candidates:
        if path and os.path.exists(os.path.join(path, "openclaw.json")):
            return path
    return None

Environment variable takes priority; if not set, candidates are tried in order. Prioritizing "probably works" over requiring precise pre-configuration means the install script stays simpler.


Design: Erasing the "Internal Tool" Feel

FullDash is styled with TechsFree brand colors. Lite needed to feel like a generic product — neutral enough for any customer.

  • Color palette: Deep dark gray + purple accent
  • Font: System font stack (no external loading)
  • Logo: Text only (no SVG or image dependencies)

The zero-dependency constraint ended up shaping the visual design too.


Testing Checklist

Verified on a test server:

  • /api/simple-tasks → returns project list ✓
  • /api/node/status → returns active, OpenClaw v2026.2.26 ✓
  • /api/node/bots → returns local bot list ✓

Frontend: manually tested task add → complete → delete cycle. Gateway log viewer on the node page also confirmed working.


Takeaway

"Deleting" code is unglamorous, but it makes structural debt visible. This project revealed that FullDash's bloat wasn't from feature-over-feature additions — it was over-engineering features that should have been Lite-scale from the beginning.

Next time we revisit FullDash, Lite will serve as a reference for simpler implementation patterns worth back-porting.

More from this blog

A

techsfree

208 posts