Skip to content

Monitor: configurable inactivity threshold (days since last seen, or last N PCAPs) #488

Description

@NotYuSheng

Summary

In monitor mode, an entity (device / IP / application / protocol) is currently treated as active if and only if it appears in the latest snapshot, and absent otherwise. This binary rule is too naive: an entity missing from the most recent PCAP hasn't necessarily left the network — it may simply not have communicated during that capture window. A single quiet capture flips a still-present device to "absent".

We should let the user configure an inactivity threshold that governs when an entity is considered inactive, based on how recently it was last seen rather than "not in the latest snapshot".

Current behaviour

The active/absent split is computed client-side in the drift panels — DeviceDriftPanel, IpDriftPanel, ProtocolDriftPanel (and the app equivalent). The rule is identical in each: build a lastSeen map across all snapshots, then anything not present in the latest snapshot's key set is pushed into absentEntities (see frontend/src/components/monitor/DeviceDriftPanel/DeviceDriftPanel.tsx:52-88).

  • AbsentEntity (frontend/src/features/monitor/types/monitor.types.ts:104) already carries lastSeenFileName, lastSeenStartTime, lastSeenFileId.
  • LastSeenModal surfaces the last-seen file + capture start time to the user.

So the "last seen" snapshot + timestamp already exist — we just don't use them to make a threshold-based inactivity decision.

Proposed feature

Let the user configure an inactivity threshold under one of two modes. An entity is active if it's in the latest snapshot, inactive if it falls outside the configured threshold, and quiet (new) if it's absent from the latest snapshot but still within the threshold — i.e. it was just quiet in that window, not gone.

Mode A — days since last seen

An entity is inactive if it was last seen more than N days ago. The "how many days ago" is measured against a configurable reference point:

  1. Relative to the latest PCAPlatestSnapshot.startTime − entity.lastSeenStartTime > N days. Anchoring to capture time (not wall-clock) is the right default for retrospective analysis of an already-collected set of PCAPs, where "today" is meaningless.
  2. Relative to today's datenow − entity.lastSeenStartTime > N days. Appropriate for a live/ongoing monitored network where staleness against the real current date is what matters.

Mode B — last seen within the latest N PCAPs

An entity is inactive if it does not appear in any of the most recent N snapshots (ordered by capture start time). This is capture-count-based rather than time-based, which is the more natural threshold when captures are taken on an irregular cadence — "seen in at least one of the last 3 captures" is meaningful even when the gap between captures varies wildly, where a fixed day count would not be.

The three modes (days-relative-to-PCAP, days-relative-to-today, last-N-PCAPs) are mutually exclusive per network.

Configuration surface

  • Threshold config lives per network (different networks have different capture cadences). Suggested shape on NetworkEntity / NetworkDto:
    • inactivityMode (enum: DAYS | SNAPSHOTS)
    • inactivityThreshold (int — interpreted as days when DAYS, as a snapshot count when SNAPSHOTS)
    • inactivityReference (enum: LATEST_PCAP | TODAY) — only relevant when inactivityMode = DAYS
  • Editable from EditNetworkModal, defaulted at creation in CreateNetworkModal.
  • Sensible defaults, e.g. inactivityMode = DAYS, inactivityThreshold = 7, inactivityReference = LATEST_PCAP — preserving today's behaviour reasonably (with a small grace window instead of the current 0-day cliff).
  • Flyway migration to add the columns.

Applying the threshold

Replace the binary "in latest snapshot ⇒ active" check in the drift panels with the three-way classification driven by the configured mode/threshold:

  • Active — present in the latest snapshot.
  • Quiet (new) — absent from the latest snapshot but within the threshold. Present it distinctly from truly inactive (e.g. a muted "quiet" badge rather than "absent"), so the user isn't misled into thinking it left.
  • Inactive — outside the threshold (last seen > N days ago, or not in any of the last N snapshots).

The mode/threshold must be applied consistently across all four drift panels (device / IP / app / protocol) and reflected in the network diagram and any absent-entity counts.

Acceptance criteria

  • Per-network config for inactivityMode (DAYS | SNAPSHOTS), inactivityThreshold (int ≥ 0), and inactivityReference (LATEST_PCAP | TODAY, used only in DAYS mode).
  • Editable in EditNetworkModal, set on creation in CreateNetworkModal, persisted via Flyway migration + NetworkDto.
  • Drift panels (device / IP / app / protocol) classify entities as active / quiet / inactive using the configured mode and threshold, instead of the current "not in latest snapshot ⇒ absent" rule.
  • DAYS mode with "relative to latest PCAP" measures days from the latest snapshot's startTime; with "relative to today" measures from the current date.
  • SNAPSHOTS mode classifies an entity inactive when it appears in none of the most recent N snapshots.
  • Quiet-but-recent entities are visually distinct from inactive ones.
  • Defaults preserve a reasonable out-of-the-box experience (documented default mode + threshold + reference).

Notes / open questions

  • The classification currently happens on the frontend. Given the config is per-network and needs to be consistent across panels, consider computing the active/quiet/inactive state server-side (or in a shared hook) rather than duplicating the day-/snapshot-math in each drift panel.
  • UI should hide the reference selector when SNAPSHOTS mode is chosen, since it doesn't apply.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestmonitorMonitor modeuxUser experience

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions