Add state management, WebSocket polling, html.js templating, and refactor pages
- lib/state.py: per-subsystem collectors with versioned state store - daemon/server.py: state refresh on request, batch routing updates - webui/static/hoover/html.js: new html tag template helper via htm.js - webui/static/hoover/websocket.js: real-time state change notifications - webui/static/hoover/vdom.js: VDOM improvements for keyed diff - All frontend pages refactored to use html templates - Add tests for state management and polling - Update docs and AGENTS.md
This commit is contained in:
@@ -81,6 +81,30 @@ Vacuum Wall uses a declarative configuration model. Persistent user-facing confi
|
||||
| networkd | `config/network/config.json` | `data/networkd/` | `/etc/systemd/network/50-<name>.network` | The JSON file defines per-interface static addresses, routes, DNS, DHCP, and link settings. Each entry renders to a `50-<name>.network` INI file. Stale files are cleaned on apply. Public DNS servers are auto-synced to dnsmasq upstreams. |
|
||||
| ACME | N/A (`~/.acme.sh/` managed by acme.sh) | `data/acme/` | Certificate and key files | acme.sh manages its own state, renewal scheduling, and account keys. Vacuum Wall triggers issuance and renewal but does not maintain independent ACME state. |
|
||||
|
||||
#### Background Polling
|
||||
|
||||
The daemon runs background polling tasks for subsystems with external runtime state. Each subsystem has a configurable interval and a two-layer diff (structural vs volatile) to minimize unnecessary broadcasts.
|
||||
|
||||
| Subsystem | Interval | Rationale |
|
||||
|-----------|----------|-----------|
|
||||
| firewall | 30s | Most expensive collector (6+ subprocess calls) |
|
||||
| wireguard | 10s | Peer connections/handshakes change frequently |
|
||||
| dnsmasq | 10s | Lease file + service status |
|
||||
| networkd | 10s | Interface up/down, DHCP address changes |
|
||||
|
||||
nginx and acme are not polled — they have no external runtime state.
|
||||
|
||||
**Two-layer diff:** Each poll cycle classifies changes as:
|
||||
- **Structural change** (zones added, peers removed, config changed): triggers `bump()` + broadcast `{"type": "versions", ...}` → full UI re-load
|
||||
- **Volatile change only** (transfer counters, DHCP-assigned IPs): sends `{"type": "tick", "subsystems": [...]}` → lightweight per-subsystem re-fetch
|
||||
- **No change**: silence
|
||||
|
||||
Volatile fields per subsystem: `wireguard` (peer transfer/handshake stats), `firewall` (DHCP-assigned IPs), `networkd` (DHCP addresses, link metrics). Defined per collector via `register_volatile()`.
|
||||
|
||||
Poll intervals are configurable via `VACUUM_WALL_POLL_INTERVALS` env var (`firewall:30,wireguard:10,...`).
|
||||
|
||||
On collector failure during a poll, no broadcast is sent (avoids noisy ticks). State data is set to `None`.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
### Config — Declarative Settings
|
||||
|
||||
Reference in New Issue
Block a user