4.2 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Working principles
- Don't assume. Don't hide confusion. Surface tradeoffs.
- Minimum code that solves the problem. Nothing speculative.
- Touch only what you must. Clean up only your own mess.
- 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.py → notify.py fires connectivity alerts on state change.
Plugin data received: udp.py decodes PLG datagram → stores on Host → threshold.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.