ADR-0201: CRD model — prefixed kinds, static vs reconciled split¶
The
Kollect*CRDs, their scopes, and the split between static config and reconciled work kinds.
Theme: 02 · API & tenancy · Status: Current
KollectInventory moved to namespaced; cluster-scoped variants reserved.
Context¶
Kollect replaces a hardcoded batch collector schema with CRD-driven configuration. Operators in this space use different tenancy and config patterns:
- external-secrets splits
SecretStore(namespaced) vsClusterSecretStore(cluster) with namespaceconditionson the cluster variant; provider config is a discriminated union in spec. - Flux notification-controller keeps
ProviderandAlertas static objects (no status subresource, no dedicated reconciler) whileReceiveris reconciled. - Argo CD uses
AppProjectas a tenancy boundary andApplicationas the reconciled unit.
Kollect must combine generic attribute selection, resource selection, aggregation, and multi-backend export — no single OSS project covers all of this. Templated documentation sync (Confluence, etc.) is explicitly rejected (ADR-0702).
Platform users commonly terminate TLS to internal Git/GitLab with custom CAs; sink configuration must support that from early phases, not as a later bolt-on.
Decision¶
API group kollect.dev/v1alpha1. All kinds are prefixed (Kollect*) to avoid collisions.
Static config (validated, no controller)¶
| Kind | Scope | Role |
|---|---|---|
KollectProfile |
Namespace (breaking; was cluster) | Reusable extraction schema: GVK + named CEL/JSONPath attributes (ADR-0204) |
KollectSink |
Namespace (breaking; was cluster) | Backend config: type (git, gitlab, s3, gcs, postgres, kafka) + endpoint + secretRef + TLS trust (ADR-0201) |
KollectScope |
Namespace (Phase 1 priority) | Tenancy boundary: allowed GVKs, namespaces, sinks for a team (ADR-0203) |
Reconciled (controller + dynamic informers)¶
| Kind | Scope | Role |
|---|---|---|
KollectTarget |
Namespaced | profileRef + selectors; drives collection in team namespace (ADR-0201) |
KollectClusterTarget |
Cluster | Platform-wide collection across namespaces via namespaceSelector; profileRef → KollectClusterProfile or platform-namespace profile (ADR-0201) |
KollectInventory |
Namespaced | Aggregates namespaced targets in the same namespace; dispatches to sinks |
KollectClusterInventory |
Cluster | Platform rollup: composes namespace snapshots/shards from federated targets — explicit federation, not implicit whole-cluster capture (ADR-0203) |
Rejected (never ship)¶
| Kind | Rationale |
|---|---|
KollectPublication |
Doc-sync / Confluence / in-operator templating — out of scope; use Git export + external CI (ADR-0702) |
KollectHub |
Hub Deployment lifecycle via CRD — rejected; use shared-sink fleet (ADR-0501) |
Reconciled (connection test)¶
| Kind | Scope | Role |
|---|---|---|
KollectConnectionTest |
Namespace | One-shot / CI probe of sink (+ optional profile); ADR-0201 |
Reserved (designed, not built yet)¶
KollectReceiver— inbound webhook → trigger (Flux Receiver pattern).KollectTargetSet— generator templating many Targets (ApplicationSet pattern).KollectClusterProfile(cluster) — platform-shared extraction schemas (ADR-0204).KollectClusterScope(cluster) — platform tenancy boundary when namespacedKollectScopeis insufficient; ships with collection ceiling fields (ADR-0207). Frozen/minimal — no new behavioral knobs without a concrete platform blocker.
Cluster-scoped family sink kinds (KollectClusterSnapshotSink, KollectClusterDatabaseSink,
KollectClusterEventSink) are shipped and retained long-term (ADR-0414).
The historical unified KollectClusterSink name remains reserved; use family cluster variants.
Short names: kprof, ksink, kscope, ktgt, kinv (reserved: kcinv, kcscope). kpub was
reserved for rejected KollectPublication — do not use.
All reconciled kinds support spec.suspend and kollect.dev/requestedAt manual-trigger annotation.
Validating admission (early)¶
Validating webhooks (Phase 0/1, not post-hoc workarounds) must reject at apply time:
- Invalid or non-compilable CEL and JSONPath expressions on
KollectProfileattributes - Unknown
KollectSink.typevalues (enum aligned with ADR-0602) - Cross-field constraints already expressed as CEL
x-kubernetes-validationsin OpenAPI
Runtime collection may still surface ErrTerminal for GVK/API discovery failures; expression syntax
is webhook-validated first.
KollectSink TLS trust (custom CA)¶
Git and GitLab sink specs include explicit trust material (at least one of):
tls.caSecretRef— preferred — namespaced secret key containing CA bundletls.caBundle— inline PEM (base64) for org-internal CA; validating webhook enforces max 64 KiB — large CAs must usecaSecretRef
Default remains verify TLS; insecureSkipVerify is opt-in only for dev and must be flagged in
status when used.
Credential material stays in secretRef (tokens, SSH keys) — never in spec/status.
JSONPath filters on targets¶
Deferred: sink-side or target-side JSONPath filters (post-extraction row filtering) are not Phase 1 API. Schema clarity and aggregation matter more than where filtering runs (REQUIREMENTS.md).
Pre-store collection gates (namespace allow/deny, resourceRules, CEL matchPolicy) live on
KollectTarget / KollectClusterTarget — see ADR-0207. That
is distinct from export-time Inventory row filters, which remain deferred.
Consequences¶
Positive¶
- Static Profile/Sink cuts moving parts (validated at admission, read at reconcile time).
- Namespaced Profiles and Sinks align with team ownership and
tenantModeinstalls. - Prefix naming is grep-friendly and avoids generic kind collisions in multi-operator clusters.
- Early webhooks prevent bad profiles from wedging reconcilers.
KollectClusterInventoryreserved for platform portal without blocking team-scoped MVP.KollectClusterInventoryfederation uses optionalspec.namespacesplus optional selectors — shard-composed rollups, not one monolithic payload.
Multi-operator clusters¶
Multiple independent operator installs may watch overlapping GVK/namespace scopes by design
— no admission guardrails block duplicate collection. Default golden path: one platform
cluster-wide operator with per-tenant KollectScope; team-owned namespace-scoped installs
remain supported with minimal RBAC beyond CRD install (ADR-0203).
Negative¶
- Namespaced inventory requires one object per namespace (or explicit federation via
KollectClusterInventory— ADR-0203). - Breaking scope migration for Profile and Sink requires sample and RBAC sweep.
- Webhook + CEL maintenance cost on every new attribute type rule.
Open questions¶
- RESOLVED :
KollectSinkis namespaced;KollectClusterSinkreserved for platform-shared backends (ADR-0201). - RESOLVED : Keep both
caBundleandcaSecretRef;caSecretRefpreferred;caBundlemax 64 KiB at webhook. - RESOLVED :
KollectClusterInventoryfederation supports optionalspec.namespaces(empty or absent = documented semantics; not required) plus optional selectors — shard-composed rollups, not implicit cluster-wide wildcard.