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 (preferPOST /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 distincttraffic_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 sevenpeer_* 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 capturesname directly — no separate namespace path segment — so:
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.