Repository navigation
feat: namespace filters and opt-out annotations with coverage counters - #11
Merged
Merged
Conversation
Add the collection filter layer promised in docs/security.md: namespace
allow/deny lists ('*' globs; empty allow admits all, deny wins) from a
new YAML config file (-config flag; in-cluster this will be the
Helm-rendered ConfigMap, ADR 0001), plus the rebuildstack.co/collect:
"false" opt-out annotation on namespaces and pods, honored regardless
of configuration and evaluated live via a namespace informer.
The filter gates every pod-derived signal before it reaches a callback:
an excluded pod is neither reported nor scanned for OOM kills. Nodes
are deliberately not gated (security.md §8). Excluded pods surface only
as aggregate counts per filter type — never by name — logged as a
periodic coverage line, the seed of the §11 coverage report. Exclusions
attribute to the first rejecting filter in fixed order: namespace
filter, namespace annotation, pod annotation.
Config parsing is strict: an unknown field is an error, so a typo
cannot silently disable a filter.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
kfeofantov
added a commit
that referenced
this pull request
Aug 22, 2026
An audit of security.md. Roughly a third of its 750 lines was a second copy of the decision records: §10.1 restated ADR 0016 including its argument about recording rules; §10.2 restated ADR 0015's Decision; the on-node scanning subsection restated five ADRs across 89 lines. CLAUDE.md rightly requires this file updated in the same PR as any collection change, and what each slice did with that rule was append its ADR's summary. The cost is the same one ADR 0022 addressed, and it is measurable. "Four aggregate counters" was stale in ADR 0009 §5 and in security.md §8 — one wrong claim, two files. The false eligible-set claim existed in three copies: ADR 0011 §3, security.md §7.2, and the §5 network table; fixing the code comments in #48 left two standing. Both are corrected here. Three further defects, all of the kind a mechanism catches and a reader does not. The section claiming to enumerate what leaves the cluster never named usage_snapshot, usage_window or ebpf_profile — the two highest-volume payloads appeared only as "hourly rollup histograms". A cross-reference sent the reader to "§11, your controls" while §11 was titled Agent self-reporting and listed no controls. And the first design principle, "no write verbs on any Kubernetes resource", was contradicted by §4's identity-Secret grant and then repeated a third time in §9 — ADR 0008 had said explicitly that the categorical version must go. Two conventions now carry the file. Reasoning moves to the ADRs and the document asserts and links instead of re-arguing. A claim that is not built is marked [planned] at the sentence, not in a footnote: backend transport and mTLS, the identity Secret grant, enrollment, the pprof profile and its pod probing, the shipped NetworkPolicy, the startup self-audit, the filter-fingerprint payload. The label selector is removed as a decision, not an omission. It appeared twice in this file and once in ADR 0020 §2, was never decided in any ADR, and never existed in the configuration — the only claim the audit found with no source at all. The controls are the namespace allow/deny lists and the opt-out annotation. docs/security_test.go holds the parts that are lists: the new payload table must mirror internal/sink/registry.go exactly in both directions, every internal anchor must resolve to a heading, and every link into adr/ must resolve to a file. Both checks were negative-tested; the anchor check reproduces the broken #11-your-controls link that shipped in this file. Honest result on size: the document did not get shorter. About 130 lines of duplicated reasoning came out and about the same went in — the payload table that was missing, the controls table that was scattered, and the eligible-set paragraph that was wrong. What changed is what the length is made of. ADR 0024 records that rather than claiming a reduction. Closes two more of ADR 0022 §5's five open items. Remaining: eligible-set enforcement, and the controller Deployment vs StatefulSet question. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The filter layer, landed before the agent ever writes anywhere — filters must exist before the first payload does.
What
internal/config: first agent configuration — a YAML file via-configflag (in-cluster this becomes the Helm-rendered ConfigMap mounted as a file, per ADR 0001). Parsing is strict: unknown fields error out, so a typo cannot silently disable a filterinternal/collector/filter.go:PodFilterwith namespaceallow/denylists (*globs only; empty allow = admit all; deny always applies and wins on conflict) and both opt-out annotations (rebuildstack.co/collect: "false"on namespace or pod), honored regardless of configuration. Namespace annotations are read live from a new namespace informer, so annotating a namespace mid-run takes effectpod observedand nooom kill observed. Nodes are deliberately not gated (security.md §8)coverageline — the seed of the §11 coverage reportScope
Implements exactly what docs/security.md already promises (§4 namespaces RBAC, §8 aggregate-counts rule, §11 opt-out) — no doc change needed.
sigs.k8s.io/yamlmoves from indirect to direct dependency.Verified
pods_observed:0, excluded_namespace_filter:9, no excluded names anywhere in the log🤖 Generated with Claude Code