Skip to main content

POST /pod/traffic

Insert a single observed traffic event. The kguardian controller calls this directly when a network event arrives from one of the eBPF ring buffers; external integrations rarely need it (prefer POST /pod/traffic/batch below for bulk). Duplicate events (same pod_ip, pod_port, traffic_type, traffic_in_out_ip, traffic_in_out_port, decision) are deduped server-side; the check is protocol-aware, branching on the event’s protocol rather than treating ip_protocol as a plain key column. The result reported back is inserted-or-Null. The audit forwarder (when evaluator.enabled: true) is fired only for events that were genuinely new.

Request

traffic_type is "INGRESS" (matches the upstream NetworkPolicy casing) or "EGRESS"; decision is "ALLOW" (eBPF observed an allow) or "DROP" (NetworkPolicy dropped the flow before it reached the destination, observed via the netpolicy-drop probe). The request carries only the peer’s IP (traffic_in_out_ip). The broker resolves it to a peer identity as part of the insert and stores the result on the row — see Peer attribution below. Clients need not send the peer_* fields; any values they do send are overwritten.

POST /pod/traffic/batch

Bulk variant taking a JSON array of the same shape. The controller batches up to 100 events at a time (or one second elapsed since last flush, whichever first); call sites talking to a remote broker should use this path to amortise the round-trip. Response is an integer count of the rows actually inserted after dedup. Peer resolution runs once per distinct traffic_in_out_ip in the batch, not once per row, so a 100-row batch from a pod talking to three peers costs three lookups. The dedup key is unchanged: the peer_* columns are never part of it.

GET /pod/traffic

Get a most-recent-first window of observed traffic rows, ordered (time_stamp DESC, uuid DESC) — newest first, with the UUID primary key as a tiebreak for microsecond-collision rows from a single batch ingest. The query is backed by the idx_pod_traffic_time_stamp index.

Query Parameters

Example

Response

Peer attribution

The seven peer_* fields are the peer identity the broker resolved when the row was ingested (or shortly after, by the late-resolve pass). They are the fields every consumer should read first; the peer IP is a fallback, not the source of truth. Why: How peers are attributed. peer_kind values the broker writes: There is no "external" value. Stamping it permanently would remove the guarded by-IP fallback for a row whose peer spec merely arrived late, so an unmatched peer stays null. Consumers have exactly two cases: peer_kind set → use the stored identity verbatim; peer_kind null → resolve with GET /pod/ip/{ip}?at=<row time_stamp> and, on a 404, render an unattributed peer (an ipBlock, never a selector). A null row younger than PEER_LATE_RESOLVE_WINDOW_SECS (default 600, chart value broker.peerResolution.lateResolveWindowSeconds) is retried by a broker task every PEER_LATE_RESOLVE_INTERVAL_SECS (default 60) until it resolves or ages out. Rows written before the columns existed are not backfilled. The seven fields follow time_stamp and are always present on read. On POST they are #[serde(default)]: an old controller that omits them is accepted, and anything a client sends in them is overwritten by the broker’s own resolution.

GET /pod/traffic/{name}

Get traffic for a single pod by name. The actix route captures name directly — no separate namespace path segment — so:
Same row shape as GET /pod/traffic above, peer_* fields included; same ordering. A name that doesn’t match any rows returns 404 with body "No data found". The frontend’s per-pod traffic view handles this transparently by falling back to an empty list.