Commit Graph
637 Commits
Author SHA1 Message Date
andreasandClaude Sonnet 5 49b631f02d refactor: extract shared time-series chart renderer from CPU chart
Pulls the CPU chart's SVG-drawing logic into a parameterized
renderTimeSeriesChart(elId, pts, opts) helper (yDomain, colorFor,
clipId, unitSuffix), keeping renderCpuChart's signature and behavior
unchanged. Task 5 will reuse this for the RTT chart.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DEimzMv4Q5EjFg3hoiZ69T
2026-08-17 07:33:22 -04:00
andreas 8204737ea6 fix: use the latest RTT sample, not the oldest, in host info API 2026-08-17 07:28:36 -04:00
andreas 5b2fc8fd46 fix: hide synthetic rtt history keys from plugin accordion list 2026-08-17 07:25:17 -04:00
andreasandClaude Sonnet 5 70c3e93684 fix: remove extra blank line to pass flake8 E303 check
Removed extraneous blank line after conndata() try/except block
that was left during code relocation. Fixes E303 linting error.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DEimzMv4Q5EjFg3hoiZ69T
2026-08-17 07:22:33 -04:00
andreasandClaude Sonnet 5 52968201fa feat: record RTT history per heartbeat for charting
Capture RTT on every heartbeat using the existing plugin_data history
mechanism, recording under rtt_ipv4 or rtt_ipv6 keys. Maintains 100-sample
retention per host and address family.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DEimzMv4Q5EjFg3hoiZ69T
2026-08-17 07:18:01 -04:00
andreasandClaude Sonnet 5 a7b698e6b9 fix: clear flap state when a host is dropped
flap._state persists at module level keyed by (host, service) and only
clears via a RECOVER-triggered quiet window. A host dropped mid-flap with
no RECOVER ever received leaves ok_since permanently None, so the
flapping flag could never clear on its own.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-07 08:35:10 -04:00
andreas 136b10239f update 2026-08-07 07:30:41 -04:00
Andreas WredeandClaude Sonnet 5 1e46f33648 fix: correct plugin import path in docs, wire up orphaned footer template
docs/PLUGIN_DEVELOPMENT.md referenced a nonexistent hbd/plugin.py and
hbd/plugins/ directory; the real module is hbd/client/plugin.py with
plugins under hbd/client/plugins/, as README.md and the actual code
already use. Anyone following the doc would hit ModuleNotFoundError.

foot.html was never included by any template (confirmed via git grep
and template audit) despite being actively maintained; wired it into
about.html. menu.html was fully commented out and unreferenced, so
removed it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 09:51:36 -04:00
andreas 4c3a63e645 update claude guideline 2026-07-31 09:22:57 -04:00
Andreas Wrede 1f4dd7cb1f version 5.4.0
Release / release (push) Successful in 44s
v5.4.0
2026-07-23 13:17:33 -07:00
Andreas WredeandClaude Opus 4.8 ae3f2fc70f feat: gate remote command execution behind allow_remote_command
CMD packets arrive as unauthenticated UDP datagrams, yet every hbc client
executed the shell command they carry without any opt-in. Add an
allow_remote_command config key, default false: when off, the command is
logged and refused with "Refused: allow_remote_command is false" (visible in
the server event log under the command service), and subprocess is never
reached. When on, the client warns at startup that it will execute CMD
packets.

Applied to all four clients that handle CMD — hbc, hbc_windows.py,
hbc_mini.py, and the C hbc_mini — since gating only one leaves the others
wide open. The C client reads the same key from ~/.hbc.json and needs a
rebuild to pick it up. UPD (self-update) is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 13:10:41 -07:00
Andreas WredeandClaude Opus 4.8 4414967bdc feat: flapping detection suppresses notification storms
A (host, service) pair is flapping once it exceeds flap_count warning or
critical notifications within flap_interval minutes. The notification that
trips the state carries "Now flapping!! No more messages!" and every later
one is dropped, including RECOVER. The state ends silently flap_interval
minutes after a RECOVER, provided no further alert arrived meanwhile.

Hooked into notify.send_notification, the single choke point for channel
delivery, so only outbound notifications are suppressed — eventlog keeps
recording, leaving the journal and /log with the full history of the flap.

Threshold alerts key on their metric path, so a flapping disk check cannot
silence a CPU alert; connectivity, boot and shutdown events key on the host
itself. State lives at module level in flap.py and is never pickled.

