> For the complete documentation index, see [llms.txt](https://docs.layeronecloud.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.layeronecloud.com/network-monitor/architecture/trust-and-data-flow.md).

# Trust and data flow

Each Proxmox node runs an unprivileged l1-monitor-agent systemd service with only CAP\_NET\_RAW.

Each Proxmox node runs an unprivileged `l1-monitor-agent` systemd service with only `CAP_NET_RAW`. It opens configured libpcap interfaces, reads kernel interface counters, performs bounded metadata parsing and DDoS classification, and writes encoded historical aggregate batches to a local SQLite spool. Live messages contain at most the top 100 device totals, top 100 other dimensions, and (since 0.1.18) a separate allowance of 1,000 exact host rows per capture, while minute aggregation has a hard 20,000-key memory bound and marks coverage degraded if that bound is hit. The agent is the only component that contacts the central server, and it does so over outbound HTTPS with a host-scoped bearer key read from a root-managed credential file. The full key is never stored by Django.

An independent continuous scanner obtains an authorized plan from the monitor's existing cached Pulsar VM/NIC inventory. It scans only active guests assigned to its own hypervisor, binding to the WAN address owner rather than the packet capture port. Its separate systemd service delegates a resource-limited cgroup to each active target, leaving normal traffic collection outside scanner limits. It reads the main agent's refreshed local desired-config cache and supplies a local health status file, with no additional configuration or inventory calls. Three dedicated pools allow eight daily full scans, two customer full scans, and two hourly top-100 TCP/UDP scans, with no overlapping scans for one target. The site's operator-managed daily schedule defaults to 22:00 America/New\_York. The plan carries the latest eligible daily boundary as a stable UTC timestamp; GET requests do not schedule or mutate work. In managed mode, the existing Aegis worker submits released daily intents to Operations, then polls and claims its durable queue. The sensor executes the Operations UUID, not a second locally generated daily attempt. Its durable schedule still manages hourly common scans and prevents duplicate execution; long scans continue past the boundary. Host pressure suspends scanning. Discovery and bounded open-port service probes produce a separate durable JSON outbox. The monitor stores current attempts, preserves daily full results independently from hourly common results, and retains positive evidence with its original scan profile and timestamps. Common scans cannot retire evidence outside their coverage or resolve a customer's full scan request. Full evidence remains fresh for 27 hours and common evidence for two hours. Applications shows each profile's coverage. Exact public-allocation results use the existing Aegis telemetry ownership page and independent receipts per scan UUID. Profile-aware receivers get explicit coverage; older receivers receive common results as partial full-range evidence.

Sensor 0.1.24 durably records actual successful spawn and supervised heartbeats in a separately acknowledged lifecycle outbox. Operations distinguishes Queued claims from Running execution, and retains manual and automatic attempt history. Late lifecycle proof can enrich terminal timestamps but cannot reopen a job. Filtered or silent ports are explicit uncertainty, not a failed scan; completed uncertain sweeps preserve older positive observations conservatively. See [scan lifecycle](/network-monitor/scan-lifecycle.md) for source ownership and retry rules.

The Nmap child uses `-T4` with adaptive 50–500 ms probe timeouts (100 ms initial), one retry, at most 256 outstanding probes, and an 8,000 packets/sec discovery ceiling per host. TCP discovery ignores throttled reset replies; UDP discovery likewise ignores ICMP rate limiting. Only positively open ports advance to `--version-all` identification, whose service waits remain separate from discovery timing. No minimum probe rate or host timeout is imposed. Neither plan building nor scan delivery adds Pulsar inventory/discovery reads.

The central endpoint hashes the bearer key to identify the agent, then validates compressed and decompressed size limits, the protobuf schema version, and that the envelope agent ID matches the authenticated key. Detailed history is stored in a PostgreSQL inbox table before the upload is acknowledged, so an upload is durable the moment the sensor hears 202 and the sensor deletes its copy at once. Workers claim one agent at a time with a skip-locked lock on the agent row, apply that agent's rows in sequence order inside one transaction and delete them; any number of workers on any number of web instances share the inbox without coordination. Traffic and application details close on each wall-clock minute, carrying the start of that completed minute even when an outage delays delivery. Shutdown and capture reconfiguration also retain the final partial minute.

Since agent 0.1.16, a separate `/agent/v1/live` request carries current traffic. One `LiveSnapshot` carries the latest measured window from each capture, normally every ten seconds as of 0.1.17. Its supervised uploader uses an independent sequence and a bounded memory buffer; newer readings replace unsent readings and windows older than thirty seconds expire. This path authenticates established installations without the database row lock used by history, then publishes directly to Redis and Django Channels. It neither consumes a durable sequence nor waits for a historical commit. Independent health reports continue every fifteen seconds. The server advertises `live_upload_version: 1`; older servers retain the legacy spooled-live path during rolling upgrades.

The live request timeout is fifteen seconds, and a failed attempt waits ten seconds before trying the newest available readings. The server publishes all capture windows in one Redis history transaction and one asynchronous fan-out operation. History and WebSocket delivery run concurrently with a two-second deadline each; if both fail, the live endpoint returns a retryable failure. Legacy durable live envelopes still commit even when disposable publication fails. The upgrade changes existing two-second defaults to ten seconds and bumps their configuration revision; explicit custom intervals are preserved.

Positive device activity in live totals also updates a bounded five-minute presence cache. Device status uses the newer of this observation and durable inventory history, so current activity stays visible during history delays. Omission from a bounded live ranking does not itself mark a device offline.

Telemetry uses Protocol Buffers and Zstandard level 3. Minute metric rows are ordered by their identity so repeated device and dimension strings compress together; this changes neither counters nor live ranking. When a minute needs multiple uploads, its intact application-fact and coverage block shares the last metric chunk if it fits the existing count and byte bounds. Discovery events omit display text that binding ingestion does not use, and omit segment/source only when the receiver's existing defaults recover the same values.

Repeated uploads are deduplicated using the locked per-boot committed sequence and, for an envelope applied ahead of that prefix, its own inbox row kept as a tombstone until the prefix passes it. An envelope the server cannot apply is recorded as a `telemetry_rejected` event and skipped like a sensor gap, so one bad batch never strands the history behind it. Unchanged committed prefixes and inventory topology fields are not rewritten. This avoids unnecessary PostgreSQL row versions while preserving acknowledgement and inventory timing. Interface, device, host, application, and endpoint records intentionally retain different views of the same traffic; reducing wire size does not reduce the number of historical metric rows. The read-only [`postgres_storage.sql`](https://github.com/LayerOne-LLC/Network-Monitor/blob/master/tests/performance/postgres_storage.sql) report groups daily partitions and separates table and index space for storage investigations.

WAN availability has three states. A probe round that arrived is a verdict, up or down. A minute nobody reported is unverified: it is reported on its own and counted neither as up nor as down, so the reporting path failing never reads as the link failing. The newest probe round also rides the health report, whose small JSON path keeps working when bulk telemetry is stuck; the control plane applies it to current link state only and leaves history to the durable round.

The one-hour chart also has a disposable Redis repair stream for each site. Its stored frames omit application and port dimensions that the history endpoint does not return and compress the remaining JSON with Zstandard; WebSocket subscribers still receive the complete live frame. Active streams are trimmed by Redis arrival time to `LIVE_HISTORY_RETENTION_SECONDS` (one hour by default) and approximately capped at `LIVE_HISTORY_MAXLEN` entries (20,000 by default), while the same retention is applied as a TTL so inactive site keys disappear. The environment overrides are `L1_LIVE_HISTORY_SECONDS` and `L1_LIVE_HISTORY_MAXLEN`.

The overview's live throughput graph uses ten-second averages. Each report's byte counters are divided by its actual measured duration, then weighted by overlap with each chart bucket before designated uplinks are summed. The two-second dashboard refresh is independent of that averaging period. Partial buckets use the measured portion, and the existing short delivery-gap allowance keeps a recent reading visible until it expires. Historical ranges still use minute rollups.

The spool normally uploads in insertion order and is an outage buffer, not a queue: a batch is deleted the moment the server accepts it, so the spool is empty whenever the server is reachable and only grows while it is not. Once the oldest entry is at least fifteen seconds old, the uploader sends the newest WAN probe round and any legacy spooled live window, then up to eight historical batches per cycle. New live snapshots use their independent uploader throughout. Delivery of history is still at least once: a batch whose acknowledgement was lost is uploaded again and refused by the inbox, and late WAN probes update historical minute totals without replacing newer link or reachability state. The sensor's fifteen-second health report carries how many batches are still waiting on it and how old the oldest is; the Agents page shows that beside the server's own unprocessed count for the same sensor.

The DDoS detector runs before ordinary aggregation discards source-IP detail. It keeps non-evictable capture-wide and protocol-wide counters alongside bounded per-target detail, so a target or port spray cannot hide by exhausting the detail map. Ten-second windows are evaluated every two seconds for volumetric, SYN, UDP/reflection, ICMP, fragmented-packet, TCP RST, and ACK-only floods. Conservative absolute thresholds always remain active. When capture coverage is healthy, a slowly learned EWMA mean and variance can also identify a material departure from that target's normal traffic; candidate, active, degraded, and post-incident windows do not train the baseline. Adaptive thresholds cannot fall below 50% of the corresponding absolute threshold, and adaptive detection always requires its minimum source count, regardless of the threshold multiple. The static path retains its source-count exception at 2× the absolute threshold and its authoritative-interface safeguard when packet coverage is poor.

Opening requires three confirmations with sufficient fresh evidence. Each confirmation must have qualifying traffic in the newly completed two-second packet bucket or a new qualifying authoritative interface sample. A sample can confirm only once; overlapping ten-second windows cannot count the same brief burst three times. During the quiet interval before an incident resolves, newly observed sources are no longer attributed to that incident.

Every observed inbound packet is assigned to at most one confirmed sensor incident, with the most specific matching incident winning, or to the `other` DDoS dimension. Those raw observed bytes and packets are emitted as ordinary `MetricPoint` values and are never scaled for capture loss. Interface RX counters remain the bandwidth source of truth and provide a corroborating context line; missing packet observations remain gaps. The control plane correlates matching target/scope and protocol-family reports within a site into one operator-facing incident. Rates and source estimates use the maximum member observation rather than adding duplicate vantage points.

The detector emits readiness heartbeats so the control plane can distinguish a legacy agent, disabled capture, missing protected targets, baseline learning, ready detection, and degraded capture. It retains bounded source contributors and emits incident/source deltas rather than individual packets. Source activity ratings require absolute packet counts, sustained rates, traffic share, and capture quality. The server caps each sensor's stored rating before grouping sources, preserving raw observations while preventing a historical seven-packet source from retaining a high activity rating. Event confidence independently uses one supported sensor detection; rates, source counts, and quality from different sensors cannot be combined to manufacture stronger evidence. Neither rating is a probability of malicious traffic or an automatic blocking action. Evidence-enabled captures keep up to 30 seconds of pre-trigger packet data in a bounded local buffer and write matching traffic to 60-second PCAP chunks. Agents upload completed chunks directly to private S3-compatible storage using short-lived, checksum-bound URLs; bucket credentials never reach a sensor.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.layeronecloud.com/network-monitor/architecture/trust-and-data-flow.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
