Skip to content

Architecture Overview

KVM Fleet is a multi-tenant platform for managing KVM-over-IP devices.

Stack

Component Technology
API server FastAPI (Python)
Database PostgreSQL with Row-Level Security
Cache / pubsub Redis
Frontend React (SPA)
Agent Go (static binary)
Reverse proxy Caddy (auto-TLS)
Hosting Hetzner CX22 (Falkenstein, DE)

System diagram

graph LR
    subgraph "KVM Devices"
        A1[Agent] -->|WSS outbound| C
        A2[Agent] -->|WSS outbound| C
        A3[Agent] -->|WSS outbound| C
    end

    subgraph "Hetzner CX22"
        C[Caddy] --> API[FastAPI]
        API --> PG[(Postgres)]
        API --> R[(Redis)]
        C --> FE[React SPA]
    end

    U[Browser] -->|HTTPS| C
    MCP[MCP Client] -->|API key| C

Key design decisions

Outbound-only tunnels

Agents initiate outbound WebSocket connections to the platform. No inbound ports are opened on KVM devices. This works behind NATs, firewalls, and corporate networks without VPN configuration.

See WS Multiplex for the tunnel protocol.

Database-level multi-tenancy

All tenant isolation is enforced via Postgres RLS. The API connects as a non-superuser role (kvmfleet_app) with FORCE ROW LEVEL SECURITY. Even a bug in the API code cannot leak data across organizations.

See Row-Level Security for the policy design.

Tamper-evident audit log

Every auditable action (login, console open, device enroll, setting change) is recorded in an append-only table. Each row includes a SHA-256 hash of the previous row, forming a verifiable chain.

See Audit Chain for the hash chain design.

Device-agnostic agent

The agent doesn't know or care what KVM software is running. It tunnels HTTP traffic from the platform to the device's local web UI. This means any KVM with a web interface works — PiKVM, JetKVM, TinyPilot, or custom builds.

Data flow: console session

sequenceDiagram
    participant User as Browser
    participant API as FastAPI
    participant Redis
    participant Agent as Agent (on KVM)
    participant KVM as KVM Web UI

    User->>API: POST /v1/sessions (start console)
    API->>API: Auth + RBAC check
    API->>Redis: Publish "open session" to agent channel
    Redis-->>Agent: Session request (via WS)
    Agent->>KVM: HTTP request to localhost
    KVM-->>Agent: HTTP response
    Agent-->>API: Response over WS multiplex
    API-->>User: Proxied response

Deployment

Single-server deployment on Hetzner CX22:

  • Caddy handles TLS termination and routes traffic
  • FastAPI runs via uvicorn behind Caddy
  • Postgres and Redis run as local services
  • All components managed by systemd