SwissHD: Technical Setup & Configuration
Implementation procedures and configuration standards for the SwissHD dashboard within a Node.js runtime environment.
1. Overview
SwissHD is configured through declarative YAML files and environment variable definitions. Visual layouts, categories, and service cards are modified without recompiling application source code, while sensitive API tokens and credentials remain isolated within server-side environment files.
- Status: Active
- Target Environment: Linux / Windows / macOS (Node.js >= 20.0.0)
2. Requirements & Prerequisites
Necessary components or states prior to implementation:
- Node.js: Version 20.0.0 or higher.
- Package Manager:
npm(v9.0.0 or higher). - API Access: Valid authentication tokens, API keys, or login credentials for target homelab services.
- Network Routing: Unrestricted network reachability from the backend server to internal homelab subnets or Tailscale IP addresses.
3. Implementation Procedure
A. Environment Configuration
-
Clone the repository and install dependencies for both root and subpackages:
-
Copy the template environment file to create the local secrets configuration:
-
Populate
.env.localwith authentication tokens and API keys required by active modules (see Section 4).
B. Local Development Servers
-
Execute the Vite frontend development server in the primary terminal (serves UI at
http://localhost:3000): -
Execute the Express backend server in a secondary terminal (runs API on port 3001):
Note: The internal Vite proxy (
vite.config.ts) automatically forwards all client requests matching/api/*tohttp://localhost:3001. Local development execution setsNODE_TLS_REJECT_UNAUTHORIZED=0automatically to permit communication with self-signed SSL certificates on local homelab services.
4. Configuration Standards
Settings are managed according to two distinct files based on security requirements:
| File | Content Type | Server Restart Required? |
|---|---|---|
public/config.yaml |
Visible UI settings (service names, clickable links, color palettes, typography) | No (hot-reloaded on browser refresh) |
.env.local |
Secret authentication tokens, passwords, and API keys | Yes (backend process restart required) |
A. UI Configuration (public/config.yaml)
1. Title and Background System
2. Color Palette Definitions
Hexadecimal values define the dark slate foundation and status accent colors:
theme:
bg: "#202124"
text: "#e8e2d0"
accent:
yellow: "#F2CD60"
mint: "#6DEcb9"
orange: "#FF9E64"
pink: "#F04770"
cyan: "#3DD6F5"
violet: "#9E86FF"
3. Typography Configuration
Assign Google Fonts or local font families loaded from public/font/:
4. Service Declarations
Declare active services under the services array. Each entry requires a unique id corresponding to a backend integration module:
services:
- id: adguard_pve
name: AdGuard Home
url: http://[ADGUARD_IP]:[PORT]/
category: Services
labels:
primary: queries
secondary: blocked
- id: filebrowser
name: FileBrowser Quantum
url: https://[FILEBROWSER_URL]/
category: Storage
labels:
primary: files
secondary: used
id: Unique identifier mapped by the backend Express server (backend/server.js) to attach telemetry handlers and credentials.name: Display string rendered on the dashboard card.url: Hyperlink target opened when the card is clicked.apiUrl: (Optional) Override endpoint utilized by the backend if the polling address differs from the web navigation URL.category: Navigation tab grouping string.labels: Text descriptors for primary and secondary metric values returned by the API.
B. Secret Environment Variables (.env.local)
Define secret credentials in .env.local for local execution. In production CI/CD deployments, define these exact variable names within GitHub Actions Repository Secrets:
# Proxmox VE
PVE_1_AUTH="PVEAPIToken=USER@PAM!TOKENID=SECRET"
PVE_2_AUTH="PVEAPIToken=USER@PAM!TOKENID=SECRET"
# Kubernetes (K3s)
K3S_AUTH="Bearer YOUR_K3S_SERVICE_ACCOUNT_TOKEN"
# AdGuard Home
ADGUARD_PVE_USERNAME=admin
ADGUARD_PVE_PASSWORD=secret_password
# Media Stack (*Arr Stack & Torrents)
SONARR_API_KEY=your_api_key
RADARR_API_KEY=your_api_key
LIDARR_API_KEY=your_api_key
BAZARR_API_KEY=your_api_key
PROWLARR_API_KEY=your_api_key
QBITTORRENT_USERNAME=admin
QBITTORRENT_PASSWORD=secret_password
JELLYFIN_API_KEY=your_api_key
# Infrastructure & Tools
ARGOCD_TOKEN=your_jwt_token
GRAFANA_API_KEY=glsa_your_api_key
N8N_API_KEY=n8n_api_your_key
NOCODB_URL=http://[NOCODB_IP]:[PORT]
NOCODB_API_KEY=your_api_key
IMMICH_API_KEY=your_api_key
NETALERTX_API_KEY=your_api_key
AUTHENTIK_API_KEY=your_api_key
BACKREST_USERNAME=admin
BACKREST_PASSWORD=secret_password
NPM_EMAIL=admin@example.com
NPM_PASSWORD=secret_password
PORTAINER_API_KEY=ptr_your_key
PBS_API_TOKEN=USER@PAM!TOKENID
PBS_API_SECRET=secret_token
FILEBROWSER_TOKEN=your_quantum_api_token
C. Backend Control Architecture
The server control layer (backend/server.js) initializes Express on port 3001 and parses public/config.yaml.
- Service Registration: The server maps every entry in
config.servicesto its corresponding module insidebackend/modules/<name>/service.js. - Concurrent Polling: When
/api/dashboardis requested, the backend executes all registered service polling functions concurrently usingPromise.allSettled. - Caching Layer: Individual module handlers wrap API requests in an in-memory cache (
lib/cache.js) with configured TTLs (typically 15 to 60 seconds) to prevent API rate-limiting or service exhaustion during frequent frontend polling. - Activity Journal: The backend records status changes and collector events in SQLite, exposes
GET /api/activity, and supports timeline purge throughDELETE /api/activity.
D. Logs & Activity Journal (SQLite)
SwissHD incorporates a lightweight, zero-dependency operational event journal powered by the Node.js native node:sqlite (DatabaseSync) module.
1. Database Architecture
- Engine: Built-in
node:sqlite(DatabaseSync API), requiring no external driver dependencies. - WAL Mode: Write-Ahead Logging (WAL) is enabled at initialization to support high-performance concurrent reads and writes.
- Persistence: The database file is written to
backend/activity.db(git-ignored). In containerized environments, the location defaults to the/datamount point (configurable viaDB_PATHenvironment variable) to ensure persistence across pod restarts using a Kubernetes PVC. - Retention Policy: A background worker runs every 24 hours to purge records older than the configured threshold (env variable
ACTIVITY_RETENTION_DAYS, defaulting to 180 days, with a strict floor of 7 days).
2. Database Schema
The database maintains three tables:
activities: Append-only operational event log.- Fields:
id(INTEGER PRIMARY KEY),timestamp(INTEGER),service_id(TEXT),type(TEXT),level(TEXT),message(TEXT),metadata(TEXT/JSON string).
- Fields:
service_states: Persists status state for consecutive failures tracking and status changes.- Fields:
service_id(TEXT PRIMARY KEY),consecutive_failures(INTEGER),status(TEXT),last_changed(INTEGER).
- Fields:
collector_cursors: Stores incremental state tracking pointers for background event collectors.- Fields:
collector_name(TEXT PRIMARY KEY),cursor_val(TEXT/JSON/Timestamp),last_run(INTEGER).
- Fields:
3. API Control Layer
- Read Endpoint (
GET /api/activity?limit=N): Queries and returns the lastNrecords from theactivitiestable. - Delete Endpoint (
DELETE /api/activity): Drops all rows fromactivitiesandcollector_cursorstables, clearing the visible timeline dashboard log immediately.
4. Frontend Integration
- Worker Loop: An activity worker checks all service health states every 60 seconds, recording transitions (e.g. from
OKtoOFFLINE) to the database. - Log Presentation: Logs render as a modular timeline sortable by timestamp, status, and service name, using 15-item client-side pagination.
- Detail Drawers: Expanding a log row displays a specialized detail viewer (e.g., PBS backup summaries, single PBS backups, ArgoCD sync events, ArgoCD health events, service status incidents, or generic metadata key-value grids) based on the event
typeattribute.
5. Verification
System health and operational correctness are validated through these procedures:
- Execute
npm run buildand confirm zero TypeScript compilation or bundler errors occur. - Navigate to
http://localhost:3000and confirm the grid interface renders with the configured typography and color scheme. - Inspect network requests in the browser developer tools; confirm
/api/dashboardreturns HTTP 200 with JSON payload containing active metrics. - Confirm
/api/dashboard/metareturns the discovered service registry. - Verify
/api/activity?limit=100returns journal records or an empty list. - Verify browser console logs remain clean of HTTP 401 (Unauthorized), 403 (Forbidden), or CORS routing errors.
- Confirm individual cards display formatted telemetry corresponding to their YAML definitions (e.g., FileBrowser displaying
27.9 GB / 1.8 TB).
6. Troubleshooting
| Symptom | Root Cause | Technical Resolution |
|---|---|---|
Card displays ONLINE without metrics |
Missing integration mapping | Verify the service id in config.yaml matches an active integration name exactly. |
Card displays OFFLINE or fetch_failed |
Unreachable endpoint | Confirm network routing to the target IP/URL; verify target service SSL certificates. |
| HTTP 401 / 403 errors in backend logs | Invalid secret credential | Check .env.local token syntax; ensure API keys have not expired or lost permissions. |
FileBrowser displays 500 source not provided |
Multi-source Quantum setup | Ensure backend is running v0.12.1+ with automatic /api/settings?property=sources discovery. |
Local frontend cannot reach /api/dashboard |
Proxy misconfiguration | Confirm vite.config.ts includes the proxy rule forwarding /api to http://localhost:3001. |