Skip to content

feat: what the filters excluded is named inside the cluster (ADR 0084) - #125

Merged
kfeofantov merged 1 commit into
mainfrom
feat/collection-decisions-endpoint
Sep 17, 2026
Merged

kfeofantov merged 1 commit into
mainfrom
feat/collection-decisions-endpoint

Conversation

@kfeofantov

Copy link
Copy Markdown
Contributor

The agent counts what its filters exclude and never names it. That is the promise about what leaves the cluster — and it has also meant the customer, who owns every one of those annotations and can read them with kubectl, learns from this agent only that nine of something were excluded. Two questions had no answer: which object carries the annotation someone wrote, and why a given workload is missing from the report when five controls could have removed it.

What this adds

A second listener on the controller, at 127.0.0.1:9091, serving /collection: one NDJSON line per namespace, pod and Job — collected or not, profilable or not, which control refused it, and whether its workload was a kind the agent cannot read (ADR 0028 §3).

Nothing is retained to answer it. The informer caches already hold every object whether admitted or not, so the listing runs the same Admit* calls the event path makes and cannot drift from the decision. Reading it moves no counter.

Why loopback and not a path on 9090

A NetworkPolicy selects ports, not paths. The health port is opened to any source by a rule whose own justification is that every path there "names nothing in the cluster" — the caller is the kubelet, arriving from an address no pod selector can name. A path naming objects does not share that port. The policy is also not rendered at all in a controller-only install, and is inert where the CNI ignores it, so the bind address is the only control that holds in every case.

Loopback also settles authorization without inventing any: kubectl port-forward is the only way in, and Kubernetes already gates it on create of pods/portforward in the agent's namespace.

A non-loopback collection.listenAddress is a startup failure rather than a value normalized into something safe.

What keeps it inside the cluster

Not the handler — the import graph. Nothing that can write a payload or open a connection imports the package, and TestNothingThatCanTransmitKnowsThisPackage fails if that changes.

Not changed

No payload byte, no RBAC, no new verb, no Service, no container port, no rule in the shipped NetworkPolicy. The inference ADR 0054 named is neither closed nor widened; this is not shipped anywhere.

Evidence

go test -race ./...     # all packages ok, e2e tags type-checked
golangci-lint run       # 0 issues (v2.11.4, the CI pin)
make chart-lint         # three profiles, 0 failed

Also built and tested from a clean git archive HEAD extraction.

Not exercised against a kind cluster: the endpoint has no e2e test in this PR.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KeZECJXTDHL6Nts95eKMJA

The agent counts exclusions and never names them, which is the promise about
what leaves the cluster — and it has also meant the customer, who owns every
one of those annotations, cannot find out which object carries one.

The controller now answers that inside the cluster: `/collection` on a loopback
listener of its own, one NDJSON line per namespace, pod and Job, with the
control that refused it and whether its workload could be read at all. Computed
on request from the informer caches through the same Admit* calls the event
path makes, so nothing is retained and the answer cannot drift from the
decision; reading it moves no counter.

Loopback because a NetworkPolicy selects ports and not paths: the health port
is open to any source by a rule whose justification is that every path there
names nothing in the cluster. A port-forward is the only way in, which leaves
authorization to the RBAC the customer already wrote. A non-loopback address is
a startup failure rather than a value normalized into something safe.

Nothing that can transmit imports the package, and a test on the import graph
is what keeps that true.

  go test -race ./... && golangci-lint run  # 0 issues, v2.11.4 as CI pins
  make chart-lint                           # three profiles, 0 failed

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeZECJXTDHL6Nts95eKMJA
@kfeofantov
kfeofantov merged commit 10702ee into main Sep 17, 2026
4 checks passed
@kfeofantov
kfeofantov deleted the feat/collection-decisions-endpoint branch September 17, 2026 18:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant