What installation are you running?
Production (netalertx) 📦
Is there an existing issue for this?
The issue occurs in the following browsers. Select at least 2.
Current Behavior
Once a device has at least one other device linked to it via devParentRelType='nic' (the Host/NIC relationship, meant for tracking a host's multiple network interfaces as one logical device), the parent's own directly-detected network presence stops being used at all. Instead, every scan cycle, the parent's Connected/Disconnected events lock onto whatever the nic child's presence is - and because of how this interacts with event generation, only ONE of the two event types can ever fire for that parent, forever, regardless of what's actually happening on the network. If the nic child is permanently or persistently unreachable, the parent gets an endless stream of Connected events and zero Disconnected events, even while the parent's own real network presence is being genuinely, repeatedly re-detected every cycle. (The mirror case - a permanently-reachable nic child - produces the opposite: endless Disconnected, never Connected, even if the parent is actually offline.)
This surfaced as a real-world notification flood: a device running NetAlertX itself started reporting itself as endlessly "Disconnected."
Expected Behavior
A parent device's own directly-detected presence should never be completely discarded. At minimum, the nic child's presence should only be able to bring the parent's presence UP (mark it present when its own detection missed it), never force it down when the parent itself was just detected as genuinely present - and the reverse for the "require all NICs online" mode. A device that answers a scan directly should never be permanently reported as either connected-only or disconnected-only regardless of its own real state.
Steps To Reproduce
Reproduced in an isolated Docker container (ghcr.io/jokob-sk/netalertx:latest, same image as production), separate from any production data, over a real ~2 hour window of live scan cycles (not simulated/manually invoked - this is the shipped scheduler running the shipped code).
- Fresh NetAlertX instance.
LOG_LEVEL='trace', TIMEZONE='Europe/Warsaw', ARPSCAN_RUN_SCHD='*/2 * * * *'.
- Create two devices:
- A device with genuine, continuously-renewed real presence. (In my repro this was the container's own self-detected interface, auto-registered every cycle via
save_own_device()/devSourcePlugin='local_MAC' independent of ARPSCAN - any device with reliably real, continuous presence works the same way. MAC in this run: 0a:17:f2:8d:02:97.)
- A second device,
devParentMAC pointing at the first, devParentRelType='nic'. This one should never actually be reachable on the network (MAC in this run: bb:bb:bb:bb:bb:02, a fictitious MAC that never answers ARP).
- Let several real scan cycles pass (6+ recommended for a clear pattern).
- Query
Events for the parent MAC.
Result: the parent gets a Connected event every single cycle, and never a Disconnected event, for as long as the nic child stays unreachable - even though the parent's own devLastConnection keeps genuinely advancing every cycle (proof its real presence IS being detected correctly underneath the bug).
Note on constructing a minimal repro: a device that is never real on the wire at all (no genuine detection, ever) gets zero events of any kind, not a one-sided stream - this is a separate, structural gate in insert_events() (the "New Connections" branch requires the MAC to actually be a row in CurrentScan), not part of this bug. The flood specifically requires a parent with real, ongoing detection whose presence then gets discarded by a fake/absent nic child - i.e. genuine detection silently overridden, not "device that was never really there."
Relevant app.conf settings
LOG_LEVEL='trace'
TIMEZONE='Europe/Warsaw'
ARPSCAN_RUN_SCHD='*/2 * * * *'
SCAN_SUBNETS='172.17.0.0/28 --interface=eth0'
docker-compose.yml
services:
netalertx:
image: ghcr.io/jokob-sk/netalertx:latest
container_name: netalertx-repro-nic2
cap_add:
- CHOWN
- NET_ADMIN
- NET_BIND_SERVICE
- NET_RAW
- SETGID
- SETUID
ports:
- "11041:1031"
- "10412:20212"
environment:
TZ: Europe/Warsaw
PORT: 1031
GRAPHQL_PORT: 20212
volumes:
- ./data:/data
(Isolated repro setup - bridge networking rather than production's `network_mode: host`, so it doesn't collide with a real instance on the same box. `cap_add` is required for the image's entrypoint to start at all under bridge networking - without it, several bundled binaries with Linux file-capabilities fail with "Operation not permitted", exit 126, unrelated to this bug.)
Debug or Trace enabled
Relevant app.log section
[Process Scan] Processing scan results
[Process Scan] Exclude ignored devices
[Process Scan] Print Stats
{'devMac': '0a:17:f2:8d:02:97', 'devName': 'ReproParent-selfMAC', ..., 'devLastConnection': '2026-08-13 04:10:34', ..., 'devPresentLastScan': 0, ...}
[Process Scan] Stats end
[Process Scan] Sessions Events (connect / disconnect)
[Process Scan] Creating new devices
[Process Scan] Updating Devices Info
[Process Scan] Updating Sync Hub Node
[Process Scan] Updating devLastConnection from CurrentScan
[Process Scan] Updating Presence from CurrentScan
[Process Scan] Updating NICs presence
[Process Scan] Updating forced presence
...
[Process Scan] Pairing session events (connection / disconnection)
[Process Scan] Creating sessions snapshot
[Process Scan] Inserting scan results into Online_History
This is one full cycle: notice devPresentLastScan: 0 in the "Print Stats" dump taken before this cycle's own presence update runs - i.e. this is the value left over from last cycle's "Updating NICs presence" step - while devLastConnection from the previous cycle is already fresh (one cycle old), proving the device really was detected as present a moment earlier. The step order confirms the mechanism: "Updating Presence from CurrentScan" (which would correctly set this to 1) runs, then "Updating NICs presence" immediately overwrites it back down.
This section is trimmed to the log lines that demonstrate the mechanism directly. A curated excerpt of one complete cycle end-to-end is attached here: trace_excerpt_one_full_cycle_06-12-41.log. The complete internal application log for this run (8MB, LOG_LEVEL=trace) is attached here: app_trace_log_full.log.
Docker Logs
[Notification] final_json: {"new_devices": [], ..., "events": [{"devName": "ReproParent-selfMAC", "eveMac": "0a:17:f2:8d:02:97", "devVendor": "", "eveIp": "172.17.0.2", "eveDateTime": "2026-08-13T06:10:34+02:00", "eveEventType": "Connected", ...}], "events_meta": {...}, ...}
This is the application's own generated notification payload - proof this isn't just an internal DB inconsistency, it's what NetAlertX itself reports as a real "Connected" event to the user, repeatedly, cycle after cycle, for a device whose actual state never changes.
Supporting data (SQL run directly against the repro instance's own app.db):
SELECT eveDateTime, eveMac, eveEventType FROM Events WHERE eveMac='0a:17:f2:8d:02:97' ORDER BY eveDateTime;
2026-08-12 22:35:09 0a:17:f2:8d:02:97 Connected (New Device, first seen)
2026-08-13 04:01:11 0a:17:f2:8d:02:97 Connected
2026-08-13 04:03:11 0a:17:f2:8d:02:97 Connected
2026-08-13 04:05:18 0a:17:f2:8d:02:97 Connected
2026-08-13 04:06:21 0a:17:f2:8d:02:97 Connected
2026-08-13 04:08:27 0a:17:f2:8d:02:97 Connected
2026-08-13 04:10:34 0a:17:f2:8d:02:97 Connected
2026-08-13 04:12:41 0a:17:f2:8d:02:97 Connected
8 Connected, 0 Disconnected, across ~11 minutes of accumulated real cycles (plus a long idle gap in between where the container was simply left running). Negative control confirming the "never-real device" distinction noted above: the originally-planned fully-fictitious parent MAC (bb:bb:bb:bb:bb:01, never given a path into CurrentScan) produced exactly 0 events of any kind over the same ~7.5 hour window.
Full docker logs output (this run's complete history) is attached here: docker_logs_full.txt.
What installation are you running?
Production (netalertx) 📦
Is there an existing issue for this?
The issue occurs in the following browsers. Select at least 2.
Current Behavior
Once a device has at least one other device linked to it via
devParentRelType='nic'(the Host/NIC relationship, meant for tracking a host's multiple network interfaces as one logical device), the parent's own directly-detected network presence stops being used at all. Instead, every scan cycle, the parent'sConnected/Disconnectedevents lock onto whatever the nic child's presence is - and because of how this interacts with event generation, only ONE of the two event types can ever fire for that parent, forever, regardless of what's actually happening on the network. If the nic child is permanently or persistently unreachable, the parent gets an endless stream ofConnectedevents and zeroDisconnectedevents, even while the parent's own real network presence is being genuinely, repeatedly re-detected every cycle. (The mirror case - a permanently-reachable nic child - produces the opposite: endlessDisconnected, neverConnected, even if the parent is actually offline.)This surfaced as a real-world notification flood: a device running NetAlertX itself started reporting itself as endlessly "Disconnected."
Expected Behavior
A parent device's own directly-detected presence should never be completely discarded. At minimum, the nic child's presence should only be able to bring the parent's presence UP (mark it present when its own detection missed it), never force it down when the parent itself was just detected as genuinely present - and the reverse for the "require all NICs online" mode. A device that answers a scan directly should never be permanently reported as either connected-only or disconnected-only regardless of its own real state.
Steps To Reproduce
Reproduced in an isolated Docker container (
ghcr.io/jokob-sk/netalertx:latest, same image as production), separate from any production data, over a real ~2 hour window of live scan cycles (not simulated/manually invoked - this is the shipped scheduler running the shipped code).LOG_LEVEL='trace',TIMEZONE='Europe/Warsaw',ARPSCAN_RUN_SCHD='*/2 * * * *'.save_own_device()/devSourcePlugin='local_MAC'independent of ARPSCAN - any device with reliably real, continuous presence works the same way. MAC in this run:0a:17:f2:8d:02:97.)devParentMACpointing at the first,devParentRelType='nic'. This one should never actually be reachable on the network (MAC in this run:bb:bb:bb:bb:bb:02, a fictitious MAC that never answers ARP).Eventsfor the parent MAC.Result: the parent gets a
Connectedevent every single cycle, and never aDisconnectedevent, for as long as the nic child stays unreachable - even though the parent's owndevLastConnectionkeeps genuinely advancing every cycle (proof its real presence IS being detected correctly underneath the bug).Note on constructing a minimal repro: a device that is never real on the wire at all (no genuine detection, ever) gets zero events of any kind, not a one-sided stream - this is a separate, structural gate in
insert_events()(the "New Connections" branch requires the MAC to actually be a row inCurrentScan), not part of this bug. The flood specifically requires a parent with real, ongoing detection whose presence then gets discarded by a fake/absent nic child - i.e. genuine detection silently overridden, not "device that was never really there."Relevant
app.confsettingsdocker-compose.yml
Debug or Trace enabled
Relevant
app.logsectionThis is one full cycle: notice
devPresentLastScan: 0in the "Print Stats" dump taken before this cycle's own presence update runs - i.e. this is the value left over from last cycle's "Updating NICs presence" step - whiledevLastConnectionfrom the previous cycle is already fresh (one cycle old), proving the device really was detected as present a moment earlier. The step order confirms the mechanism: "Updating Presence from CurrentScan" (which would correctly set this to 1) runs, then "Updating NICs presence" immediately overwrites it back down.This section is trimmed to the log lines that demonstrate the mechanism directly. A curated excerpt of one complete cycle end-to-end is attached here: trace_excerpt_one_full_cycle_06-12-41.log. The complete internal application log for this run (8MB, LOG_LEVEL=trace) is attached here: app_trace_log_full.log.
Docker Logs
This is the application's own generated notification payload - proof this isn't just an internal DB inconsistency, it's what NetAlertX itself reports as a real "Connected" event to the user, repeatedly, cycle after cycle, for a device whose actual state never changes.
Supporting data (SQL run directly against the repro instance's own
app.db):8
Connected, 0Disconnected, across ~11 minutes of accumulated real cycles (plus a long idle gap in between where the container was simply left running). Negative control confirming the "never-real device" distinction noted above: the originally-planned fully-fictitious parent MAC (bb:bb:bb:bb:bb:01, never given a path intoCurrentScan) produced exactly 0 events of any kind over the same ~7.5 hour window.Full
docker logsoutput (this run's complete history) is attached here: docker_logs_full.txt.