Hub Overview

Architecture and internals of the Valvet hub

The hub is the central management server. It is a single Rust binary built with Axum that serves the web UI, REST API, WebSocket endpoint, and SSE event stream — all on a single port.

Components

Web UI

A React single-page application embedded in the binary at build time via tower_http::services::ServeDir. The frontend communicates exclusively through the REST API.

REST API

All endpoints live under /api/v1/. Authentication uses JWT access tokens (15-minute expiry) sent as Bearer tokens, plus HTTP-only refresh cookies (30-day expiry) for silent renewal.

WebSocket Server

Nodes connect to /api/v1/ws with a short-lived WebSocket token (60-second expiry, rotated regularly). The hub dispatches commands (backup, restore, prune, check, stats) and receives results over this channel.

SSE Event Stream

The UI subscribes to /api/v1/events for live updates — job progress, node status changes, backup completions. This eliminates polling and keeps the dashboard current.

Scheduler

Plans with cron expressions are evaluated on a timer. When a plan’s cron matches, the hub creates a job and dispatches it to the target node.

Database

SQLite in WAL mode provides concurrent read access with single-writer semantics. Migrations run automatically on startup and are idempotent (safe to re-run).

Data Flow

Browser ──REST/SSE──> Hub ──WebSocket──> Node ──restic──> Repository
  1. User action in the UI triggers a REST API call
  2. Hub validates, records the job in SQLite, and sends a WebSocket command to the node
  3. Node executes restic and streams progress back over WebSocket
  4. Hub updates SQLite and broadcasts SSE events
  5. UI reacts to SSE events and refreshes relevant data