- Run scheduled Proxmox VE backups to a **normally-off PBS**: wake → wait → backup → prune → (GC) → power-off → notify.
- Be **config-driven** and distributable (Docker image / LXC), nothing hard-coded.
- Modify **nothing** on the Proxmox host: Joulenap owns its own scheduler and acts via APIs + one SSH command.
## Components
- **Web UI** (frontend): single-page app. Talks to the backend over the REST API below.
- **Backend / API**: serves the UI, exposes the REST API, holds the scheduler, runs the backup cycle, manages config.
- **Scheduler**: in-process (APScheduler). Cron-style triggers for the backup job and the scheduled verify; GC has no trigger of its own — it runs as a step of the backup cycle. Re-armed whenever config changes.
-`pbs` — PBS API client (datastore status, start/poll Garbage Collection, verify). TLS-pinned to the fingerprint stored at setup (rejects a changed cert).
- **Store**: `config.yaml` for settings; a small SQLite DB (`data/`) for run history and logs.
## Backup cycle (the heart)
1.**Wake**: send WoL to `pbs.mac` on `pbs.wol_broadcast_iface`.
2.**Wait**: poll `pbs.host:pbs.port` until reachable or `wait_timeout` → on timeout, notify + abort.
3.**Backup**: trigger `vzdump` via PVE API for the selected guests, to `pve.storage_id`, with `mode` and `retention` (prune-backups). Poll the task to completion.
4.**Maintenance** (if due): start PBS **GC** via PBS API and **wait** for it to finish; optional verify.
5.**Power-off**: on success, SSH `poweroff` to PBS. On failure, leave it on for inspection.
6.**Notify**: send result (success/failure, durations, sizes) on the enabled channels.
All steps are logged to the DB and exposed via `/api/logs`.
## REST API
Everything is served under `/api`. Auth is a signed **session cookie** started by `/api/login`; every endpoint requires it except `/api/health`, `/api/auth/status`, `/api/auth/setup` and `/api/login`.
**Auth & account**
| Method | Path | Purpose |
|---|---|---|
| GET | `/api/health` | version + liveness (used by the Docker healthcheck) |
| POST | `/api/wizard/ssh/install` | quick mode: install the public key on PBS over root SSH |
| POST | `/api/wizard/reset` | clear the connection config, keep the tuning |
UI convention: text fields are saved with an explicit **Apply changes** (`PUT /api/config`); only the master **enable/disable** toggle applies immediately.
## Permissions cheat-sheet
- **PVE token**: `VM.Audit` (list guests) + `VM.Backup` + `Datastore.AllocateSpace`**and `Datastore.Allocate`** on the PBS storage (the latter is required for vzdump's retention/prune, which deletes old backups). Quick setup creates a `Joulenap` role with exactly these privileges.
- **PBS token**: `DatastoreAdmin` on the datastore (status + start GC) plus `Audit` on `/system` (read-only node CPU/RAM/network for the dashboard). PBS has no API to create custom roles, so quick setup grants these built-ins scoped by path.
- **SSH to PBS**: dedicated key; ideally a forced command on PBS that only allows `poweroff`.