Skip to content

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) vs ClusterSecretStore (cluster) with namespace conditions on the cluster variant; provider config is a discriminated union in spec.
  • Flux notification-controller keeps Provider and Alert as static objects (no status subresource, no dedicated reconciler) while Receiver is reconciled.
  • Argo CD uses AppProject as a tenancy boundary and Application as 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; profileRefKollectClusterProfile 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 namespaced KollectScope is 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 KollectProfile attributes
  • Unknown KollectSink.type values (enum aligned with ADR-0602)
  • Cross-field constraints already expressed as CEL x-kubernetes-validations in 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.caSecretRefpreferred — namespaced secret key containing CA bundle
  • tls.caBundle — inline PEM (base64) for org-internal CA; validating webhook enforces max 64 KiB — large CAs must use caSecretRef

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 tenantMode installs.
  • Prefix naming is grep-friendly and avoids generic kind collisions in multi-operator clusters.
  • Early webhooks prevent bad profiles from wedging reconcilers.
  • KollectClusterInventory reserved for platform portal without blocking team-scoped MVP.
  • KollectClusterInventory federation uses optional spec.namespaces plus 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 KollectClusterInventoryADR-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 : KollectSink is namespaced; KollectClusterSink reserved for platform-shared backends (ADR-0201).
  • RESOLVED : Keep both caBundle and caSecretRef; caSecretRef preferred; caBundle max 64 KiB at webhook.
  • RESOLVED : KollectClusterInventory federation supports optional spec.namespaces (empty or absent = documented semantics; not required) plus optional selectors — shard-composed rollups, not implicit cluster-wide wildcard.