References PR #1765. Docs: - Added PIHOLEMON to docs/PLUGINS.md and a new "Approach 4" section in docs/PIHOLE_GUIDE.md, leading with anomaly detection (the actual differentiator vs PIHOLEAPI) and explaining when to pick each plugin. - README/PLUGINS.md/config.json's UI-facing description all reordered and shortened to lead with anomaly detection instead of device import, and to drop implementation detail that belongs in the README, not the Settings page. - Trimmed the "Why not extend PIHOLEAPI" README section per feedback - useful context for a maintainer, not for an end user configuring the plugin. config.json / pihole_monitor.py: - RUN defaults to "disabled", matching every other non-core plugin. - VERIFY_SSL split into PRIMARY_VERIFY_SSL / SECONDARY_VERIFY_SSL - each instance can be http/https independently. Settings reordered so each *_VERIFY_SSL sits right under its matching *_PASSWORD. - GRAPHQL_TOKEN removed; graphql_token now reads the core API_TOKEN setting instead of a plugin-specific duplicate. - GRAPHQL_URL replaced with a GET_OWNER boolean - the endpoint is now derived from this app's own GRAPHQL_PORT (single source of truth) instead of a URL the user had to keep in sync by hand. - HISTORY_LENGTH (run count) replaced with HISTORY_DAYS (a real time window): state now stores [timestamp, delta] samples and trim_history() drops anything older than the window, so the baseline means the same thing regardless of schedule - a faster schedule adds more data points instead of shrinking the window. - STATE_FILE moved from the log folder to dbFolderPath, so the rolling anomaly baseline survives NetAlertX upgrades instead of being wiped with the logs. - netalertx_device_owner() (1 GraphQL call per device) replaced by netalertx_device_owners() (1 call per run, batched) - avoids N blocking round-trips on a large network. - Fixed a zero-baseline bug: `bool(... and baseline and ...)` silently exempted a device with an all-zero blocked-query history (0.0 is falsy in Python) from ever being flagged, even on its first real spike. Now checks `baseline is not None`. - Fixed the placeholder-MAC filter: only excluded the literal "ip-::", not Pi-hole's general "ip-<address>" placeholder pattern. Caught downstream by is_mac() either way, but now the actual placeholder check does what it looks like it does. - Fixed a cumulative-counter bug: Pi-hole's /api/stats/top_clients returns a count that's cumulative since FTL last started, not a per-interval or daily-resetting one (confirmed against FTL's own source and long-standing user reports that it doesn't reset at midnight). Comparing that raw total directly against a rolling average made any device's ordinary growing traffic look like an escalating anomaly. compute_delta() now diffs each run's raw count against the previous run's (state gained a per-key last_raw reference point alongside the delta history) - None (not 0) on the first-ever run for a device or right after a counter reset, so those runs re-anchor the reference point instead of fabricating or swallowing a delta. - RUN_SCHD default changed from every 6 hours to every 5 minutes now that the baseline window is real days, not run count, so a frequent schedule only adds data points instead of narrowing the window; also matches the default most other device-scanner plugins use. - RUN_SCHD gained the same live cron-validity checkmark ARPSCAN and other scanner plugins use (a ✓/✗ icon next to the field, validated client-side against a regex) - reuses the existing generic validateRegex() widget, nothing plugin-specific to build. Tests: 48 tests (up from 37), 99% line+branch coverage. Every fix above verified via mutation testing (deliberately broken, confirmed the relevant test fails, then restored).
Documentation overview
ℹ️ In the app hover over settings or fields/labels or click blue in-app ❔ (question-mark) icons to get to relevant documentation pages.
There is also an in-app Help / FAQ section that should be answering frequently asked questions.
📥 Installation
🐳 Docker (Fully supported)
- The main installation method is as a docker container - follow these instructions here.
💻 Bare-metal / On-server (Experimental/community supported 🧪)
-
Alternative bare-metal install forks:
- leiweibau's fork (maintained)
- pucherot's original code (un-maintained)
📚 Table of contents
📥 Initial Setup
- Synology Guide
- Subnets and VLANs configuration for arp-scan
- Scanning Remote Networks
- SMTP server config
- Custom Icon configuration and support
- Notifications
- Better name resolution with Reverse DNS
- Network treemap configuration
- Backups
- Plugins overview
🐛 Debugging help & tips
- Debugging tips
- Debugging UI not showing
- Invalid JSON errors debug help
- Troubleshooting Plugins
- File Permissions
- Performance tips
🔝 Popular/Suggested
⚙ System Management
🔎 Examples
♻ Misc
👩💻For Developers👨💻
- Setting up your DEV environment
- Server APP code structure
- Database structure
- API endpoints details
- Plugin development guide
- Settings system
- New Version notifications
- Frontend development tips
- Webhook secrets
Feel free to suggest or submit new docs via a PR.
👨💻 Development priorities
Priorities from highest to lowest:
- 🔼 Fixing core functionality bugs not solvable with workarounds
- 🔵 New core functionality unlocking other opportunities (e.g.: plugins)
- 🔵 Refactoring enabling faster implementation of future functionality
- 🔽 (low) UI functionality & improvements (PRs welcome 😉)
Design philosophy: Focus on core functionality and leverage existing apps and tools to make NetAlertX integrate into other workflows.
Examples:
1. Supporting apprise makes more sense than implementing multiple individual notification gateways
2. Implementing regular expression support across settings for validation makes more sense than validating one setting with a specific expression.
UI-specific requests are a low priority as the framework picked by the original developer is not very extensible (and afaik doesn't support components) and has limited mobile support. Also, I argue the value proposition is smaller than working on something else.
Feel free to submit PRs if interested. try to keep the PRs small/on-topic so they are easier to review and approve.
That being said, I'd reconsider if more people and or recurring sponsors file a request 😉.
🙏 Feature requests
Please be as detailed as possible with workarounds you considered and why a native feature is the better way. This gives me better context and will make it more likely to be implemented. Ideally, a feature request should be in the format "I want to be able to do XYZ so that ZYX. I considered these approaches XYZ".
➕ Pull requests (PRs)
If you submit a PR please:
- Check that your changes are backward compatible with existing installations and with a blank setup.
- Existing features should always be preserved.
- Keep the PR small, on-topic and don't change code that is not necessary for the PR to work
- New features code should ideally be re-usable for different purposes, not for a very narrow use case.
- New functionality should ideally be implemented via the Plugins system, if possible.
Suggested test cases:
- Blank setup with no DB or config
- Existing DB / config
- Sending a notification (e. g. Delete a device and wait for a scan to run) and testing all notification gateways, especially:
- Email, Apprise (e.g. via Telegram), webhook (e.g. via Discord), MQTT (e.g. via Home Assistant)
- Saving settings
- Test a couple of plugins
- Check the Error log for anything unusual
Some additional context:
- Permanent settings/config is stored in the
app.conffile - Currently temporary (session?) settings are stored in the
ParametersDB table as key-value pairs. This table is wiped during a container rebuild/restart and its values are re-initialized from cookies/session data from the browser.
🐛 Submitting an issue or bug
Before submitting a new issue please spend a couple of minutes on research:
- Check 🛑 Common issues
- Check 💡 Closed issues if a similar issue was solved in the past.
- When submitting an issue ❗enable debug❗
⚠ Please follow the pre-defined issue template to resolve your issue faster.
