Skip to main content

POST /pod/spec

Insert or update pod metadata. Upserts on pod_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 against pod_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 per pod_name are possible).

GET /pod/ip/{ip}

Get a single pod by IP. Matches either the primary pod_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 to is_dead=false; ordered by (pod_namespace ASC, pod_name ASC) so reconciler “marking X as dead” log sequences are deterministic.