Local AI Infrastructure Notes (5/15) — Home Server Dashboard Design with Next.js
Visualize only what agents send. Never control them directly.
Key Takeaways
- In a multi-agent environment, tracking state via individual terminals does not scale. A dashboard becomes essential infrastructure as operational scope grows.
- Next.js + SQLite is a practical fit for home server dashboards. Server-side rendering eliminates a separate frontend build, and there are no external DB dependencies.
- The core design principle is read-only: adding control logic to a dashboard causes complexity to explode.
Why Multi-Agent Monitoring Matters
As the number of agents grows, answering questions like "which agents are currently active?", "when did the last insight arrive?", or "has a stock signal come in recently?" requires manually digging through multiple terminals and log files. Beyond a certain operational threshold, this approach becomes unworkable.
The dashboard's role is to aggregate that information into a single interface. The design driver is operability, not technical interest.
Body
1. Why Next.js + SQLite
Three criteria drove the stack selection:
- Single-process operation — Home server resources are constrained. Running a separate DB server process was not acceptable.
- Server-side rendering — API and frontend managed within a single project.
- Fast prototyping — With agent operations already urgent, spending time on infrastructure was not an option.
Next.js satisfies criteria 1 and 2. API Routes provide backend endpoints, and page rendering lives in the same project. SQLite satisfies criterion 3: a single file is the entire database. No server process like PostgreSQL or MySQL is required.
Alternatives evaluated and rejected:
- Grafana + Prometheus: Monitoring-only with limited custom widget support. Poorly suited for unstructured data like agent messages.
- Express + React: Frontend/backend separation is overengineering for a home server.
- Static HTML + cron generation: No real-time capability.
2. Dashboard Sections
The dashboard is organized into four sections.
Agent Status Panel
Displays registered agents as cards showing current status: Active, Suspended, or Complete. Each card includes the agent name, role, tech stack, and last activity timestamp.
Status is determined from webhook receipt history. An agent is marked Active if an insight or message arrived within the most recent polling window; otherwise, the default status from the agent's configuration file is shown. The active threshold is adjustable per agent type in the config.
Insight Timeline
Lists insights sent by agents via webhooks in chronological order. Each entry shows the source agent, tags, and timestamp. Filters allow narrowing to a specific agent or tag.
This timeline is the dashboard's core. It answers "what is happening in the system right now" at a glance.
Stock Signal Widget
Stock-related signals are isolated in a dedicated widget. It displays ticker, signal type (Buy / Sell / Hold), score, and receipt time. Separation from the general insight timeline is justified by different update frequency and the need for immediate visual recognition.
Agent Message Log
Agent-to-agent messages are displayed as a log: sender, recipient, message type (task_request, insight, status_update, etc.), and ACK status. Unacknowledged messages are visually highlighted.
3. Data Flow — Webhook Ingestion → SQLite → Rendering
The full data flow is unidirectional:
Agent → POST /api/webhooks → SQLite write → Dashboard render
Agents POST insights and state changes as webhooks. The API endpoint receives them, writes to SQLite, and reads the latest data at page render time.
Real-time updates use polling. WebSocket was considered, but agent event frequency is on the order of a few times per minute — 30-second polling is sufficient. The added complexity of WebSocket yields no operational benefit at this scale.
4. API Endpoint Design
Three core endpoints make up the dashboard API.
POST /api/webhooks — Insight ingestion
{
"content": "Qwen3 model benchmark complete. 15% performance improvement over previous baseline.",
"tags": ["benchmark", "qwen3"],
"source": "model-manager"
}
Fire-and-forget endpoint for agents. No ACK required. Incoming payloads are timestamped and written to SQLite.
GET /api/events — Event retrieval
Read endpoint called by the dashboard frontend. Supports query parameter filtering by source, tag, and time range. Pagination included.
POST /api/agent-messages — Inter-agent messages
{
"fromAgent": "research-agent",
"toAgent": "quality-agent",
"messageType": "task_request",
"intent": "fact-check requested",
"body": { "text": "Source file verification requested" },
"dedupeKey": "fc-20260405-001"
}
Unlike webhooks, this endpoint requires ACK: bidirectional communication. The receiving agent must confirm receipt. Unacknowledged messages are visually flagged in the dashboard.
5. Design Principle — "Visualize Only. Never Control."
The most important principle upheld throughout dashboard design: the dashboard visualizes what agents send; it does not control them directly.
Adding agent start/stop or task assignment functionality introduces three problems:
- Control creates state synchronization issues — If the dashboard stops an agent but the agent self-recovers, state diverges.
- Each agent has a different control interface — Presenting a unified control surface across Flutter apps, Python scripts, and Node.js servers carries high abstraction cost.
- Read-only means bugs are non-fatal — A bug in the dashboard does not affect agent operation.
This principle keeps dashboard development simple: receive, store, display. Three operations only.
Agent control, when needed, is handled directly within each agent's own project. The dashboard is for observing the system, not operating it.
Troubleshooting Notes
SQLite concurrent write contention. When multiple agents POST webhooks simultaneously, "database is locked" errors occur. Enabling WAL (Write-Ahead Logging) mode resolves this. A single line — PRAGMA journal_mode=WAL; — activates it, and concurrent read/write throughput improves significantly.
Polling interval tuning. An overly short polling interval generates excessive SQLite read load. Agent events do not require sub-second real-time delivery, so 30-second polling is operationally appropriate. High-frequency widgets can have independent polling intervals configured separately.
Agent name normalization. Agent identifiers containing non-ASCII characters or spaces cause repeated encoding issues in URL parameters, filters, and SQLite queries. The internal layer uses English slugs exclusively; the display layer maps slugs to human-readable names.
Summary
Core design decisions for the home server dashboard:
- Use Next.js + SQLite for a single-process dashboard.
- Organize into four sections: agent status, insight timeline, stock signals, message log.
- Maintain unidirectional flow: webhook ingestion → SQLite write → page render.
- Keep the dashboard read-only. Do not add agent control functionality.
The last point is the most critical. The temptation to add control to a dashboard is strong, but the moment it is added, a simple monitoring tool becomes a complex orchestration platform. Monitoring and control must be separated at the design layer.
Series overview: Series index
๋๊ธ
๋๊ธ ์ฐ๊ธฐ