Storm Pulse is a secure server management agent for Storm Developments infrastructure. Runs on VPS servers, connects outbound to a Django dashboard over WebSocket with mTLS.
Find a file
smattymatty 6666abd148
Some checks failed
Tests / test (push) Failing after 53s
send pdefaults
2026-02-22 21:12:11 -05:00
.forgejo/workflows test and workflow 2026-02-21 16:37:23 -05:00
.venv 0.1.1 2026-02-22 18:40:46 -05:00
config remove most commands 2026-02-22 12:08:42 -05:00
storm_pulse_agent.egg-info 0.1.1 2026-02-22 18:40:46 -05:00
stormpulse send pdefaults 2026-02-22 21:12:11 -05:00
tests send pdefaults 2026-02-22 21:12:11 -05:00
.gitignore phase 4 - websocket infra 2026-02-21 11:48:32 -05:00
pyproject.toml 0.1.1 2026-02-22 18:40:46 -05:00
README.md 0.1.1 2026-02-22 18:40:46 -05:00

Storm Pulse Agent

Secure server management agent for Storm Developments. Connects outbound to a Django dashboard over WebSocket with mTLS, pushes system metrics, and executes whitelisted deploy commands. Zero listening ports.

How It Works

  1. Agent connects outbound to the dashboard. Nginx terminates mTLS.
  2. Sends a register message (including its available commands list), then pushes metrics every 15s (CPU, memory, disk, load, containers).
  3. Dashboard sends HMAC-signed commands. Agent verifies signature, nonce, and expiry before executing.
  4. Commands run via subprocess.run(shell=False) against a strict whitelist. Custom commands can be added via config with optional overridable parameters (regex-validated). No shell injection possible.

Read the Protocol Specification for exact information.

Security

Five layers, each independent:

  • Network -- No inbound ports. Agent initiates all connections.
  • Transport -- mTLS with per-agent certs from a private CA.
  • Application -- HMAC-SHA256 + nonce + expiry on every command.
  • Execution -- Whitelisted commands only. Absolute paths. shell=False. Config placeholders from local config only; runtime params are regex-validated.
  • OS -- Dedicated user. Systemd sandboxing.

See the Security Architecture wiki page for the full design.

Setup

Requires Python 3.12+. Three runtime deps: websockets, psutil, cryptography.

For full setup instructions (system user, permissions, systemd, firewall), see the Setup Guide.

CLI

stormpulse enroll ENDPOINT AGENT_ID TOKEN [--creds-dir DIR] [--force]
stormpulse init [--creds-dir DIR] [--force]
stormpulse run [CONFIG]
stormpulse status [CONFIG]
stormpulse --version

enroll -- One-time enrollment. Generates an EC P-256 keypair, sends a CSR to the dashboard, writes the signed cert + CA cert + HMAC key to /etc/stormpulse/. The private key never leaves the machine.

init -- Interactive setup wizard. Generates config, creates systemd service, sets permissions. Run after enrollment.

run -- Starts the agent. Connects to the dashboard, sends heartbeats and metrics, executes commands. Reconnects automatically with exponential backoff.

status -- Local inspection. Shows version, agent ID, config path, dashboard URL, certificate expiry, nonce DB entry count, and whether the agent process is running. No network required.

Configuration

See config/stormpulse.example.toml for all options. Key settings:

Section Field Description
agent id Unique identifier for this server
agent pulse_token UUID from the Server record in the dashboard
agent disabled_commands List of command names to remove from the registry (optional)
dashboard url WebSocket URL (wss://...)
project project_dir Absolute path to the deployed project
project compose_file Absolute path to docker-compose.yml
project env_file Absolute path to .env file (optional, passed as --env-file to docker compose)
commands.* Custom commands (optional, see example config)

Documentation

Develop

git clone <repo-url> && cd storm-pulse
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest          # 395 tests
mypy .          # strict

License

MIT