# 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 ```bash 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 start|stop|restart|status` - Service autostart: `rc-update add 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