4.5 KiB
Pulse Architecture
Pulse is a real-time monitoring system designed for Proxmox VE, Proxmox Backup Server, and Docker/Host infrastructure. It is built with a Go backend and a SolidJS frontend, focusing on low latency, high concurrency, and a premium user experience.
🏗 High-Level Overview
The system operates as a single binary that serves both the API and the static frontend assets. It connects to Proxmox infrastructure via their REST APIs (using API tokens or password auth), while Docker/Host metrics are collected by lightweight agents that push data to Pulse.
graph TD
User[User Browser] <-->|WebSocket / HTTP| Pulse[Pulse Server]
subgraph "Pulse Server (Go)"
API[REST API]
WS[WebSocket Hub]
Monitor[Monitoring Engine]
Config[Config Manager]
end
Pulse -->|HTTPS API :8006| PVE[Proxmox VE Node]
Pulse -->|HTTPS API :8007| PBS[Proxmox Backup Server]
Agent[Pulse Agent] -->|HTTPS POST| API
Agent -.->|Collects from| DockerHost[Docker / Host]
Monitor --> WS
Monitor --> API
🔌 Backend Architecture (Go)
The backend is a high-performance Go application designed for concurrent monitoring.
Core Components
-
Entry Point (
cmd/pulse/main.go):- Initializes the configuration, logger, and persistence layer.
- Starts the
ReloadableMonitorwhich manages the lifecycle of monitoring routines. - Launches the HTTP server and WebSocket hub.
-
Monitoring Engine (
internal/monitoring):- Polymorphic Monitors: Uses interfaces to treat PVE and PBS hosts uniformly where possible.
- Goroutines: Each host is monitored in its own lightweight goroutine to ensure non-blocking operations.
- API Clients: Communicates with Proxmox VE/PBS via their REST APIs using API tokens or password-based tickets.
-
Agent Receivers (
internal/api/agents):- Receives metrics from
pulse-agentinstances via HTTP POST. - Agents collect Docker container stats, host metrics, and temperatures locally.
- Push-based model: agents initiate connections to Pulse, not vice versa.
- Receives metrics from
-
WebSocket Hub (
internal/websocket):- Manages active client connections.
- Broadcasts metric updates in real-time.
- Handles "commands" from the frontend (e.g., requesting immediate updates).
-
API Layer (
internal/api):- RESTful endpoints for configuration (adding nodes, setting thresholds).
- Handles authentication and secure token management.
Data Flow
- Collection:
- Proxmox: The Monitoring Engine polls PVE/PBS REST APIs (default: 2s interval).
- Agents: Docker/Host agents push metrics to Pulse at their configured interval (default: 30s).
- Normalization: API responses and agent reports are parsed into standardized Go structs (
HostMetrics,ContainerMetrics). - Broadcast: Normalized data is sent to the
WebSocket Hub. - Delivery: The Hub serializes the data to JSON and pushes it to all subscribed frontend clients.
🎨 Frontend Architecture (SolidJS)
The frontend is a modern Single Page Application (SPA) built with SolidJS and TypeScript. It prioritizes performance by using fine-grained reactivity instead of a Virtual DOM.
Key Technologies
- SolidJS: For reactive UI components.
- TailwindCSS: For styling and theming (Dark/Light mode).
- Vite: For fast development and optimized builds.
State Management
- Stores (
frontend-modern/src/stores):websocket.tsmanages the WS connection and reactive updates;metricsHistory.tsbuffers metrics for sparklines.
Component Design
- Atomic Design: Small, reusable components (
MetricBar,StatusBadge) compose into larger views (NodeSummaryTable). - Visualizations: Custom SVG-based charts (Sparklines) are used instead of heavy charting libraries to keep the bundle size small and rendering fast.
🔒 Security
- Encryption at Rest: Sensitive configuration (passwords, API keys) is encrypted on disk using
AES-GCMwith a user-provided passphrase. - Transport Security: All communications can be secured via TLS.
- Authentication: Session-based auth for API access.
🚀 Deployment
Pulse is distributed as:
- Docker Container: Multi-stage build resulting in a scratch-based or alpine-based image containing just the binary and frontend assets.
- Single Binary: The frontend is embedded into the Go binary using
embed, allowing for a single-file deployment.