Flapping pairs surface in Host.stateinfo() and render as an amber badge on
the live dashboard. Config: flap_count (5), flap_interval (10 minutes),
0 in either disables; both editable on the settings page.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 09:53:46 -07:00
Andreas Wrede e3b0e5041f add: info on deplyment 2026-07-23 09:51:32 -07:00
andreasandClaude Fable 5 92982c0ca7 fix: harden events log — non-dict ring entries, off-loop journal reads, no default-config singleton
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 15:51:34 -04:00
andreasandClaude Fable 5 00fdbcaa5b fix: skip journal scheduling when the event loop is not running
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 15:39:42 -04:00
andreasandClaude Fable 5 600e68b509 feat: remove log section from live dashboard (moved to /log)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 15:29:39 -04:00
andreasandClaude Fable 5 b26e853a1c fix: dedup log rows between API seed and websocket tail
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 15:25:01 -04:00
andreasandClaude Fable 5 36adad5b3b feat: dedicated /log page with journal-backed history and nav item
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 15:20:17 -04:00
andreasandClaude Fable 5 7591268a89 feat: GET /api/0/log — paged, filtered events journal API
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 15:15:46 -04:00
andreasandClaude Fable 5 7afc06a329 fix: journal startup/shutdown lifecycle events (fired outside journal lifetime)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 15:11:30 -04:00
andreasandClaude Fable 5 a3f303e6ba feat: eventlog writes to dedicated events journal; init/backfill/close wiring
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 15:03:48 -04:00
andreasandClaude Fable 5 16c2922bea feat: events journal read path with filters and backward pagination
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 14:57:32 -04:00
andreasandClaude Fable 5 0343eb4a24 fix: drop typing names not yet used from journal.py import
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 14:53:24 -04:00
andreasandClaude Fable 5 fb5570155b feat: events journal write primitives (log_event, backfill, get_events_journal)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 14:47:42 -04:00
andreasandClaude Fable 5 8d268adf70 feat: Live Dashboard redesign — 6 columns, hover details, last-alert column
Replaces the 11-column table with severity-sorted record rows: host,
combined status chip, latency, alert chips, time-in-state, and the most
recent alert message. Per-family address/state/latency/last-change move
to a hover card on the status chip and permanently to a new Connectivity
table in the Host Overview info section (host info API now includes
connections). Last-alert is seeded from /api/0/alerts and kept fresh
from the event stream; the event log below uses the same row idiom with
its filters intact. WS reconnect state shows in the toolbar instead of
a modal.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NfPpSpccTWBfZg1FTveyaU
2026-07-10 13:45:03 -04:00
andreasandClaude Fable 5 5d9f161706 feat: extend the record-row design system to Host Overview, Alerts, and About
Extract the settings redesign's tokens and components into shared
static/hbd-ui.css + hbd-ui.js and dedupe settings.html against them.
About becomes yaml-key sections with kv rows; Alerts gets stat tiles,
chip filters, and alert record rows (same fetch/ack logic); Host
Overview keeps its DOM and live-update JS but is re-skinned to the
token system with a page toolbar. Live Dashboard intentionally
untouched pending its own redesign.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NfPpSpccTWBfZg1FTveyaU
2026-07-10 09:37:58 -04:00
andreasandClaude Fable 5 067feacd59 fix: settings toolbar sticks below the site nav instead of scrolling away
Sticky offsets (toolbar, rail, mobile chips, anchor margins) derive from
the measured nav/toolbar heights via CSS variables, since the fixed nav's
height varies with viewport width.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NfPpSpccTWBfZg1FTveyaU
2026-07-10 09:19:21 -04:00
andreasandClaude Fable 5 c564fafe73 fix: settings page not scrollable — restore html/body overflow override
static/style.css pins html/body to height:100% with overflow:hidden for
the dashboard pages; the redesign dropped the counter-override the old
template carried.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NfPpSpccTWBfZg1FTveyaU
2026-07-10 08:20:21 -04:00
andreasandClaude Fable 5 8eeae48964 feat: unified record-row redesign of the settings page
One idiom for every section: mono identifier, badges, fact chips, inline
accordion editor. Scalar config sections merge into one grouped 'server'
section. Sidebar becomes YAML-key nav (mobile: horizontal chips); sticky
toolbar carries pending-changes state and Publish. Staging, publish,
channel CRUD, and permission gating are unchanged — same payload shapes
and endpoints.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NfPpSpccTWBfZg1FTveyaU
2026-07-10 08:08:58 -04:00
andreas 880dc0e33f version 5.3.12
Release / release (push) Successful in 47s
v5.3.12
2026-07-09 20:43:59 -04:00
andreasandClaude Fable 5 f06b2ef9e8 fix: users loop clobbered requesting username in settings filtering
The users-section loop reused 'username' as its loop variable, overwriting
the requesting user's name so host/threshold filtering compared against the
last user in the config. Rename to 'uname' and add a regression test with a
user that is not last in the users dict.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NfPpSpccTWBfZg1FTveyaU
2026-07-09 18:48:05 -04:00
andreasandClaude Fable 5 447b9574a3 fix: type annotations for mypy parity with master
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NfPpSpccTWBfZg1FTveyaU
2026-07-09 17:52:02 -04:00
andreasandClaude Fable 5 e98cca22f7 docs: ownership rules and settings access for owners/managers
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NfPpSpccTWBfZg1FTveyaU
2026-07-09 17:50:24 -04:00
andreasandClaude Fable 5 5fe069a1a2 feat: retire per-channel private flag on profile page
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NfPpSpccTWBfZg1FTveyaU
2026-07-09 16:53:55 -04:00
andreasandClaude Fable 5 ca2868a888 feat: settings UI renders per-user edit rights and owner controls
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NfPpSpccTWBfZg1FTveyaU
2026-07-09 16:52:44 -04:00
andreasandClaude Fable 5 ddb1b2b875 feat: settings page accessible to all authenticated users
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NfPpSpccTWBfZg1FTveyaU
2026-07-09 16:50:01 -04:00
andreasandClaude Fable 5 39c4d45bd7 feat: per-user filtering of settings sections
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NfPpSpccTWBfZg1FTveyaU
2026-07-09 16:48:58 -04:00
andreasandClaude Fable 5 07fefab861 feat: scoped non-admin saves through POST /api/0/config
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NfPpSpccTWBfZg1FTveyaU
2026-07-09 16:46:47 -04:00
andreasandClaude Fable 5 6b4524b30d feat: threshold form payload carries per-config owner
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NfPpSpccTWBfZg1FTveyaU
2026-07-09 16:46:10 -04:00
andreasandClaude Fable 5 a149a1e285 feat: channel ownership rule — owner-presence means private, admin promote/demote
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NfPpSpccTWBfZg1FTveyaU
2026-07-09 16:45:21 -04:00
andreasandClaude Fable 5 dd939ab86e feat: scoped threshold-config merge for non-admin saves
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NfPpSpccTWBfZg1FTveyaU
2026-07-09 16:43:56 -04:00
andreasandClaude Fable 5 5c6f462081 feat: scoped host merge for non-admin config saves
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NfPpSpccTWBfZg1FTveyaU
2026-07-09 16:43:08 -04:00
andreasandClaude Fable 5 98b50dbbf4 feat: ownership visibility helpers for config entities
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NfPpSpccTWBfZg1FTveyaU
2026-07-09 16:42:00 -04:00
andreasandClaude Opus 4.8 960822918a fix: don't re-announce boot/shutdown on SIGHUP restart
Setting args.boot = False after sending the boot message was dead code: the
SIGHUP restart re-execs via os.execv(sys.argv[0], sys.argv) with the original
argv, so the re-launched process re-parsed -b and announced a boot again. The
exiting process also sent a spurious shutdown (send_shutdown armed by -b), so
each config reload looked like a host reboot to the server.

