Files
joulenap/docs/ARCHITECTURE.md
T

109 lines
6.2 KiB
Markdown
Raw Normal View History

2026-07-02 20:46:57 +02:00
# Joulenap — Architecture & API
## Goals
- 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.
- **Connectors**:
- `pve` — PVE API client (list guests, trigger `vzdump`, read task status).
- `pbs` — PBS API client (datastore status, start/poll Garbage Collection, verify). TLS-pinned to the fingerprint stored at setup (rejects a changed cert).
2026-07-02 20:46:57 +02:00
- `wol` — sends the Wake-on-LAN magic packet on the LAN.
- `power` — SSH to PBS for `poweroff`, verified against `data/known_hosts` (host key confirmed in the wizard).
2026-07-02 20:46:57 +02:00
- `notify` — Apprise / Telegram / ntfy / Discord / email senders.
- **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) |
| GET | `/api/update` | running version, plus the latest GitHub release when `app.update_check` is on (cached 24h; no outbound call when off) |
| GET | `/api/config/yaml` | the redacted config serialised as YAML, for the Advanced tab's editor |
| PUT | `/api/config/yaml` | apply an edited YAML document (same validation and merge as `PUT /api/config`) |
2026-07-02 20:46:57 +02:00
| GET | `/api/auth/status` | whether first-run setup is still needed / already signed in |
| POST | `/api/auth/setup` | first run: create the admin account |
| POST | `/api/login` | authenticate, start session |
| POST | `/api/logout` | end session |
| GET | `/api/auth/me` | current user |
| PUT | `/api/account` | change username / password |
**Dashboard & config**
| Method | Path | Purpose |
|---|---|---|
| GET | `/api/status` | scheduler state, next/last run, PBS power, datastore + node load |
| GET | `/api/config` | current config (secrets redacted) |
| PUT | `/api/config` | validate + save config, re-arm scheduler (the "Apply changes" action) |
| GET | `/api/guests` | list CTs/VMs from PVE (id, name, type) for the selection panel |
| POST | `/api/scheduler/toggle` | enable/disable the backup job (atomic switch) |
**Jobs & power**
| Method | Path | Purpose |
|---|---|---|
| POST | `/api/backup/run` | run a backup cycle now (optional `{keep_on}` to leave the PBS on) |
| POST | `/api/gc/run` | run a GC cycle now: wake → GC → power-off (optional `{keep_on}` to leave the PBS on) |
2026-07-02 20:46:57 +02:00
| POST | `/api/power/on` | wake the PBS (Wake-on-LAN) |
| POST | `/api/power/off` | power the PBS off (SSH) |
| POST | `/api/wol/test` | send a test magic packet |
| POST | `/api/notify/test` | send a test notification |
**History & logs**
| Method | Path | Purpose |
|---|---|---|
| GET | `/api/logs?limit=` | recent activity-log lines |
| GET | `/api/runs?limit=` | run history (summaries) |
| GET | `/api/runs/{id}` | one run with its steps + logs |
| GET | `/api/tasklog?after=` | live PVE/PBS task output for the current run (task-log panel) |
**Setup wizard**
| Method | Path | Purpose |
|---|---|---|
| POST | `/api/wizard/pve/connect` | connect to PVE, list nodes + PBS storages (quick mode also mints a scoped token) |
| POST | `/api/wizard/storage/derive` | derive PBS host/port/datastore/fingerprint from a storage |
| POST | `/api/wizard/pbs/check` | reach the PBS, read its fingerprint |
| POST | `/api/wizard/pbs/provision` | quick mode: auto-create a scoped PBS token from root creds |
| GET | `/api/wizard/interfaces` | local NICs, to pick the WoL broadcast interface |
| POST | `/api/wizard/wol/detect-mac` | detect the PBS MAC via ping + ARP |
| POST | `/api/wizard/ssh/keygen` | generate the poweroff SSH keypair |
| POST | `/api/wizard/ssh/hostkey` | scan the PBS SSH host key + fingerprint (to confirm before the root password is sent) |
| POST | `/api/wizard/ssh/trust` | persist the user-confirmed PBS host key to `data/known_hosts` |
2026-07-02 20:46:57 +02:00
| 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`.