pengumon 0.20.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pengumon-0.20.1/LICENSE +21 -0
- pengumon-0.20.1/PKG-INFO +265 -0
- pengumon-0.20.1/README.md +240 -0
- pengumon-0.20.1/pyproject.toml +56 -0
- pengumon-0.20.1/setup.cfg +4 -0
- pengumon-0.20.1/src/pengumon/__init__.py +3 -0
- pengumon-0.20.1/src/pengumon/_vendor/__init__.py +1 -0
- pengumon-0.20.1/src/pengumon/_vendor/homeguard/__init__.py +10 -0
- pengumon-0.20.1/src/pengumon/_vendor/homeguard/_vendored.py +13 -0
- pengumon-0.20.1/src/pengumon/_vendor/homeguard/db.py +281 -0
- pengumon-0.20.1/src/pengumon/_vendor/homeguard/security.py +1039 -0
- pengumon-0.20.1/src/pengumon/analysis/__init__.py +1 -0
- pengumon-0.20.1/src/pengumon/analysis/incident.py +354 -0
- pengumon-0.20.1/src/pengumon/api/__init__.py +5 -0
- pengumon-0.20.1/src/pengumon/api/common.py +30 -0
- pengumon-0.20.1/src/pengumon/api/devices.py +206 -0
- pengumon-0.20.1/src/pengumon/api/history.py +51 -0
- pengumon-0.20.1/src/pengumon/api/incidents.py +60 -0
- pengumon-0.20.1/src/pengumon/api/notifications.py +23 -0
- pengumon-0.20.1/src/pengumon/api/ops.py +81 -0
- pengumon-0.20.1/src/pengumon/api/path.py +351 -0
- pengumon-0.20.1/src/pengumon/api/payloads.py +10 -0
- pengumon-0.20.1/src/pengumon/api/reports.py +53 -0
- pengumon-0.20.1/src/pengumon/api/router.py +13 -0
- pengumon-0.20.1/src/pengumon/api/state.py +74 -0
- pengumon-0.20.1/src/pengumon/api/targets.py +62 -0
- pengumon-0.20.1/src/pengumon/api/ws.py +97 -0
- pengumon-0.20.1/src/pengumon/capabilities/__init__.py +179 -0
- pengumon-0.20.1/src/pengumon/capabilities/features.py +93 -0
- pengumon-0.20.1/src/pengumon/cli.py +285 -0
- pengumon-0.20.1/src/pengumon/clients/__init__.py +2 -0
- pengumon-0.20.1/src/pengumon/clients/api.py +182 -0
- pengumon-0.20.1/src/pengumon/clients/component.py +24 -0
- pengumon-0.20.1/src/pengumon/clients/settings.py +13 -0
- pengumon-0.20.1/src/pengumon/components.py +135 -0
- pengumon-0.20.1/src/pengumon/config.py +140 -0
- pengumon-0.20.1/src/pengumon/db/clients.py +44 -0
- pengumon-0.20.1/src/pengumon/db/core.py +183 -0
- pengumon-0.20.1/src/pengumon/db/devices.py +141 -0
- pengumon-0.20.1/src/pengumon/db/incidents.py +286 -0
- pengumon-0.20.1/src/pengumon/db/migrations/__init__.py +100 -0
- pengumon-0.20.1/src/pengumon/db/migrations/common.py +21 -0
- pengumon-0.20.1/src/pengumon/db/migrations/v001_baseline.py +35 -0
- pengumon-0.20.1/src/pengumon/db/migrations/v002_samples_rollups.py +70 -0
- pengumon-0.20.1/src/pengumon/db/migrations/v003_custom_targets.py +14 -0
- pengumon-0.20.1/src/pengumon/db/migrations/v004_meta.py +14 -0
- pengumon-0.20.1/src/pengumon/db/migrations/v005_docsis_event_log.py +21 -0
- pengumon-0.20.1/src/pengumon/db/migrations/v006_incident_kind.py +67 -0
- pengumon-0.20.1/src/pengumon/db/migrations/v007_error_kind.py +16 -0
- pengumon-0.20.1/src/pengumon/db/migrations/v008_cause_code.py +13 -0
- pengumon-0.20.1/src/pengumon/db/migrations/v009_device_tables.py +114 -0
- pengumon-0.20.1/src/pengumon/db/migrations/v010_client_counts.py +21 -0
- pengumon-0.20.1/src/pengumon/db/migrations/v011_rollup_percentiles.py +38 -0
- pengumon-0.20.1/src/pengumon/db/migrations/v012_incident_loss.py +17 -0
- pengumon-0.20.1/src/pengumon/db/migrations/v013_path_targets.py +15 -0
- pengumon-0.20.1/src/pengumon/db/migrations/v014_path_versions.py +44 -0
- pengumon-0.20.1/src/pengumon/db/path.py +347 -0
- pengumon-0.20.1/src/pengumon/db/probes.py +385 -0
- pengumon-0.20.1/src/pengumon/db/repository.py +16 -0
- pengumon-0.20.1/src/pengumon/db/retention.py +37 -0
- pengumon-0.20.1/src/pengumon/db/samples.py +71 -0
- pengumon-0.20.1/src/pengumon/db/schema.py +304 -0
- pengumon-0.20.1/src/pengumon/db/storage.py +44 -0
- pengumon-0.20.1/src/pengumon/db/targets.py +27 -0
- pengumon-0.20.1/src/pengumon/devices/__init__.py +5 -0
- pengumon-0.20.1/src/pengumon/devices/component.py +32 -0
- pengumon-0.20.1/src/pengumon/devices/device.py +264 -0
- pengumon-0.20.1/src/pengumon/devices/monitors.py +269 -0
- pengumon-0.20.1/src/pengumon/devices/registry.py +191 -0
- pengumon-0.20.1/src/pengumon/devices/samples.py +109 -0
- pengumon-0.20.1/src/pengumon/devices/settings.py +130 -0
- pengumon-0.20.1/src/pengumon/devices/snapshots.py +26 -0
- pengumon-0.20.1/src/pengumon/devices/summary.py +107 -0
- pengumon-0.20.1/src/pengumon/devices/wan.py +75 -0
- pengumon-0.20.1/src/pengumon/drivers/__init__.py +15 -0
- pengumon-0.20.1/src/pengumon/drivers/asuswrt.py +677 -0
- pengumon-0.20.1/src/pengumon/drivers/common.py +77 -0
- pengumon-0.20.1/src/pengumon/drivers/http.py +112 -0
- pengumon-0.20.1/src/pengumon/drivers/openwrt.py +493 -0
- pengumon-0.20.1/src/pengumon/drivers/snmp.py +357 -0
- pengumon-0.20.1/src/pengumon/drivers/snmp_ber.py +386 -0
- pengumon-0.20.1/src/pengumon/drivers/xfinity_xb.py +324 -0
- pengumon-0.20.1/src/pengumon/engine/classifier/__init__.py +196 -0
- pengumon-0.20.1/src/pengumon/engine/classifier/component.py +35 -0
- pengumon-0.20.1/src/pengumon/engine/classifier/metrics.py +145 -0
- pengumon-0.20.1/src/pengumon/engine/classifier/rules.py +275 -0
- pengumon-0.20.1/src/pengumon/engine/classifier/tracker.py +129 -0
- pengumon-0.20.1/src/pengumon/engine/classifier/verdict.py +38 -0
- pengumon-0.20.1/src/pengumon/engine/classifier_settings.py +28 -0
- pengumon-0.20.1/src/pengumon/engine/device_links.py +100 -0
- pengumon-0.20.1/src/pengumon/engine/discovery/__init__.py +16 -0
- pengumon-0.20.1/src/pengumon/engine/discovery/addresses.py +110 -0
- pengumon-0.20.1/src/pengumon/engine/discovery/api.py +51 -0
- pengumon-0.20.1/src/pengumon/engine/discovery/component.py +129 -0
- pengumon-0.20.1/src/pengumon/engine/discovery/devices.py +193 -0
- pengumon-0.20.1/src/pengumon/engine/discovery/draft.py +244 -0
- pengumon-0.20.1/src/pengumon/engine/discovery/drift.py +298 -0
- pengumon-0.20.1/src/pengumon/engine/discovery/engine.py +282 -0
- pengumon-0.20.1/src/pengumon/engine/discovery/infer.py +402 -0
- pengumon-0.20.1/src/pengumon/engine/discovery/jobs.py +129 -0
- pengumon-0.20.1/src/pengumon/engine/discovery/lookups.py +180 -0
- pengumon-0.20.1/src/pengumon/engine/discovery/settings.py +13 -0
- pengumon-0.20.1/src/pengumon/engine/discovery/traces.py +105 -0
- pengumon-0.20.1/src/pengumon/engine/discovery/upnp.py +322 -0
- pengumon-0.20.1/src/pengumon/engine/icmp_base.py +48 -0
- pengumon-0.20.1/src/pengumon/engine/icmp_dual.py +96 -0
- pengumon-0.20.1/src/pengumon/engine/icmp_linux.py +481 -0
- pengumon-0.20.1/src/pengumon/engine/icmp_macos.py +292 -0
- pengumon-0.20.1/src/pengumon/engine/icmp_packet.py +158 -0
- pengumon-0.20.1/src/pengumon/engine/icmp_select.py +112 -0
- pengumon-0.20.1/src/pengumon/engine/icmp_windows.py +457 -0
- pengumon-0.20.1/src/pengumon/engine/line_load.py +62 -0
- pengumon-0.20.1/src/pengumon/engine/load.py +280 -0
- pengumon-0.20.1/src/pengumon/engine/load_settings.py +19 -0
- pengumon-0.20.1/src/pengumon/engine/loop.py +76 -0
- pengumon-0.20.1/src/pengumon/engine/mtr.py +172 -0
- pengumon-0.20.1/src/pengumon/engine/netinfo.py +275 -0
- pengumon-0.20.1/src/pengumon/engine/path.py +211 -0
- pengumon-0.20.1/src/pengumon/engine/path_preview.py +162 -0
- pengumon-0.20.1/src/pengumon/engine/path_rules.py +41 -0
- pengumon-0.20.1/src/pengumon/engine/prober.py +370 -0
- pengumon-0.20.1/src/pengumon/engine/prober_settings.py +60 -0
- pengumon-0.20.1/src/pengumon/engine/probing.py +46 -0
- pengumon-0.20.1/src/pengumon/grades.py +68 -0
- pengumon-0.20.1/src/pengumon/incidents/__init__.py +0 -0
- pengumon-0.20.1/src/pengumon/incidents/loss.py +73 -0
- pengumon-0.20.1/src/pengumon/incidents/model.py +76 -0
- pengumon-0.20.1/src/pengumon/incidents/taxonomy.py +111 -0
- pengumon-0.20.1/src/pengumon/lines/__init__.py +31 -0
- pengumon-0.20.1/src/pengumon/lines/docsis.py +428 -0
- pengumon-0.20.1/src/pengumon/lines/docsis_model.py +52 -0
- pengumon-0.20.1/src/pengumon/live/__init__.py +0 -0
- pengumon-0.20.1/src/pengumon/live/component.py +30 -0
- pengumon-0.20.1/src/pengumon/live/message.py +22 -0
- pengumon-0.20.1/src/pengumon/main.py +126 -0
- pengumon-0.20.1/src/pengumon/metrics.py +43 -0
- pengumon-0.20.1/src/pengumon/models/__init__.py +3 -0
- pengumon-0.20.1/src/pengumon/models/hosts.py +51 -0
- pengumon-0.20.1/src/pengumon/models/probes.py +127 -0
- pengumon-0.20.1/src/pengumon/notify.py +244 -0
- pengumon-0.20.1/src/pengumon/notify_settings.py +86 -0
- pengumon-0.20.1/src/pengumon/observability/__init__.py +6 -0
- pengumon-0.20.1/src/pengumon/observability/export.py +221 -0
- pengumon-0.20.1/src/pengumon/observability/gauges.py +136 -0
- pengumon-0.20.1/src/pengumon/observability/grafana.py +54 -0
- pengumon-0.20.1/src/pengumon/observability/logs.py +254 -0
- pengumon-0.20.1/src/pengumon/observability/telemetry.py +82 -0
- pengumon-0.20.1/src/pengumon/paths.py +108 -0
- pengumon-0.20.1/src/pengumon/reports/__init__.py +3 -0
- pengumon-0.20.1/src/pengumon/reports/common.py +51 -0
- pengumon-0.20.1/src/pengumon/reports/dossier.py +285 -0
- pengumon-0.20.1/src/pengumon/reports/loss_grid.py +38 -0
- pengumon-0.20.1/src/pengumon/reports/queries.py +59 -0
- pengumon-0.20.1/src/pengumon/reports/reliability.py +71 -0
- pengumon-0.20.1/src/pengumon/reports/uptime.py +80 -0
- pengumon-0.20.1/src/pengumon/secrets.py +355 -0
- pengumon-0.20.1/src/pengumon/security.py +87 -0
- pengumon-0.20.1/src/pengumon/service.py +717 -0
- pengumon-0.20.1/src/pengumon/services/__init__.py +1 -0
- pengumon-0.20.1/src/pengumon/services/api.py +55 -0
- pengumon-0.20.1/src/pengumon/services/checker.py +260 -0
- pengumon-0.20.1/src/pengumon/services/component.py +63 -0
- pengumon-0.20.1/src/pengumon/services/settings.py +41 -0
- pengumon-0.20.1/src/pengumon/ui.py +91 -0
- pengumon-0.20.1/src/pengumon/web/assets.py +76 -0
- pengumon-0.20.1/src/pengumon/web/pages.py +31 -0
- pengumon-0.20.1/src/pengumon/web/settings.py +27 -0
- pengumon-0.20.1/src/pengumon/web/static/components.css +384 -0
- pengumon-0.20.1/src/pengumon/web/static/dialogs/add-target/dialog.css +6 -0
- pengumon-0.20.1/src/pengumon/web/static/dialogs/add-target/dialog.html +34 -0
- pengumon-0.20.1/src/pengumon/web/static/dialogs/add-target/dialog.js +168 -0
- pengumon-0.20.1/src/pengumon/web/static/dialogs/alerts/dialog.css +27 -0
- pengumon-0.20.1/src/pengumon/web/static/dialogs/alerts/dialog.html +33 -0
- pengumon-0.20.1/src/pengumon/web/static/dialogs/alerts/dialog.js +346 -0
- pengumon-0.20.1/src/pengumon/web/static/dialogs/device-setup/dialog.css +5 -0
- pengumon-0.20.1/src/pengumon/web/static/dialogs/device-setup/dialog.html +21 -0
- pengumon-0.20.1/src/pengumon/web/static/dialogs/device-setup/dialog.js +124 -0
- pengumon-0.20.1/src/pengumon/web/static/dialogs/dossier/dialog.css +34 -0
- pengumon-0.20.1/src/pengumon/web/static/dialogs/dossier/dialog.html +23 -0
- pengumon-0.20.1/src/pengumon/web/static/dialogs/dossier/dialog.js +140 -0
- pengumon-0.20.1/src/pengumon/web/static/dialogs/dossier/markdown.js +62 -0
- pengumon-0.20.1/src/pengumon/web/static/dialogs/layout/dialog.css +16 -0
- pengumon-0.20.1/src/pengumon/web/static/dialogs/layout/dialog.html +17 -0
- pengumon-0.20.1/src/pengumon/web/static/dialogs/path-editor/dialog.css +130 -0
- pengumon-0.20.1/src/pengumon/web/static/dialogs/path-editor/dialog.html +51 -0
- pengumon-0.20.1/src/pengumon/web/static/dialogs/path-editor/dialog.js +936 -0
- pengumon-0.20.1/src/pengumon/web/static/dialogs/sign-out/dialog.css +11 -0
- pengumon-0.20.1/src/pengumon/web/static/dialogs/sign-out/dialog.html +24 -0
- pengumon-0.20.1/src/pengumon/web/static/dialogs/sign-out/dialog.js +16 -0
- pengumon-0.20.1/src/pengumon/web/static/dialogs/target-history/dialog.css +30 -0
- pengumon-0.20.1/src/pengumon/web/static/dialogs/target-history/dialog.js +606 -0
- pengumon-0.20.1/src/pengumon/web/static/dialogs/trace/dialog.css +12 -0
- pengumon-0.20.1/src/pengumon/web/static/dialogs/trace/dialog.html +21 -0
- pengumon-0.20.1/src/pengumon/web/static/dialogs/trace/dialog.js +142 -0
- pengumon-0.20.1/src/pengumon/web/static/favicon.ico +0 -0
- pengumon-0.20.1/src/pengumon/web/static/favicon.svg +46 -0
- pengumon-0.20.1/src/pengumon/web/static/icons/app-180.png +0 -0
- pengumon-0.20.1/src/pengumon/web/static/icons/app-192.png +0 -0
- pengumon-0.20.1/src/pengumon/web/static/icons/app-512.png +0 -0
- pengumon-0.20.1/src/pengumon/web/static/js/bus.js +36 -0
- pengumon-0.20.1/src/pengumon/web/static/js/chart-theme.js +29 -0
- pengumon-0.20.1/src/pengumon/web/static/js/chrome.js +266 -0
- pengumon-0.20.1/src/pengumon/web/static/js/device-feed.js +65 -0
- pengumon-0.20.1/src/pengumon/web/static/js/device-panel.js +79 -0
- pengumon-0.20.1/src/pengumon/web/static/js/devices.js +49 -0
- pengumon-0.20.1/src/pengumon/web/static/js/dispatch.js +25 -0
- pengumon-0.20.1/src/pengumon/web/static/js/grades.js +28 -0
- pengumon-0.20.1/src/pengumon/web/static/js/help.js +105 -0
- pengumon-0.20.1/src/pengumon/web/static/js/layout.js +315 -0
- pengumon-0.20.1/src/pengumon/web/static/js/live.js +78 -0
- pengumon-0.20.1/src/pengumon/web/static/js/main.js +39 -0
- pengumon-0.20.1/src/pengumon/web/static/js/panels/index.js +33 -0
- pengumon-0.20.1/src/pengumon/web/static/js/path-api.js +182 -0
- pengumon-0.20.1/src/pengumon/web/static/js/path.js +247 -0
- pengumon-0.20.1/src/pengumon/web/static/js/session.js +23 -0
- pengumon-0.20.1/src/pengumon/web/static/js/sparkline.js +97 -0
- pengumon-0.20.1/src/pengumon/web/static/js/state.js +16 -0
- pengumon-0.20.1/src/pengumon/web/static/js/tabs.js +98 -0
- pengumon-0.20.1/src/pengumon/web/static/js/util.js +268 -0
- pengumon-0.20.1/src/pengumon/web/static/manifest.webmanifest +14 -0
- pengumon-0.20.1/src/pengumon/web/static/page.css +142 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/diagnosis/help.js +11 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/diagnosis/panel.css +124 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/diagnosis/panel.html +21 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/diagnosis/panel.js +339 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/diagnosis/panel.json +1 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/heat/panel.css +8 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/heat/panel.html +11 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/heat/panel.js +83 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/heat/panel.json +1 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/incidents/help.js +16 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/incidents/incidents.js +310 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/incidents/inspector-charts.js +251 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/incidents/inspector.css +32 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/incidents/inspector.html +64 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/incidents/inspector.js +283 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/incidents/panel.css +40 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/incidents/panel.html +33 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/incidents/panel.js +32 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/incidents/panel.json +1 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/latency/charts.js +396 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/latency/history.js +175 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/latency/panel.css +16 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/latency/panel.html +22 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/latency/panel.js +33 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/latency/panel.json +1 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/line/channel-charts.js +55 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/line/channel-history.js +247 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/line/channels.css +15 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/line/channels.html +50 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/line/channels.js +143 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/line/docsis.js +181 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/line/help.js +33 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/line/panel.css +24 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/line/panel.html +29 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/line/panel.js +58 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/line/panel.json +1 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/matrix/help.js +24 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/matrix/panel.css +30 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/matrix/panel.html +26 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/matrix/panel.js +153 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/matrix/panel.json +1 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/report/panel.css +26 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/report/panel.html +12 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/report/panel.js +14 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/report/panel.json +1 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/report/report.js +123 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/router/clients.js +373 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/router/help.js +38 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/router/history.css +8 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/router/history.html +20 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/router/history.js +295 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/router/network.css +46 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/router/network.html +45 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/router/panel.css +28 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/router/panel.html +33 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/router/panel.js +180 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/router/panel.json +1 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/router/topology.js +388 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/services/help.js +13 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/services/panel.css +20 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/services/panel.html +9 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/services/panel.js +15 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/services/panel.json +1 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/services/services.js +196 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/strips/panel.css +26 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/strips/panel.html +15 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/strips/panel.js +116 -0
- pengumon-0.20.1/src/pengumon/web/static/panels/strips/panel.json +1 -0
- pengumon-0.20.1/src/pengumon/web/static/style.css +113 -0
- pengumon-0.20.1/src/pengumon/web/static/tokens.css +129 -0
- pengumon-0.20.1/src/pengumon/web/static/vendor/README.md +21 -0
- pengumon-0.20.1/src/pengumon/web/static/vendor/chart.umd.min.js +20 -0
- pengumon-0.20.1/src/pengumon/web/templates/features/grafana.html +10 -0
- pengumon-0.20.1/src/pengumon/web/templates/icons.svg +70 -0
- pengumon-0.20.1/src/pengumon/web/templates/index.html +80 -0
- pengumon-0.20.1/src/pengumon/webhook.py +172 -0
- pengumon-0.20.1/src/pengumon.egg-info/PKG-INFO +265 -0
- pengumon-0.20.1/src/pengumon.egg-info/SOURCES.txt +301 -0
- pengumon-0.20.1/src/pengumon.egg-info/dependency_links.txt +1 -0
- pengumon-0.20.1/src/pengumon.egg-info/entry_points.txt +5 -0
- pengumon-0.20.1/src/pengumon.egg-info/requires.txt +16 -0
- pengumon-0.20.1/src/pengumon.egg-info/top_level.txt +1 -0
pengumon-0.20.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 PenguPowered
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
pengumon-0.20.1/PKG-INFO
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pengumon
|
|
3
|
+
Version: 0.20.1
|
|
4
|
+
Summary: Containerized network diagnostics, hop-by-hop latency tracker, and outage root-cause classifier
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: fastapi>=0.110.0
|
|
10
|
+
Requires-Dist: uvicorn>=0.29.0
|
|
11
|
+
Requires-Dist: websockets>=12.0
|
|
12
|
+
Requires-Dist: dnspython>=2.6.1
|
|
13
|
+
Requires-Dist: aiosqlite>=0.20.0
|
|
14
|
+
Requires-Dist: pydantic>=2.7.0
|
|
15
|
+
Requires-Dist: pydantic-settings>=2.2.1
|
|
16
|
+
Requires-Dist: pyyaml>=6.0.1
|
|
17
|
+
Requires-Dist: jinja2>=3.1.3
|
|
18
|
+
Requires-Dist: httpx>=0.27.0
|
|
19
|
+
Requires-Dist: opentelemetry-sdk<2,>=1.45.1
|
|
20
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-http<2,>=1.45.1
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest>=8.1.0; extra == "dev"
|
|
23
|
+
Requires-Dist: pytest-asyncio>=0.23.6; extra == "dev"
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
<p align="center">
|
|
27
|
+
<img src="docs/images/logo.png" width="112" height="112" alt="">
|
|
28
|
+
</p>
|
|
29
|
+
|
|
30
|
+
<h1 align="center">Pengumon</h1>
|
|
31
|
+
|
|
32
|
+
<p align="center">
|
|
33
|
+
<a href="https://github.com/pengupowered/pengumon/actions/workflows/ci.yml"><img src="https://github.com/pengupowered/pengumon/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI status"></a>
|
|
34
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT license"></a>
|
|
35
|
+
</p>
|
|
36
|
+
|
|
37
|
+
<p align="center">
|
|
38
|
+
<b>Find out where your internet actually breaks: your Wi-Fi, your modem, your provider, or the internet beyond.</b><br>
|
|
39
|
+
A self-hosted network monitor that pings every hop once a second, explains each outage, and keeps the evidence.
|
|
40
|
+
</p>
|
|
41
|
+
|
|
42
|
+
<p align="center">
|
|
43
|
+
<img src="docs/images/overview-dark.png" alt="The Pengumon dashboard: path health from this host through the router, modem and three provider hops to the internet, a 24-hour latency chart and the cable modem's signal" width="100%">
|
|
44
|
+
</p>
|
|
45
|
+
|
|
46
|
+
## Why
|
|
47
|
+
|
|
48
|
+
"The internet is slow again" is hard to act on. Was it the Wi-Fi, the cable to the modem, a congested node at the
|
|
49
|
+
provider, or one far-away server? Pengumon watches each stage of the path all the time, so when something goes wrong
|
|
50
|
+
you can see where it started, how long it lasted and what the modem and router were doing at that moment, and hand your
|
|
51
|
+
provider a report instead of a hunch.
|
|
52
|
+
|
|
53
|
+
## Features
|
|
54
|
+
|
|
55
|
+
- **The whole path, live.** This host → router → modem → each provider hop → the internet, with the median latency,
|
|
56
|
+
loss and jitter of every stage and how much each link adds.
|
|
57
|
+
- **Every outage explained.** A classifier places each problem in your home network, the line, the provider or beyond,
|
|
58
|
+
and records an incident with per-hop evidence, the modem's signal and the line load at the time.
|
|
59
|
+
- **Busy is not broken.** Router WAN traffic is taken into account, so lag while someone is downloading is marked as
|
|
60
|
+
expected rather than reported as a fault.
|
|
61
|
+
- **Patterns over time.** Loss by hour of the week, per-target uptime, a reliability report, and an ISP dossier to send
|
|
62
|
+
to support. Click any hour or uptime block to see the incidents in it.
|
|
63
|
+
- **Your modem and router.** DOCSIS channel levels and codeword errors; router CPU, memory, WAN traffic, connected
|
|
64
|
+
clients and mesh nodes. Drivers are pluggable.
|
|
65
|
+
- **Your own apps too.** HTTP health checks for self-hosted services, with incidents and alerts by [ntfy](https://ntfy.sh) or webhook.
|
|
66
|
+
- **Small and private.** One container and one SQLite file. Nothing leaves your network unless you turn on
|
|
67
|
+
notifications or the optional OpenTelemetry export. Works on a phone.
|
|
68
|
+
|
|
69
|
+
## Install
|
|
70
|
+
|
|
71
|
+
Pengumon runs natively on Windows, macOS and Linux, or in a container. One command per system; each link goes to that
|
|
72
|
+
system's section in [docs/install.md](docs/install.md), with what it needs, what it sets up and how to remove it.
|
|
73
|
+
|
|
74
|
+
**Windows** 10/11 (no administrator rights), in PowerShell ([details](docs/install.md#windows)):
|
|
75
|
+
|
|
76
|
+
```powershell
|
|
77
|
+
irm https://github.com/pengupowered/pengumon/releases/latest/download/install.ps1 | iex
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**macOS** (Apple silicon or Intel) ([details](docs/install.md#macos)), **Linux** (systemd, x86-64 or arm64)
|
|
81
|
+
([details](docs/install.md#linux)) and **Raspberry Pi** (64-bit OS only) ([details](docs/install.md#raspberry-pi)) use the
|
|
82
|
+
same line:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
curl -fsSL https://github.com/pengupowered/pengumon/releases/latest/download/install.sh | sh
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Container** (Docker or Podman on Linux): `docker compose up -d` with the published image, as below
|
|
89
|
+
([details](docs/install.md#container)).
|
|
90
|
+
|
|
91
|
+
The scripts download the release, check every file against its hash, set up Pengumon's own Python, start it in the
|
|
92
|
+
background so it survives reboots, and open `http://127.0.0.1:8000` (this computer only until you allow more, with an
|
|
93
|
+
access token). On Linux and macOS the script asks for sudo once, says why, and has a `--user` option to avoid it. Run
|
|
94
|
+
the same command again to upgrade (your data is backed up first). Not tested yet on real Windows, macOS or Raspberry Pi
|
|
95
|
+
hardware: see [what has been tested where](docs/install.md#what-has-been-tested-where).
|
|
96
|
+
|
|
97
|
+
> [!IMPORTANT]
|
|
98
|
+
> Anyone who can reach port 8000 can read the data and change settings until you set `PENGUMON_ACCESS_TOKEN`. The
|
|
99
|
+
> default listens on this computer only. See [docs/security.md](docs/security.md).
|
|
100
|
+
|
|
101
|
+
**Container**, the short version (Linux host with Docker or Podman, x86-64 or arm64; host networking so the probes
|
|
102
|
+
measure the host's own path):
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
mkdir pengumon && cd pengumon
|
|
106
|
+
curl -fsSL https://raw.githubusercontent.com/pengupowered/pengumon/main/compose.release.yaml -o compose.yaml
|
|
107
|
+
curl -fsSL https://raw.githubusercontent.com/pengupowered/pengumon/main/config.example.yaml -o config.yaml
|
|
108
|
+
# no targets in it: Pengumon finds your network path itself
|
|
109
|
+
docker compose up -d # or: podman compose up -d
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Open `http://<host>:8000`. Rootless Podman needs unprivileged ping allowed once, and Docker Desktop (macOS, Windows) is
|
|
113
|
+
not supported for this: both are explained in [docs/install.md](docs/install.md#container), as is building from source.
|
|
114
|
+
`pengumon doctor` says whether probing works here and, if not, what fixes it.
|
|
115
|
+
|
|
116
|
+
## First run
|
|
117
|
+
|
|
118
|
+
Nothing to configure. When you open the dashboard for the first time, Pengumon traces the path from this computer to the
|
|
119
|
+
internet by itself (it takes a few seconds; nothing about your path leaves your computer at this point) and **Path health**
|
|
120
|
+
shows a banner, "Pengumon found your network path". Press **Review** to see each hop it found, with a role (your
|
|
121
|
+
network, the line, your provider, the internet), how sure it is (Sure, Likely or Unsure) and why, then **Apply**. Until
|
|
122
|
+
you apply, the default targets keep being probed.
|
|
123
|
+
|
|
124
|
+
Later, **Path health > Edit** opens the same editor: rename, re-role, reorder, add, remove, or press **Rediscover** to
|
|
125
|
+
trace again and see what changed. Details: [docs/path-editor.md](docs/path-editor.md) and
|
|
126
|
+
[docs/discovery.md](docs/discovery.md), which lists exactly what discovery sends where.
|
|
127
|
+
|
|
128
|
+
## Telling it about your network
|
|
129
|
+
|
|
130
|
+
The probe path is edited in the dashboard, not in `config.yaml`: **Path health > Edit** shows the hops as cards in path
|
|
131
|
+
order (see [First run](#first-run) and [docs/path-editor.md](docs/path-editor.md)). **Export** and **Import** move a
|
|
132
|
+
path between installs as YAML.
|
|
133
|
+
|
|
134
|
+
Upgrading from 0.17 or older: the first start imports config.yaml's `targets:` and the targets added from the dashboard
|
|
135
|
+
into the database once, with their ids, so all history stays attached. From then on config.yaml's `targets:` is ignored
|
|
136
|
+
(the startup log says so); every other section stays in `config.yaml`. A `targets:` list in config.yaml is now just an optional fixed list for the first start only; a fresh install leaves it out and discovers its path.
|
|
137
|
+
|
|
138
|
+
Each hop has a role (**category**) that places it on the path. Any number of each works, and none is required:
|
|
139
|
+
|
|
140
|
+
- `local`: shown as **Router**; your router or mesh nodes.
|
|
141
|
+
- `gateway`: shown as **Line**; the cable/fiber modem or the provider's box.
|
|
142
|
+
- `isp`: shown as **ISP hop 1, 2…**; your provider's routers, in path order.
|
|
143
|
+
- `public`: shown as **Internet**; public resolvers, the figure is their median.
|
|
144
|
+
- `custom`: shown under its own name; anything else worth watching: a NAS, a VPN, a server.
|
|
145
|
+
|
|
146
|
+
The dashboard adapts to the shape of your network. A long path folds its provider hops into one node when space runs
|
|
147
|
+
out. A provider router that ignores ping while traffic passes through it is shown as such, not as down. Custom targets
|
|
148
|
+
are listed under the path. Hops can be IPv4 or IPv6 addresses, or host names probed over either; the IPv6 servers
|
|
149
|
+
discovery finds are their own node, "Internet (IPv6)", and on a host without IPv6 they say "IPv6 unavailable on this
|
|
150
|
+
host" instead of counting as down. Everything is in [docs/configuration.md](docs/configuration.md).
|
|
151
|
+
|
|
152
|
+
### Supported devices
|
|
153
|
+
|
|
154
|
+
- `xfinity_xb`: Xfinity / Comcast XB6, XB7, XB8 gateways. Reads DOCSIS downstream and upstream levels, SNR and
|
|
155
|
+
codeword errors.
|
|
156
|
+
- `asuswrt`: AsusWRT routers (RT-AX, ZenWiFi, …). Reads WAN traffic, CPU, memory, clients by band and mesh nodes.
|
|
157
|
+
- `openwrt`: OpenWrt routers (with LuCI, or rpcd and uhttpd-mod-ubus). Reads WAN traffic, CPU, memory, model and
|
|
158
|
+
clients (DHCP leases, Wi-Fi stations by band).
|
|
159
|
+
- `snmp`: anything with an SNMPv2c agent: routers, switches, NAS boxes. Reads WAN (or uplink) traffic, CPU, memory
|
|
160
|
+
and model.
|
|
161
|
+
|
|
162
|
+
Probing works with any equipment; devices are an optional extra. Setting up each one, and adding a driver, is
|
|
163
|
+
described in [docs/devices.md](docs/devices.md).
|
|
164
|
+
|
|
165
|
+
## Alerts
|
|
166
|
+
|
|
167
|
+
Pengumon can tell you when an incident starts and ends, and when one of your own apps stops answering. Expected
|
|
168
|
+
conditions (busy line) never alert, a cause alerts at most once every 5 minutes, and a slow receiver cannot delay the
|
|
169
|
+
probes. Two channels, either or both:
|
|
170
|
+
|
|
171
|
+
- **ntfy.** Set `PENGUMON_NOTIFY_URL` (and `PENGUMON_NOTIFY_TOKEN` for a protected server) in `.env`.
|
|
172
|
+
- **Webhooks.** List them under `notifications.webhooks` in `config.yaml`: Discord, Slack, or JSON for Home Assistant
|
|
173
|
+
and your own scripts. Tokens go in headers, which may name a secret instead of holding it.
|
|
174
|
+
|
|
175
|
+
```yaml
|
|
176
|
+
notifications:
|
|
177
|
+
public_url: "http://pengu.lan:8000" # optional: the dashboard link in the messages
|
|
178
|
+
webhooks:
|
|
179
|
+
- { name: discord, url: "https://discord.com/api/webhooks/<id>/<token>", format: discord }
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
The **Alerts** dialog lists the configured channels and has a **Send test** button. The payload, the three formats and
|
|
183
|
+
the Home Assistant recipe are in [docs/webhooks.md](docs/webhooks.md).
|
|
184
|
+
|
|
185
|
+
## A closer look
|
|
186
|
+
|
|
187
|
+
<img src="docs/images/inspector.png" alt="The incident inspector: when it started, where loss first appeared, per-hop lanes before, during and after, and the modem's reading" width="640">
|
|
188
|
+
|
|
189
|
+
**Incident inspector.** Where loss first appeared, every hop before, during and after, the modem's signal and the line load.
|
|
190
|
+
|
|
191
|
+
<img src="docs/images/drilldown.png" alt="Loss by hour of the week; one evening hour selected, and the incident list filtered to that hour" width="640">
|
|
192
|
+
|
|
193
|
+
**When it breaks.** Loss by hour of the week; click an hour to see its incidents.
|
|
194
|
+
|
|
195
|
+
<img src="docs/images/router.png" alt="The router card: WAN traffic against the line capacity, CPU and memory, clients by band and the mesh nodes" width="640">
|
|
196
|
+
|
|
197
|
+
**Router.** WAN traffic against the line's capacity, CPU and memory, clients by band and per mesh node.
|
|
198
|
+
|
|
199
|
+
<img src="docs/images/modem.png" alt="The cable modem card: downstream power, SNR and upstream power against their good ranges, codeword errors and channel lock" width="640">
|
|
200
|
+
|
|
201
|
+
**Cable modem.** Signal levels against their good ranges, codeword errors and channel lock, with history.
|
|
202
|
+
|
|
203
|
+
<img src="docs/images/first-run.png" alt="Path health on a first run: the banner 'Pengumon found your network path' with a Review button" width="640">
|
|
204
|
+
|
|
205
|
+
**First run.** Discovery runs by itself and offers the path it found; the defaults are probed until you apply it.
|
|
206
|
+
|
|
207
|
+
<img src="docs/images/path-editor.png" alt="The path editor with a draft from discovery: each hop with a role, a Sure or Likely badge and the reasons" width="640">
|
|
208
|
+
|
|
209
|
+
**Path editor.** Discovery's draft: every hop with a role, how sure it is and why. Edit, then Apply.
|
|
210
|
+
|
|
211
|
+
<p align="center">
|
|
212
|
+
<img src="docs/images/phone-live.png" width="180" alt="The dashboard on a phone: path health as a vertical chain">
|
|
213
|
+
|
|
214
|
+
<img src="docs/images/phone-incidents.png" width="180" alt="The incidents tab on a phone">
|
|
215
|
+
|
|
216
|
+
<img src="docs/images/overview-light.png" width="300" alt="The dashboard in the light theme">
|
|
217
|
+
</p>
|
|
218
|
+
|
|
219
|
+
## Documentation
|
|
220
|
+
|
|
221
|
+
- [Install](docs/install.md): Windows, macOS, Linux, Raspberry Pi and container: the install scripts, the background service, upgrading, removing, verifying a download
|
|
222
|
+
- [Configuration](docs/configuration.md): The probe path (targets), DNS lookups, thresholds, retention, line capacity, the classifier
|
|
223
|
+
- [Probing](docs/probing.md): Ping and traceroute over IPv4 and IPv6 on Linux, macOS and Windows, `pengumon doctor`, the Linux sysctl step
|
|
224
|
+
- [Path editor](docs/path-editor.md): Edit the probe path in the dashboard: hops, roles, groups, Test, Apply, Rediscover, Export and Import, upgrading from 0.17
|
|
225
|
+
- [Discovery](docs/discovery.md): Finding the path and who operates each hop: what is sent where, how roles are judged
|
|
226
|
+
- [Devices](docs/devices.md): The `devices:` list: drivers, capabilities, TLS, credentials, writing a driver
|
|
227
|
+
- [Diagnosis](docs/diagnosis.md): How outages are classified: load-aware rules, incident kinds and causes
|
|
228
|
+
- [Dashboard](docs/dashboard.md): The panels, the path chain, layout, themes, the phone view
|
|
229
|
+
- [Clients](docs/clients.md): Connected clients and mesh nodes, privacy settings, what is stored
|
|
230
|
+
- [Services](docs/services.md): Health checks for your own apps and their notifications
|
|
231
|
+
- [Webhooks](docs/webhooks.md): Alerts to Discord, Slack, Home Assistant or any URL: config, payload, secrets
|
|
232
|
+
- [Telemetry](docs/telemetry.md): What is stored and sent, logging, OpenTelemetry, ntfy
|
|
233
|
+
- [Security](docs/security.md): Access token, request guard, environment variables
|
|
234
|
+
- [Storage](docs/storage.md): Database layout, migrations, upgrade notes
|
|
235
|
+
- [API](docs/api.md): Data and operations endpoints
|
|
236
|
+
- [Architecture](docs/architecture.md): Components, start-up order, the live message
|
|
237
|
+
- [Testing](docs/testing.md): Unit tests, browser tests and lint
|
|
238
|
+
- [Supply chain](docs/supply-chain.md): Dependency policy, hash-locked requirements, vendored code
|
|
239
|
+
|
|
240
|
+
## Development
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
pip install -e ".[dev]"
|
|
244
|
+
PENGUMON_CONFIG=config.yaml PENGUMON_DATA=data python -m pengumon.main # :8000 on 127.0.0.1, ./config.yaml
|
|
245
|
+
pytest # unit tests
|
|
246
|
+
scripts/test.sh # the same, in a container
|
|
247
|
+
tests/ui/runner/build.sh # once: the browser test image
|
|
248
|
+
scripts/test-ui.sh # browser tests in headless Chromium, no network
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Dependencies are hash-locked (`requirements*.txt`, generated from the `.in` files) and installed with
|
|
252
|
+
`--require-hashes`. `scripts/deploy.sh` is the maintainer's deploy: it adds a dependency audit (the separate `depguard`
|
|
253
|
+
tool, run when it is installed), a rollback image and a database backup before rebuilding. `docker compose` is all you need to run Pengumon. Releases are cut as described in [docs/releasing.md](docs/releasing.md).
|
|
254
|
+
|
|
255
|
+
## How accurate is it?
|
|
256
|
+
|
|
257
|
+
Pengumon sends its own ICMP (no `ping` or `traceroute` program). On Linux and macOS the probes use kernel receive
|
|
258
|
+
timestamps (`SO_TIMESTAMPNS` / `SO_TIMESTAMP`, as `ping` does) over one shared socket, started 25 ms apart; against a
|
|
259
|
+
continuous `ping` they agree within about 0.2 ms. Windows reports round trips in whole milliseconds, and the dashboard
|
|
260
|
+
says so. DNS timings are measured in Python and can read a few tenths of a millisecond high. Details per OS:
|
|
261
|
+
[docs/probing.md](docs/probing.md).
|
|
262
|
+
|
|
263
|
+
## License
|
|
264
|
+
|
|
265
|
+
[MIT](LICENSE). Chart.js, bundled in `src/pengumon/web/static/vendor/`, is also MIT-licensed.
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="docs/images/logo.png" width="112" height="112" alt="">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">Pengumon</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://github.com/pengupowered/pengumon/actions/workflows/ci.yml"><img src="https://github.com/pengupowered/pengumon/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI status"></a>
|
|
9
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT license"></a>
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
<p align="center">
|
|
13
|
+
<b>Find out where your internet actually breaks: your Wi-Fi, your modem, your provider, or the internet beyond.</b><br>
|
|
14
|
+
A self-hosted network monitor that pings every hop once a second, explains each outage, and keeps the evidence.
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
<p align="center">
|
|
18
|
+
<img src="docs/images/overview-dark.png" alt="The Pengumon dashboard: path health from this host through the router, modem and three provider hops to the internet, a 24-hour latency chart and the cable modem's signal" width="100%">
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+
## Why
|
|
22
|
+
|
|
23
|
+
"The internet is slow again" is hard to act on. Was it the Wi-Fi, the cable to the modem, a congested node at the
|
|
24
|
+
provider, or one far-away server? Pengumon watches each stage of the path all the time, so when something goes wrong
|
|
25
|
+
you can see where it started, how long it lasted and what the modem and router were doing at that moment, and hand your
|
|
26
|
+
provider a report instead of a hunch.
|
|
27
|
+
|
|
28
|
+
## Features
|
|
29
|
+
|
|
30
|
+
- **The whole path, live.** This host → router → modem → each provider hop → the internet, with the median latency,
|
|
31
|
+
loss and jitter of every stage and how much each link adds.
|
|
32
|
+
- **Every outage explained.** A classifier places each problem in your home network, the line, the provider or beyond,
|
|
33
|
+
and records an incident with per-hop evidence, the modem's signal and the line load at the time.
|
|
34
|
+
- **Busy is not broken.** Router WAN traffic is taken into account, so lag while someone is downloading is marked as
|
|
35
|
+
expected rather than reported as a fault.
|
|
36
|
+
- **Patterns over time.** Loss by hour of the week, per-target uptime, a reliability report, and an ISP dossier to send
|
|
37
|
+
to support. Click any hour or uptime block to see the incidents in it.
|
|
38
|
+
- **Your modem and router.** DOCSIS channel levels and codeword errors; router CPU, memory, WAN traffic, connected
|
|
39
|
+
clients and mesh nodes. Drivers are pluggable.
|
|
40
|
+
- **Your own apps too.** HTTP health checks for self-hosted services, with incidents and alerts by [ntfy](https://ntfy.sh) or webhook.
|
|
41
|
+
- **Small and private.** One container and one SQLite file. Nothing leaves your network unless you turn on
|
|
42
|
+
notifications or the optional OpenTelemetry export. Works on a phone.
|
|
43
|
+
|
|
44
|
+
## Install
|
|
45
|
+
|
|
46
|
+
Pengumon runs natively on Windows, macOS and Linux, or in a container. One command per system; each link goes to that
|
|
47
|
+
system's section in [docs/install.md](docs/install.md), with what it needs, what it sets up and how to remove it.
|
|
48
|
+
|
|
49
|
+
**Windows** 10/11 (no administrator rights), in PowerShell ([details](docs/install.md#windows)):
|
|
50
|
+
|
|
51
|
+
```powershell
|
|
52
|
+
irm https://github.com/pengupowered/pengumon/releases/latest/download/install.ps1 | iex
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**macOS** (Apple silicon or Intel) ([details](docs/install.md#macos)), **Linux** (systemd, x86-64 or arm64)
|
|
56
|
+
([details](docs/install.md#linux)) and **Raspberry Pi** (64-bit OS only) ([details](docs/install.md#raspberry-pi)) use the
|
|
57
|
+
same line:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
curl -fsSL https://github.com/pengupowered/pengumon/releases/latest/download/install.sh | sh
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Container** (Docker or Podman on Linux): `docker compose up -d` with the published image, as below
|
|
64
|
+
([details](docs/install.md#container)).
|
|
65
|
+
|
|
66
|
+
The scripts download the release, check every file against its hash, set up Pengumon's own Python, start it in the
|
|
67
|
+
background so it survives reboots, and open `http://127.0.0.1:8000` (this computer only until you allow more, with an
|
|
68
|
+
access token). On Linux and macOS the script asks for sudo once, says why, and has a `--user` option to avoid it. Run
|
|
69
|
+
the same command again to upgrade (your data is backed up first). Not tested yet on real Windows, macOS or Raspberry Pi
|
|
70
|
+
hardware: see [what has been tested where](docs/install.md#what-has-been-tested-where).
|
|
71
|
+
|
|
72
|
+
> [!IMPORTANT]
|
|
73
|
+
> Anyone who can reach port 8000 can read the data and change settings until you set `PENGUMON_ACCESS_TOKEN`. The
|
|
74
|
+
> default listens on this computer only. See [docs/security.md](docs/security.md).
|
|
75
|
+
|
|
76
|
+
**Container**, the short version (Linux host with Docker or Podman, x86-64 or arm64; host networking so the probes
|
|
77
|
+
measure the host's own path):
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
mkdir pengumon && cd pengumon
|
|
81
|
+
curl -fsSL https://raw.githubusercontent.com/pengupowered/pengumon/main/compose.release.yaml -o compose.yaml
|
|
82
|
+
curl -fsSL https://raw.githubusercontent.com/pengupowered/pengumon/main/config.example.yaml -o config.yaml
|
|
83
|
+
# no targets in it: Pengumon finds your network path itself
|
|
84
|
+
docker compose up -d # or: podman compose up -d
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Open `http://<host>:8000`. Rootless Podman needs unprivileged ping allowed once, and Docker Desktop (macOS, Windows) is
|
|
88
|
+
not supported for this: both are explained in [docs/install.md](docs/install.md#container), as is building from source.
|
|
89
|
+
`pengumon doctor` says whether probing works here and, if not, what fixes it.
|
|
90
|
+
|
|
91
|
+
## First run
|
|
92
|
+
|
|
93
|
+
Nothing to configure. When you open the dashboard for the first time, Pengumon traces the path from this computer to the
|
|
94
|
+
internet by itself (it takes a few seconds; nothing about your path leaves your computer at this point) and **Path health**
|
|
95
|
+
shows a banner, "Pengumon found your network path". Press **Review** to see each hop it found, with a role (your
|
|
96
|
+
network, the line, your provider, the internet), how sure it is (Sure, Likely or Unsure) and why, then **Apply**. Until
|
|
97
|
+
you apply, the default targets keep being probed.
|
|
98
|
+
|
|
99
|
+
Later, **Path health > Edit** opens the same editor: rename, re-role, reorder, add, remove, or press **Rediscover** to
|
|
100
|
+
trace again and see what changed. Details: [docs/path-editor.md](docs/path-editor.md) and
|
|
101
|
+
[docs/discovery.md](docs/discovery.md), which lists exactly what discovery sends where.
|
|
102
|
+
|
|
103
|
+
## Telling it about your network
|
|
104
|
+
|
|
105
|
+
The probe path is edited in the dashboard, not in `config.yaml`: **Path health > Edit** shows the hops as cards in path
|
|
106
|
+
order (see [First run](#first-run) and [docs/path-editor.md](docs/path-editor.md)). **Export** and **Import** move a
|
|
107
|
+
path between installs as YAML.
|
|
108
|
+
|
|
109
|
+
Upgrading from 0.17 or older: the first start imports config.yaml's `targets:` and the targets added from the dashboard
|
|
110
|
+
into the database once, with their ids, so all history stays attached. From then on config.yaml's `targets:` is ignored
|
|
111
|
+
(the startup log says so); every other section stays in `config.yaml`. A `targets:` list in config.yaml is now just an optional fixed list for the first start only; a fresh install leaves it out and discovers its path.
|
|
112
|
+
|
|
113
|
+
Each hop has a role (**category**) that places it on the path. Any number of each works, and none is required:
|
|
114
|
+
|
|
115
|
+
- `local`: shown as **Router**; your router or mesh nodes.
|
|
116
|
+
- `gateway`: shown as **Line**; the cable/fiber modem or the provider's box.
|
|
117
|
+
- `isp`: shown as **ISP hop 1, 2…**; your provider's routers, in path order.
|
|
118
|
+
- `public`: shown as **Internet**; public resolvers, the figure is their median.
|
|
119
|
+
- `custom`: shown under its own name; anything else worth watching: a NAS, a VPN, a server.
|
|
120
|
+
|
|
121
|
+
The dashboard adapts to the shape of your network. A long path folds its provider hops into one node when space runs
|
|
122
|
+
out. A provider router that ignores ping while traffic passes through it is shown as such, not as down. Custom targets
|
|
123
|
+
are listed under the path. Hops can be IPv4 or IPv6 addresses, or host names probed over either; the IPv6 servers
|
|
124
|
+
discovery finds are their own node, "Internet (IPv6)", and on a host without IPv6 they say "IPv6 unavailable on this
|
|
125
|
+
host" instead of counting as down. Everything is in [docs/configuration.md](docs/configuration.md).
|
|
126
|
+
|
|
127
|
+
### Supported devices
|
|
128
|
+
|
|
129
|
+
- `xfinity_xb`: Xfinity / Comcast XB6, XB7, XB8 gateways. Reads DOCSIS downstream and upstream levels, SNR and
|
|
130
|
+
codeword errors.
|
|
131
|
+
- `asuswrt`: AsusWRT routers (RT-AX, ZenWiFi, …). Reads WAN traffic, CPU, memory, clients by band and mesh nodes.
|
|
132
|
+
- `openwrt`: OpenWrt routers (with LuCI, or rpcd and uhttpd-mod-ubus). Reads WAN traffic, CPU, memory, model and
|
|
133
|
+
clients (DHCP leases, Wi-Fi stations by band).
|
|
134
|
+
- `snmp`: anything with an SNMPv2c agent: routers, switches, NAS boxes. Reads WAN (or uplink) traffic, CPU, memory
|
|
135
|
+
and model.
|
|
136
|
+
|
|
137
|
+
Probing works with any equipment; devices are an optional extra. Setting up each one, and adding a driver, is
|
|
138
|
+
described in [docs/devices.md](docs/devices.md).
|
|
139
|
+
|
|
140
|
+
## Alerts
|
|
141
|
+
|
|
142
|
+
Pengumon can tell you when an incident starts and ends, and when one of your own apps stops answering. Expected
|
|
143
|
+
conditions (busy line) never alert, a cause alerts at most once every 5 minutes, and a slow receiver cannot delay the
|
|
144
|
+
probes. Two channels, either or both:
|
|
145
|
+
|
|
146
|
+
- **ntfy.** Set `PENGUMON_NOTIFY_URL` (and `PENGUMON_NOTIFY_TOKEN` for a protected server) in `.env`.
|
|
147
|
+
- **Webhooks.** List them under `notifications.webhooks` in `config.yaml`: Discord, Slack, or JSON for Home Assistant
|
|
148
|
+
and your own scripts. Tokens go in headers, which may name a secret instead of holding it.
|
|
149
|
+
|
|
150
|
+
```yaml
|
|
151
|
+
notifications:
|
|
152
|
+
public_url: "http://pengu.lan:8000" # optional: the dashboard link in the messages
|
|
153
|
+
webhooks:
|
|
154
|
+
- { name: discord, url: "https://discord.com/api/webhooks/<id>/<token>", format: discord }
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The **Alerts** dialog lists the configured channels and has a **Send test** button. The payload, the three formats and
|
|
158
|
+
the Home Assistant recipe are in [docs/webhooks.md](docs/webhooks.md).
|
|
159
|
+
|
|
160
|
+
## A closer look
|
|
161
|
+
|
|
162
|
+
<img src="docs/images/inspector.png" alt="The incident inspector: when it started, where loss first appeared, per-hop lanes before, during and after, and the modem's reading" width="640">
|
|
163
|
+
|
|
164
|
+
**Incident inspector.** Where loss first appeared, every hop before, during and after, the modem's signal and the line load.
|
|
165
|
+
|
|
166
|
+
<img src="docs/images/drilldown.png" alt="Loss by hour of the week; one evening hour selected, and the incident list filtered to that hour" width="640">
|
|
167
|
+
|
|
168
|
+
**When it breaks.** Loss by hour of the week; click an hour to see its incidents.
|
|
169
|
+
|
|
170
|
+
<img src="docs/images/router.png" alt="The router card: WAN traffic against the line capacity, CPU and memory, clients by band and the mesh nodes" width="640">
|
|
171
|
+
|
|
172
|
+
**Router.** WAN traffic against the line's capacity, CPU and memory, clients by band and per mesh node.
|
|
173
|
+
|
|
174
|
+
<img src="docs/images/modem.png" alt="The cable modem card: downstream power, SNR and upstream power against their good ranges, codeword errors and channel lock" width="640">
|
|
175
|
+
|
|
176
|
+
**Cable modem.** Signal levels against their good ranges, codeword errors and channel lock, with history.
|
|
177
|
+
|
|
178
|
+
<img src="docs/images/first-run.png" alt="Path health on a first run: the banner 'Pengumon found your network path' with a Review button" width="640">
|
|
179
|
+
|
|
180
|
+
**First run.** Discovery runs by itself and offers the path it found; the defaults are probed until you apply it.
|
|
181
|
+
|
|
182
|
+
<img src="docs/images/path-editor.png" alt="The path editor with a draft from discovery: each hop with a role, a Sure or Likely badge and the reasons" width="640">
|
|
183
|
+
|
|
184
|
+
**Path editor.** Discovery's draft: every hop with a role, how sure it is and why. Edit, then Apply.
|
|
185
|
+
|
|
186
|
+
<p align="center">
|
|
187
|
+
<img src="docs/images/phone-live.png" width="180" alt="The dashboard on a phone: path health as a vertical chain">
|
|
188
|
+
|
|
189
|
+
<img src="docs/images/phone-incidents.png" width="180" alt="The incidents tab on a phone">
|
|
190
|
+
|
|
191
|
+
<img src="docs/images/overview-light.png" width="300" alt="The dashboard in the light theme">
|
|
192
|
+
</p>
|
|
193
|
+
|
|
194
|
+
## Documentation
|
|
195
|
+
|
|
196
|
+
- [Install](docs/install.md): Windows, macOS, Linux, Raspberry Pi and container: the install scripts, the background service, upgrading, removing, verifying a download
|
|
197
|
+
- [Configuration](docs/configuration.md): The probe path (targets), DNS lookups, thresholds, retention, line capacity, the classifier
|
|
198
|
+
- [Probing](docs/probing.md): Ping and traceroute over IPv4 and IPv6 on Linux, macOS and Windows, `pengumon doctor`, the Linux sysctl step
|
|
199
|
+
- [Path editor](docs/path-editor.md): Edit the probe path in the dashboard: hops, roles, groups, Test, Apply, Rediscover, Export and Import, upgrading from 0.17
|
|
200
|
+
- [Discovery](docs/discovery.md): Finding the path and who operates each hop: what is sent where, how roles are judged
|
|
201
|
+
- [Devices](docs/devices.md): The `devices:` list: drivers, capabilities, TLS, credentials, writing a driver
|
|
202
|
+
- [Diagnosis](docs/diagnosis.md): How outages are classified: load-aware rules, incident kinds and causes
|
|
203
|
+
- [Dashboard](docs/dashboard.md): The panels, the path chain, layout, themes, the phone view
|
|
204
|
+
- [Clients](docs/clients.md): Connected clients and mesh nodes, privacy settings, what is stored
|
|
205
|
+
- [Services](docs/services.md): Health checks for your own apps and their notifications
|
|
206
|
+
- [Webhooks](docs/webhooks.md): Alerts to Discord, Slack, Home Assistant or any URL: config, payload, secrets
|
|
207
|
+
- [Telemetry](docs/telemetry.md): What is stored and sent, logging, OpenTelemetry, ntfy
|
|
208
|
+
- [Security](docs/security.md): Access token, request guard, environment variables
|
|
209
|
+
- [Storage](docs/storage.md): Database layout, migrations, upgrade notes
|
|
210
|
+
- [API](docs/api.md): Data and operations endpoints
|
|
211
|
+
- [Architecture](docs/architecture.md): Components, start-up order, the live message
|
|
212
|
+
- [Testing](docs/testing.md): Unit tests, browser tests and lint
|
|
213
|
+
- [Supply chain](docs/supply-chain.md): Dependency policy, hash-locked requirements, vendored code
|
|
214
|
+
|
|
215
|
+
## Development
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
pip install -e ".[dev]"
|
|
219
|
+
PENGUMON_CONFIG=config.yaml PENGUMON_DATA=data python -m pengumon.main # :8000 on 127.0.0.1, ./config.yaml
|
|
220
|
+
pytest # unit tests
|
|
221
|
+
scripts/test.sh # the same, in a container
|
|
222
|
+
tests/ui/runner/build.sh # once: the browser test image
|
|
223
|
+
scripts/test-ui.sh # browser tests in headless Chromium, no network
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Dependencies are hash-locked (`requirements*.txt`, generated from the `.in` files) and installed with
|
|
227
|
+
`--require-hashes`. `scripts/deploy.sh` is the maintainer's deploy: it adds a dependency audit (the separate `depguard`
|
|
228
|
+
tool, run when it is installed), a rollback image and a database backup before rebuilding. `docker compose` is all you need to run Pengumon. Releases are cut as described in [docs/releasing.md](docs/releasing.md).
|
|
229
|
+
|
|
230
|
+
## How accurate is it?
|
|
231
|
+
|
|
232
|
+
Pengumon sends its own ICMP (no `ping` or `traceroute` program). On Linux and macOS the probes use kernel receive
|
|
233
|
+
timestamps (`SO_TIMESTAMPNS` / `SO_TIMESTAMP`, as `ping` does) over one shared socket, started 25 ms apart; against a
|
|
234
|
+
continuous `ping` they agree within about 0.2 ms. Windows reports round trips in whole milliseconds, and the dashboard
|
|
235
|
+
says so. DNS timings are measured in Python and can read a few tenths of a millisecond high. Details per OS:
|
|
236
|
+
[docs/probing.md](docs/probing.md).
|
|
237
|
+
|
|
238
|
+
## License
|
|
239
|
+
|
|
240
|
+
[MIT](LICENSE). Chart.js, bundled in `src/pengumon/web/static/vendor/`, is also MIT-licensed.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pengumon"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Containerized network diagnostics, hop-by-hop latency tracker, and outage root-cause classifier"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
requires-python = ">=3.11"
|
|
13
|
+
dependencies = [
|
|
14
|
+
"fastapi>=0.110.0",
|
|
15
|
+
"uvicorn>=0.29.0",
|
|
16
|
+
"websockets>=12.0",
|
|
17
|
+
"dnspython>=2.6.1",
|
|
18
|
+
"aiosqlite>=0.20.0",
|
|
19
|
+
"pydantic>=2.7.0",
|
|
20
|
+
"pydantic-settings>=2.2.1",
|
|
21
|
+
"pyyaml>=6.0.1",
|
|
22
|
+
"jinja2>=3.1.3",
|
|
23
|
+
"httpx>=0.27.0",
|
|
24
|
+
"opentelemetry-sdk>=1.45.1,<2",
|
|
25
|
+
"opentelemetry-exporter-otlp-proto-http>=1.45.1,<2"
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.optional-dependencies]
|
|
29
|
+
dev = [
|
|
30
|
+
"pytest>=8.1.0",
|
|
31
|
+
"pytest-asyncio>=0.23.6"
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.scripts]
|
|
35
|
+
pengumon = "pengumon.cli:main"
|
|
36
|
+
|
|
37
|
+
# Same command without a console window (Windows; elsewhere an ordinary script): what the logon task starts.
|
|
38
|
+
[project.gui-scripts]
|
|
39
|
+
pengumonw = "pengumon.cli:main"
|
|
40
|
+
|
|
41
|
+
[tool.setuptools.dynamic]
|
|
42
|
+
# The single source of the version (the tests run from src/ without installing, so metadata cannot be it).
|
|
43
|
+
version = {attr = "pengumon.__version__"}
|
|
44
|
+
|
|
45
|
+
[tool.setuptools.packages.find]
|
|
46
|
+
where = ["src"]
|
|
47
|
+
|
|
48
|
+
# Everything read at run time that is not a .py file: the dashboard (HTML, CSS, JS, panel manifests, icons, the
|
|
49
|
+
# vendored Chart.js) and the page templates. tests/test_wheel.py builds the wheel and checks it is all there.
|
|
50
|
+
[tool.setuptools.package-data]
|
|
51
|
+
pengumon = ["web/static/**/*", "web/templates/**/*"]
|
|
52
|
+
|
|
53
|
+
[tool.pytest.ini_options]
|
|
54
|
+
pythonpath = ["src"]
|
|
55
|
+
asyncio_mode = "auto"
|
|
56
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Third-party code vendored into the wheel (see the README of each package for how it is refreshed)."""
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""homeguard: request guards shared by the home apps that are reachable on the local network.
|
|
2
|
+
|
|
3
|
+
This package is vendored: scripts/vendor.py copies it verbatim into each app's src/ directory. Never edit
|
|
4
|
+
the copy inside an app; change it here, commit, and re-vendor.
|
|
5
|
+
|
|
6
|
+
The version constant is deliberately not called __version__: `depguard sbom` takes an app's version from
|
|
7
|
+
the first src/*/__init__.py that defines __version__, and "homeguard" sorts before the app packages.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
HOMEGUARD_VERSION = "0.1.0"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Generated by homeguard/scripts/vendor.py - do not edit.
|
|
2
|
+
|
|
3
|
+
Records which homeguard commit this copy came from and the SHA-256 of each vendored file (LF bytes).
|
|
4
|
+
FILES are the Python files in this package, SCRIPTS the PowerShell modules copied to <app>/scripts/.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
COMMIT = "8c4f179"
|
|
8
|
+
FILES = {
|
|
9
|
+
"__init__.py": "035b4c21661fbffe3d455b70580a68bed3fb54f460d837d090ba2da6f45dcdc7",
|
|
10
|
+
"db.py": "7df9c8dc66acb331a330cedf568d4dc059c3e99dd34870d8eb4f9d8b8ec0e19a",
|
|
11
|
+
"security.py": "98aa2636c3f8bea8ca5bfd5a442abfbd10ccb6a0f0ec659ab550b63d93301584",
|
|
12
|
+
}
|
|
13
|
+
SCRIPTS = {}
|