POST /pod/spec
Insert or update pod metadata. Upserts onpod_name (the primary
key) — a pod restarting reusing the same name replaces the
previous row in place. Empty/whitespace-only pod_name is
rejected at the handler (warn-log + no-op) to keep a sentinel
empty-PK row from polluting the table.
Request
pod_identity and workload_selector_labels are populated by the
controller’s extract_pod_identity_and_selectors helper (priority:
app.kubernetes.io/name → app.kubernetes.io/component → k8s-app
→ owner-reference walk to Deployment/StatefulSet/DaemonSet).
host_network mirrors spec.hostNetwork. It is optional on ingest (older controllers omit it; the broker
then derives it from pod_obj.spec.hostNetwork) and always present on
read as true, false, or null when unknown. Generators use it to
render host-network peers as ipBlock / Cilium entities instead of a
podSelector — see
Host-network peers and targets.
started_at is the last field of the record and is optional on
ingest. The broker captures it from pod_obj.status.startTime (the
API server’s RFC 3339 value, converted to naive UTC) before
compact_pod_obj strips status, and stores it in its own column
pod_details.started_at; a posted top-level started_at wins over the
derivation. On read it is always present, as "2026-08-04T09:12:41"
or null when unknown — a manifest with no status.startTime, or a row
last written by a broker predating the column (it fills in on the pod’s
next watch upsert; every live pod is re-posted when the controller
restarts). It is the input to the start-time guard: a flow is never
attributed to a pod that started after the flow’s time_stamp, nor to
one with no started_at. A row still marked alive that has not been
re-posted for PEER_STALE_ALIVE_SECS (default 900) is marked dead by
the broker’s stale-alive sweep. See
How peers are attributed.
The full record as returned by every GET below:
POST /pod/mark_dead
Mark a pod’s row as dead without deleting it (preserves historical correlation againstpod_traffic rows referencing the same IP).
The controller’s reconcile_pods_task calls this periodically for
pods that left the node — sending pod_ip alongside pod_name
lets the broker apply a precise (pod_name, pod_ip) filter rather
than a name-only one, avoiding the race where a same-name restart’s
live row gets marked dead.
Request
pod_ip is optional for backwards compatibility with older
controllers; when absent, the broker falls back to a name-only
filter (and logs a warn for the missing precision). Empty
pod_name is rejected.
GET /pod/info
Return all pod metadata rows. Ordered by(pod_namespace ASC, pod_name ASC) for stable display.
The stored/returned pod_obj is compacted, not the full manifest:
only metadata (minus managedFields) and spec.hostNetwork are
kept — containers, volumes, and status are dropped.
Query Parameters
GET /pod/namespaces
The distinct namespaces of pods running now, sorted, as a JSON array of strings. Cluster-wide rows (NULL namespace) are left out. This is what the UI’s namespace picker reads; it used to derive the list from the whole/pod/info listing, which on a busy cluster grew past the browser’s
request timeout.
GET /pod/name/{name}
Get a single pod by name. Returns the live row when one exists (falls back to the most-recent dead row only if no live row matches — defensive for a hypothetical future schema where multiple rows perpod_name are possible).
GET /pod/ip/{ip}
Get a single pod by IP. Matches either the primarypod_ip or any
address in pod_ips, so a dual-stack pod resolves on both families;
IPv6 input is canonicalised before matching.
Since peer identity is stamped on traffic rows at ingest, consumers
only need this lookup for rows whose peer_* fields are null
(pre-upgrade history, or a peer the broker never learned about). For
those, pass the row’s time_stamp as at.
Query Parameters
Without
at, the pick among several rows holding the IP is
is_dead ASC, time_stamp DESC: the live pod, else the most recently
seen dead one. That answers “who holds this IP now”, which is the
wrong question for a flow observed weeks ago — pod IPs are recycled
constantly and pod_details keeps no ownership history.
With at, candidates are filtered by the start-time guard
started_at <= at (and, for a dead candidate, record time_stamp >= at
— it must not have been dead already at at), then ordered
is_dead ASC, started_at DESC, time_stamp DESC; the first wins. A pod with a null started_at is
excluded: the controller re-posts every live pod every 60 s, so within
a minute of the broker upgrade every live pod has one, and null
means a ghost row or a Pending pod.
So for the recycled-IP case in
How peers are attributed,
?at=2026-07-23T… never returns autobrr (started 2026-08-04); it
returns the CronJob pod that held the IP then if its row survived, and
404 otherwise. The ingest resolver and the late-resolve task apply the
same guard and ordering with at = row.time_stamp.
A 404 with body "No data found" is returned when no row matches —
including when every candidate is excluded by at. Treat that as an
unattributed peer, not an error.
GET /pod/list/{node}
Return all live pods recorded for the named node. The controller’s reconciler calls this to compute the dead-pod diff each cycle. Filtered tois_dead=false; ordered by
(pod_namespace ASC, pod_name ASC) so reconciler “marking X as
dead” log sequences are deterministic.