Files
heartbeat/CLAUDE.md
T
2026-07-31 09:22:57 -04:00

4.2 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Working principles

  1. Don't assume. Don't hide confusion. Surface tradeoffs.
  2. Minimum code that solves the problem. Nothing speculative.
  3. Touch only what you must. Clean up only your own mess.
  4. Define success criteria. Loop until verified.

Commands

# Run tests
pytest -q                        # all tests
pytest tests/test_threshold.py   # single file
pytest -k test_name              # single test

# Lint and type check
tox -e lint     # flake8 over hbd/ and tests/
tox -e mypy     # mypy over hbd/

# Install for development
pip install -e ".[all,dev]"

# Run server
python -m hbd.server.cli serve -c .hb.yaml -f -v

# Generate a password hash
hbd passwd <username>

Tests live in tests/ (pytest, imported as a package). A second directory test/ contains only TLS fixtures (.pem files), not test code.

Line length: 111 characters (black, flake8, both configured).

Architecture

The system has two components: hbc (client) and hbd (server), both in the hbd Python package.

hbd/
  common/    # proto.py (encode/decode), utils.py
  client/    # hbc: plugins/, main.py, config.py, plugin.py
  server/    # hbd: all server modules

Server module map

Module Role
cli.py Argument parsing, entry point, daemon startup
main.py Asyncio runtime: starts UDP, HTTP, WebSocket; pickle save/load
hbdclass.py Host and Connection domain objects; DNS queue
udp.py UDP datagram listener; processes HTB and PLG messages; sets/resets overdue timers
http.py aiohttp web server; all REST API routes (/api/0/) and page routes
ws.py WebSocket broadcast to connected dashboard clients
notify.py Notification dispatch (Pushover, email, Mattermost, Matrix, Signal, SMS); eventlog() is the single choke-point for all alert events
threshold.py Threshold evaluation against plugin metrics; state transitions (OK/WARNING/CRITICAL/UNKNOWN)
monitor.py Timer cleanup on shutdown (reachability is event-driven via timers in udp.py, not polled)
data.py Shared in-memory message ring buffer
journal.py JSONL message journal with size-based rotation
users.py Session management, password hashing (PBKDF2), role checks
config.py Config loading and defaults
configio.py Config file read/write via ruamel.yaml (preserves comments)
settings.py Settings sections for the web UI settings page
dns.py nsupdate integration for dynamic DNS
oauth.py OAuth2 login (Gitea)

Key data flows

Heartbeat received: udp.py decodes the HTB datagram → updates Connection state in hbdclass.py → resets overdue asyncio timer → broadcasts via ws.pynotify.py fires connectivity alerts on state change.

Plugin data received: udp.py decodes PLG datagram → stores on Hostthreshold.py evaluates against configured thresholds → notify.py fires threshold alerts on state transitions.

State persistence: Host.hosts dict + data.msgs ring + active sessions are pickled every 5 minutes and on clean shutdown. Asyncio timers are stripped before pickling (Connection.__getstate__).

Config reload: SIGHUP → configio.py re-reads YAML → live-updates hosts, thresholds, users, notification channels. Port/cert/pickle/journal changes require a full restart.

Client plugin system

Plugins in hbd/client/plugins/ subclass InfoPlugin (collected once, on demand) or MonitorPlugin (periodic). initialize() returns False to self-disable. Data is sent as PLG UDP messages.

hbc_mini.py (scripts/) and hbc_mini.c (scripts/c/) are standalone single-file clients with no external dependencies.

Protocol

All UDP messages: !<ID>: <zlib-compressed key=value payload>. Encoding in hbd/common/proto.py. Lists/dicts encoded as JSON with @ prefix; booleans as 1/0.

Web UI

Jinja2 templates in hbd/server/templates/. Static assets in hbd/server/static/. Live pages (/live, /plugins) use WebSocket connections for real-time push.

CI

Gitea Actions workflow at .gitea/workflows/release.yml.