sawa-control-panel/docs/CLAUDE.md

5.8 KiB

CLAUDE.md — Sawa Control Panel

Project: sawa-control-panel
Stack: React + Node.js + Alpine Linux
Purpose: Web-based server control panel for Sawa home servers


Project Overview

A lightweight, self-hosted web control panel for managing Alpine Linux server instances running the Sawa stack. Served by nginx on the local server. Accessible from LAN and remotely via client certificate authentication — no login page, no password prompt. Unauthorized devices cannot reach the interface at all.


Tech Stack

  • Frontend: React (Vite), Tailwind CSS
  • Backend: Node.js with Express — thin API layer
  • Auth: nginx mutual TLS (mTLS) — client certificates only
  • Transport: HTTPS only, self-signed cert on local network
  • Deployment: Built React app served as static files by nginx
  • Process manager: PM2 with OpenRC on Alpine

Architecture

The control panel has two parts:

  • Static frontend — React app built and served from /var/www/panel/
  • API backend — Node.js Express app running on 127.0.0.1:3001, proxied by nginx

nginx handles:

  • TLS termination with client certificate verification
  • Serving the React static build
  • Reverse proxying /api/* to the Node.js backend

The Node.js backend executes OpenRC commands via child_process to control services. It never exposes raw shell access — only a whitelist of allowed actions.


Security Model

  • Client certificates issued manually — one per device (laptop, phone, etc.)
  • nginx ssl_verify_client on — connection dropped before HTTP if no valid cert
  • API backend binds only to 127.0.0.1 — never exposed directly
  • All service control actions are whitelisted — no arbitrary command execution
  • nftables firewall — port 443 open, everything else locked

Directory Structure

sawa-control-panel/
├── frontend/          # React Vite app
│   ├── src/
│   │   ├── components/
│   │   ├── pages/
│   │   └── App.jsx
│   └── package.json
├── backend/           # Node.js Express API
│   ├── routes/
│   ├── services/
│   └── index.js
├── nginx/             # nginx config snippets
├── certs/             # cert generation scripts
└── CLAUDE.md

Environment Variables

PORT=3001
ALLOWED_SERVICES=nginx,postgresql,mariadb,redis,memcached,sshd,nftables
LOG_PATH=/var/log

Key Commands

npm run dev          # start frontend dev server
npm run build        # build frontend for production
node backend/index.js                          # start API server
pm2 start backend/index.js --name panel-api   # production start

API Structure

All endpoints prefixed with /api/v1/

GET  /api/v1/services                    # list all services with status
POST /api/v1/services/:name/start        # start a service
POST /api/v1/services/:name/stop         # stop a service
POST /api/v1/services/:name/restart      # restart a service
GET  /api/v1/system/cpu                  # CPU usage percent
GET  /api/v1/system/memory               # used/total/free RAM
GET  /api/v1/system/disk                 # disk usage per partition
GET  /api/v1/system/uptime               # system uptime
GET  /api/v1/system/load                 # 1/5/15 min load averages
GET  /api/v1/logs/:service               # last 100 lines of service log
GET  /api/v1/vhosts                      # list virtual hosts
POST /api/v1/vhosts                      # create virtual host
DELETE /api/v1/vhosts/:name              # remove virtual host
POST /api/v1/vhosts/:name/enable         # enable site
POST /api/v1/vhosts/:name/disable        # disable site
POST /api/v1/nginx/reload                # reload nginx config

Coding Conventions

  • React functional components only — no class components
  • Tailwind for all styling — no separate CSS files
  • API routes prefixed with /api/v1/
  • All service actions POST only — never GET for mutations
  • Error responses always return JSON with { error: string }
  • Never execute arbitrary shell commands — use whitelist pattern only

Alpine Linux / Server Notes

Critical for AI agents working on this project:

  • This is Alpine Linux — use OpenRC, not systemd
  • Service control: rc-service <name> start|stop|restart|status
  • Service autostart: rc-update add <name> default
  • Config persistence: always run lbu ci after editing config files or the changes will be lost on reboot
  • The server runs in sys mode on a USB stick — write operations should be minimized
  • All data lives under /data/ (postgresql, mysql, redis subdirs)
  • The backend must run as a non-root user with a strict sudoers whitelist for rc-service
  • Deployments go to /var/www/ on the server via SFTP (WinSCP or scp)
  • Test all API endpoints with curl before wiring to frontend
  • nginx virtual host configs live in /etc/nginx/conf.d/ — one file per site
  • After any nginx config change: nginx -t first, then rc-service nginx reload

Managed Services

The following services are installed and managed on the server:

Service Port/Socket Purpose
nginx 80, 443 Web server / reverse proxy
postgresql 127.0.0.1:5432 PostgreSQL 18 (data at /data/postgresql)
mariadb 127.0.0.1:3306 MariaDB (data at /data/mysql)
redis 127.0.0.1:6379 Redis with persistence (data at /data/redis)
memcached 127.0.0.1:11211 Memcached cache
sshd 22 SSH (key auth only)
nftables Firewall

Future Scope

  • Multi-node support — manage multiple Sawa servers from one panel
  • Traffic analytics — nginx access log parsing (AWStats-style)
  • Distributed LLM inference management via exo/llama.cpp
  • Node cluster view — aggregate resource monitoring across all nodes