Strip -b/--boot from the argv passed to execv, and skip the shutdown message
in cleanup() when dorestart is set. A real SIGTERM/SIGINT shutdown still sends
shutdown as before.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 15:46:09 -04:00
andreas a376ebba8d version 5.3.11
Release / release (push) Successful in 48s
v5.3.11
2026-06-26 11:39:23 -04:00
andreasandClaude Opus 4.8 f603ef9bd2 fix: declare plugin interval so stale data waits two full intervals
The server inferred each plugin's collection interval from the gap between
the last two received PLG samples, then expired data at interval * 3. After
an outage this guess was wrong: the request_update re-send of collect-once
InfoPlugins produced two close samples, yielding a tiny inferred interval
that purged permanent info data minutes after recovery.

Clients now declare each plugin's interval in the PLG message (_interval).
The server uses it directly (from the first post-recovery sample), expiring
at interval * 3 so live data survives at least two full intervals; interval
0 marks collect-once InfoPlugins that never go stale. Legacy clients omit
the field and fall back to the previous inferred-gap behavior.

Adds _interval to all three clients: hbc, hbc_mini.py, hbc_mini.c.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 11:37:48 -04:00
andreasandClaude Opus 4.8 731545dd0e fix: cap event buffer and replay only recent messages on dashboard connect
data.msgs grew without bound (eventlog appended forever, also persisted in
the pickle), and ws.py replayed the entire history to every dashboard
client on connect - one JSON frame per message - making page reloads
progressively sluggish. Cap the buffer in eventlog() (configurable via
msg_buffer_size, default 500) and slice the WebSocket replay to the last
30, matching the window the HTTP render already uses.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 11:37:40 -04:00
andreasandClaude Opus 4.8 356c77b0b8 fix: strip plugin timers when pickling Host to prevent save failure
Host.plugin_timers holds asyncio TimerHandle objects (and their lambda
callbacks) which are not picklable, causing the 5-minute state save to
fail with "Can't pickle local object reset_plugin_timer.<locals>.<lambda>".
Add Host.__getstate__ to reset plugin_timers to {} before pickling,
mirroring Connection.__getstate__; timers are recreated on the next PLG.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 11:37:35 -04:00
andreasandClaude Sonnet 4.6 6282077fe0 fix: correct zero-safe pathconf checks and connectivity prefix match
- Use `is not None` for pathconf values so 0 is not silently dropped
- Broaden connectivity prefix check to catch bare "connectivity" key

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 13:07:54 -04:00
Andreas WredeandClaude Sonnet 4.6 ddd857173b fix: address security vulnerabilities from audit
- Path traversal: confine avatar file serving to avatar_dir (defaults to
  config file directory); validate on both read and write
- UDP owner injection: server-configured owner now takes precedence over
  UDP-supplied value, matching the documented intent
- Open redirect: reject non-relative next= values after login
- Stored XSS: enable Jinja2 autoescape on all template environments;
  add escHtml() helper in live.html and apply to all innerHTML sinks
  sourced from network data (host names, addrs, states, log messages)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 13:06:05 -04:00