- Rename package fablednetmon → fabledscryer throughout - Multi-dashboard: ownership, per-user defaults, HTMX edit (add/remove/reorder) - Read-only share tokens scoped to individual dashboards - Dashboard edit is HTMX-driven (no page reloads) - Plugin management system: remote catalog, download/install, hot-reload, in-app restart - plugin_index.py: fetch/cache remote index.yaml; default URL → bvandeusen/fabledscryer-plugins - plugin_manager.py: download_and_install_plugin, hot_reload_plugin, restart_app - ZIP extraction handles GitHub archive formats (name-v1.0.0/, name-main/) - Settings split into tabbed sections: General, Notifications, Ansible, Plugins - Plugins tab: catalog browser (HTMX), install/activate/update/restart actions - UI/branding: dark palette (#07071a), crystal ball SVG logo, animated star field, Libertinus Serif applied to headings, nav, labels, and section titles - Widget registry (core/widgets.py) for dashboard plugin integration - UPS widget.html (dashboard card) and settings/_tabs.html include - Migrations 0005–0008: dashboards, is_default, ownership, share tokens - docs/plugins/: writing-a-plugin.md updated with publishing guide, index.yaml.example template for fabledscryer-plugins repo Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4.1 KiB
Core Monitors
Fabled Scryer ships two built-in monitors: Ping and DNS. Both run as asyncio scheduled tasks on the same event loop as the web server, with no separate processes.
Ping Monitor
Source: fabledscryer/monitors/ping.py
Scheduler task: ping_monitor in fabledscryer/app.py
Interval: monitors.poll_interval_seconds (default 60s), runs on startup
How It Works
On each tick, the scheduler fetches all hosts with ping_enabled = true and calls ping_check(host, session) for each.
ping_check() probes the host using:
- ICMP if
host.probe_type == "icmp"— uses the systempingbinary (/bin/pingor equivalent). Requiresiputils-pingin Docker. - TCP if
host.probe_type == "tcp"(default) — attempts an async TCP connection tohost.address:host.probe_port(default port 80).
Each probe writes a PingResult row and calls record_metric():
source_module |
resource_name |
metric_name |
value |
|---|---|---|---|
ping |
host.name |
response_time_ms |
measured latency, or 0.0 if down |
ping |
host.name |
up |
1.0 if up, 0.0 if down |
Alert rule note: Because response_time_ms is recorded as 0.0 when a host is down, a latency rule (e.g. response_time_ms > 500) will not fire on complete outages. Use a separate rule on up == 0.0 to detect host down events.
Data Model
ping_results table (defined in fabledscryer/models/monitors.py):
| Column | Type | Description |
|---|---|---|
id |
UUID | Primary key |
host_id |
FK → hosts | |
probed_at |
timestamp UTC | When the probe ran |
status |
enum up/down |
Result |
response_time_ms |
float | Null if down |
Old rows are pruned by the data_cleanup task (default: 90 days).
UI
- Dashboard widget — live-updating via HTMX polling (
/ping/rows) /ping/page — full page with 30-pill history per host and threshold settings form- Hosts list — shows latest ping status dot and latency
DNS Monitor
Source: fabledscryer/monitors/dns.py
Scheduler task: dns_monitor in fabledscryer/app.py
Interval: monitors.poll_interval_seconds (default 60s), runs on startup
How It Works
On each tick, the scheduler fetches all hosts with dns_enabled = true and calls dns_check(host, session) for each.
dns_check() resolves host.address using the system resolver. If host.dns_expected_ip is set, the check passes only if at least one returned A/AAAA record exactly matches that string. If dns_expected_ip is null, any successful resolution counts as a pass.
Each check writes a DnsResult row and calls record_metric():
source_module |
resource_name |
metric_name |
value |
|---|---|---|---|
dns |
host.name |
resolved |
1.0 if resolved, 0.0 if failed |
dns |
host.name |
ip_changed |
1.0 if IP changed from last successful result, 0.0 otherwise |
Data Model
dns_results table (defined in fabledscryer/models/monitors.py):
| Column | Type | Description |
|---|---|---|
id |
UUID | Primary key |
host_id |
FK → hosts | |
resolved_at |
timestamp UTC | When the check ran |
status |
enum resolved/failed |
Result |
resolved_ip |
str | First returned A/AAAA record; null if failed |
ip_changed is computed by comparing resolved_ip of the current result against the most recent prior resolved result for the same host.
UI
- Dashboard widget — live-updating via HTMX polling (
/dns/rows) /dns/page — full page showing all DNS-enabled hosts with status, resolved IP, and timestamp- Hosts list — shows latest DNS status dot
Host Configuration
Hosts are managed at /hosts/. Both monitors are configured per-host:
| Field | Description |
|---|---|
ping_enabled |
Enable ping probing for this host |
probe_type |
tcp (default) or icmp |
probe_port |
TCP port to connect to (default 80; ignored for ICMP) |
dns_enabled |
Enable DNS resolution checks |
dns_expected_ip |
If set, the resolved IP must match this string exactly |
poll_interval_seconds |
Per-host override for the global poll interval; null uses global |