# ProxmoxPanel — CORE A self-hosted web management panel for Proxmox VE infrastructure. Manage LXC containers and VMs, run package updates, open interactive terminals, browse files — all from a single web interface with a dark/light Neumorphism UI. ## Features - **Dashboard** — Configurable widget grid with drag-and-drop (LXC/VM status, shortcuts, metrics) - **Proxmox** — Real-time LXC/VM list with start/stop actions, live status via WebSocket - **Updates** — Run `apt upgrade` on the host or any LXC, with streamed output - **Terminal** — Interactive SSH terminal in the browser (xterm.js + PTY) - **Settings** — Per-user preferences (theme, language, sidebar position), instance config - **Modules** — Enable/disable optional modules without restarting ## Requirements - Docker + Docker Compose - Proxmox VE 7.x or 8.x - SSH access to the Proxmox host (password authentication) - A Proxmox API token (read-only `PVEAuditor` role is sufficient for metrics) - Reverse proxy recommended (Traefik, Nginx, Caddy) for HTTPS in production ## Quick Start ```bash git clone cd core docker compose up -d --build ``` Open `http://localhost` (or your server's IP) in a browser. The installation wizard appears on first launch and guides you through the configuration. ## Installation Wizard The wizard runs automatically on first launch and collects: 1. **Instance name** and public URL (auto-detected from the HTTP request) 2. **SSH host** — address and port of the Proxmox host (e.g. `192.168.1.10:22`) 3. **SSH credentials** — Linux username and password used for PAM authentication 4. **Proxmox API** — URL and API token (e.g. `https://192.168.1.10:8006`) 5. **Default language** — `en` or `fr` All sensitive values (SSH password, API token) are encrypted with AES-256-GCM before storage. ## Authentication ProxmoxPanel uses **PAM authentication via SSH**: the panel attempts an SSH connection to the Proxmox host using the credentials provided at login. If the connection succeeds, the user is authenticated. Group membership is checked with `id -nG` — users in the `sudo` or `wheel` group are granted admin privileges. No separate user database is required. Any Linux user with SSH access can log in. Sessions use JWT RS256 (15-minute access tokens + 7-day refresh cookies). ## Configuration All configuration is stored in SQLite (`/app/data/panel.db`). There are no environment variables or config files to manage. Settings are accessible from the web interface under **Settings → General** (admin only). | Setting | Description | |---------|-------------| | `ssh_host` | SSH address of the Proxmox host (`host:port`) | | `ssh_username` | SSH username | | `ssh_password` | SSH password (AES-256-GCM encrypted) | | `proxmox_url` | Proxmox API base URL (`https://host:8006`) | | `proxmox_token` | Proxmox API token (AES-256-GCM encrypted) | | `instance_name` | Display name shown in the UI | | `public_url` | Public URL of the panel | | `default_lang` | Default language (`en` or `fr`) | ## Architecture ``` core/ ├── docker-compose.yml # Two services: backend + frontend ├── backend/ # Go 1.23+ — REST API + WebSocket server │ ├── main.go │ ├── internal/ │ │ ├── api/ # HTTP handlers (chi router) │ │ ├── auth/ # PAM-via-SSH + JWT RS256 │ │ ├── crypto/ # AES-256-GCM secret encryption │ │ ├── db/ # SQLite + versioned migrations │ │ ├── proxmox/ # Proxmox REST API client │ │ ├── ssh/ # SSH connection pool │ │ ├── websocket/ # WebSocket hub (pub/sub by channel) │ │ └── audit/ # Audit log │ └── modules/ # Module system (compiled-in, conditional init) └── frontend/ # Vue 3 + Vite + TypeScript — Nginx static └── src/ ├── views/ # One view per module ├── stores/ # Pinia (auth, ui) ├── styles/ # Neumorphism CSS (dark + light themes) └── locales/ # i18n — en.json, fr.json ``` ### Backend - **Language**: Go 1.23+ - **Router**: `go-chi/chi` v5 - **WebSocket**: `gorilla/websocket` - **Database**: `modernc.org/sqlite` (pure Go, no CGO) - **JWT**: `golang-jwt/jwt` v5, RS256, keys auto-generated at first start - **SSH**: `golang.org/x/crypto/ssh` ### Frontend - **Framework**: Vue 3 (Composition API) - **Build tool**: Vite 6 — compiled to static HTML/CSS/JS, served by Nginx - **State**: Pinia - **i18n**: vue-i18n v11 - **Terminal**: xterm.js + `@xterm/addon-fit` + `@xterm/addon-attach` - **File editor**: CodeMirror 6 - **Design**: Custom Neumorphism CSS, dark/light themes via `data-theme` on `` > Node.js is used **only during the Docker build stage** to compile the frontend. The runtime image is Nginx only. ### Data persistence A Docker volume (`proxmoxpanel-data`) stores: - `panel.db` — SQLite database - `keys/jwt.key`, `keys/jwt.pub` — RSA-2048 key pair for JWT signing - `master.key` — AES master secret for credential encryption **Do not delete this volume without backing up `panel.db` first.** ### WebSocket channels | Endpoint | Description | |----------|-------------| | `GET /ws/proxmox` | LXC/VM status updates (polled every 10s) | | `GET /ws/updates/{jobId}` | Streaming `apt` output for an update job | | `GET /ws/terminal` | Interactive SSH PTY terminal | WebSocket authentication uses a `?token=` query parameter (standard Authorization header is not supported by browser WebSocket API). ## Module System Modules are compiled into the binary but initialized only if enabled in the database. This allows disabling features without rebuilding. Each module implements the `Module` interface: ```go type Module interface { ID() string Register(registry Registry) error } ``` A module can register HTTP routes, WebSocket channels, dashboard widgets, settings tabs, translations, and database migrations via the `Registry` interface. Core modules (always enabled): `dashboard`, `proxmox`, `updates`, `settings` Optional modules (can be toggled): `files`, `terminal`, `logs`, `services` See `backend/modules/` for available modules and their documentation. ## Reverse Proxy ProxmoxPanel listens on port 80 (HTTP). In production, place it behind a reverse proxy for TLS termination. Example Traefik dynamic configuration: ```yaml http: routers: proxmoxpanel: rule: "Host(`panel.example.com`)" entryPoints: [websecure] service: proxmoxpanel tls: certResolver: letsencrypt services: proxmoxpanel: loadBalancer: servers: - url: "http://:80" healthCheck: path: /api/health interval: 30s ``` ## API All API routes are prefixed with `/api/`. | Method | Path | Auth | Description | |--------|------|------|-------------| | GET | `/api/health` | — | Health check | | GET | `/api/install/check` | — | Installation status | | GET | `/api/install/status` | — | Detected URL, pre-fill wizard | | POST | `/api/install/test-ssh` | — | Test SSH connectivity | | POST | `/api/install/configure` | — | Save initial configuration | | POST | `/api/auth/login` | — | Login (PAM via SSH) | | POST | `/api/auth/logout` | JWT | Logout | | POST | `/api/auth/refresh` | Cookie | Refresh access token | | GET | `/api/auth/me` | JWT | Current user profile | | PATCH | `/api/auth/preferences` | JWT | Update user preferences | | GET | `/api/proxmox/resources` | JWT | All Proxmox resources | | GET | `/api/proxmox/lxc` | JWT | LXC list | | POST | `/api/proxmox/lxc/{vmid}/start` | JWT+Admin | Start LXC | | POST | `/api/proxmox/lxc/{vmid}/stop` | JWT+Admin | Stop LXC | | POST | `/api/updates/run` | JWT+Admin | Start update job | | GET | `/api/updates/history` | JWT | Update history | | GET | `/api/settings` | JWT+Admin | All settings | | PUT | `/api/settings/{key}` | JWT+Admin | Update a setting | | GET | `/api/settings/audit` | JWT+Admin | Audit log | | GET | `/api/modules` | JWT | Module list | | POST | `/api/modules/{id}/enable` | JWT+Admin | Enable a module | | POST | `/api/modules/{id}/disable` | JWT+Admin | Disable a module | ## Development ### Backend ```bash cd backend go run . # or go build -o proxmoxpanel . && ./proxmoxpanel ``` Environment variables (all optional): | Variable | Default | Description | |----------|---------|-------------| | `DATA_DIR` | `/app/data` | Path to persistent data directory | | `LISTEN_ADDR` | `:3001` | HTTP listen address | | `APP_ENV` | `production` | Set to `development` for verbose logs | ### Frontend ```bash cd frontend npm install npm run dev # Dev server with proxy to backend at localhost:3001 npm run build # Production build ``` ## License MIT — see [LICENSE](LICENSE)