Expand description
Well-known wire-contract strings: the finalizer, labels, annotations, and condition types that form kopiur’s public Kubernetes surface (ADR §4.5, ADR-0005 §2/§14(c)).
These live in kopiur-api — not the controller — because they are part of
the API contract itself: external tooling (the kubectl kopiur plugin,
GitOps health checks, user automation) must agree on them byte-for-byte
with the operator. Controller-internal reasons/actions/deadlines stay in
kopiur-controller’s own consts module.
Constants§
- ALLOW_
IDENTITY_ CHANGE_ ANNOTATION - Acknowledges an intentional identity-affecting change on UPDATE. Two surfaces share it:
- ALLOW_
MASS_ DELETION_ ANNOTATION - Acknowledges a mass-deletion wave on a
Repository/ClusterRepository. Value: an RFC3339 timestamp. A HELD external deletion is released iff its Snapshot’smetadata.deletionTimestamp<= this value — “I approve what is pending NOW”. Deliberately VALUED (unlike the presence-only allow-identity-change ack, consumed at a single admission instant): this annotation is read continuously by the controller, so a presence-only ack left behind (or committed to Git) would disarm the breaker forever. With the timestamp, a stale ack is inert against any LATER wave, nothing ever needs to remove it, and the operator never edits user metadata. The controller clamps the effective value to <= its own now (clock-skew guard); an unparseable value is ignored (Warning event on the repository). - ALLOW_
REINITIALIZE_ ANNOTATION - Acknowledges a deliberate RE-INITIALIZATION of a
Repository/ClusterRepositorywhose backend was wiped: kopiur refuses to auto-create a fresh kopia repository over a once-Readyone (the pinnedstatus.uniqueId), and this annotation is the human “yes, I know the history is gone — make a new one”. - ALREADY_
INITIALIZED_ REASON reasonforSEEDED_CONDITION=Truewhenspec.seedwas a standing no-op: the repository was already initialized, so nothing was copied. This is the steady state of a seed block left in a GitOps manifest forever.- API_
VERSION - The API version string for kopiur CRDs (used in mover
TargetRefs andkubectl -o name-style output). - AZURE_
WORKLOAD_ IDENTITY_ LABEL - Pod label opting a mover pod into the azure-workload-identity mutating
webhook: pods carrying
azure.workload.identity/use: "true"and running as a federatedServiceAccountgetAZURE_TENANT_ID/AZURE_CLIENT_ID/AZURE_FEDERATED_TOKEN_FILE(and the projected token volume) injected — exactly the env kopia’s azure backend binds its credential flags to. Stamped by the operator (and the CLI’s browse sessions) on every mover pod for a repository whose azure backend usesauth.workloadIdentity. Lives here because the operator andkubectl kopiurmust agree on it byte-for-byte. - AZURE_
WORKLOAD_ IDENTITY_ LABEL_ VALUE - The
AZURE_WORKLOAD_IDENTITY_LABELvalue opting the pod in. - BLOCKED_
ON_ UNREADABLE_ RUN_ REASON reason/Event reason forSCHEDULE_RUNNABLE_CONDITION=False: a previous run of this schedule sits at a phase string written by a NEWER kopiur, so this build can never observe it reach a terminal phase. Under the defaultconcurrencyPolicy: Forbidthat stops the schedule permanently, and nothing about theSnapshotScheduleitself would otherwise say so — the silent-wedge shape of #359, one kind removed.- CONFIG_
LABEL - Label naming the
SnapshotPolicyaSnapshotwas produced from. - CREDENTIALS_
AVAILABLE_ CONDITION Snapshot/Restorecondition surfaced when the mover Job’s credential Secret is absent from the workload namespace —Falsecarries the actionable message (which Secret, which namespace, why, and how to fix). ADR §4.12.- DEFAULT_
CATALOG_ REFRESH_ INTERVAL - Default catalog re-scan cadence when
spec.catalog.refreshIntervalis unset: how often aReadyrepository re-lists its kopia snapshots to materialize (and expire)origin: discoveredSnapshotCRs. Part of the documented API contract (field-reference), so it lives here rather than in the controller. - DEFAULT_
CA_ BUNDLE_ KEY - Default key within a
tls.caBundleRefConfigMap whenkeyis unset — the conventional filename cert-manager and trust-manager emit CA bundles under. Lives here (not in the controller) because the kubectl-plugin resolves the same reference client-side and must agree on the default (centralize-config). - DEFAULT_
FAILED_ JOBS_ HISTORY_ LIMIT - Default
SnapshotSchedule.spec.failedJobsHistoryLimitwhen unset: how manyFailedSnapshotCRs from a schedule to retain (the rest are pruned). Bounds failure history so a schedule firing against a persistently-failing precondition or backend doesn’t accumulateFailedCRs forever. GFS retention applies only to successful snapshots, so this is the only bound on failures (ADR-0003). Part of the documented API contract, so it lives here, not in the controller. - DEFAULT_
HEALTH_ PROBE_ ENABLED - Default
spec.health.probe.enabledwhen unset: the periodic backend health probe is ON by default (#345). The probe is the sensor for the repository circuit breaker — with the defaultonFailure: Degrade, a sustained backend failure moves the repository toDegradedand pauses backups until a re-connect succeeds — so it must run unless the user explicitly opts out withenabled: false. Part of the documented API contract, so it lives here, not in the controller. - DEFAULT_
HEALTH_ PROBE_ FAILURE_ THRESHOLD - Default
spec.health.probe.failureThreshold: how many consecutive failing probes must accumulate before the loudRepositoryVanished/BackendReachable=Falsecondition is raised, an event fired, and — under the defaultonFailure: Degrade— the repository moved toDegraded(pausing backups). Debounces a single transient blip (an S3 list-after-delete race, a NAS reboot, a credential-rotation moment) from alarming on-call, tripping the breaker, or nudging a destructive manual recreate. - DEFAULT_
HEALTH_ PROBE_ INTERVAL - Default
spec.health.probe.intervalwhen unset: how often the backend health probe re-connects aReadyrepository to confirm the kopia repository still exists at the backend. Conservative — a vanished/unreachable repository is rare and the probe runs a short mover Job — so it leans long. Part of the documented API contract, so it lives here, not in the controller. - DEFAULT_
INDEX_ BLOB_ WARN_ THRESHOLD - Default
spec.health.indexBlobWarnThreshold: the index-blob count above which the reconciler warns that maintenance isn’t keeping up. A freshly-compacted repo sits near zero; a wedged-maintenance repo climbs unbounded (a real one reached 1448). Conservative so it only fires when maintenance is clearly behind. Overridable per-repo;0disables the warning. Part of the documented API contract, so it lives here rather than in the controller. - DEFAULT_
MASS_ DELETION_ THRESHOLD - Default
spec.deletionProtection.threshold(0 disables). 10 pending external destructive deletions is far above legitimate manual cleanup but far below a tooling-driven cascade (the motivating incident was ~600). - DELETION_
HELD_ CONDITION Snapshotcondition: this deletion is HELD by the mass-deletion breaker (Repository/ClusterRepositoryspec.deletionProtection.threshold) until acknowledged viaALLOW_MASS_DELETION_ANNOTATIONon the repository.- FANOUT_
TOO_ LARGE_ REASON reasonforSCHEDULE_FANOUT_CAPPED_CONDITION=True.- GROUP_
LABEL - Label naming the shared CSI
VolumeGroupSnapshota fanned-outSnapshotstages from — carried by BOTH the memberSnapshotCRs and theVolumeGroupSnapshotobject itself. - INDEX_
BLOB_ HEALTH_ CONDITION Repository/ClusterRepositorycondition reporting content-index-blob health (ADR-0005 §13).True= healthy (count under threshold);Falsewith reasonTooManyIndexBlobs= the index is growing unbounded because maintenance isn’t compacting. NON-BLOCKING: the repository staysReadyand GitOps health gates are not tripped — it’s a degradation warning, not an outage. Wire-visible (the kubectl plugin’sstatusreads it).- MAINTENANCE_
CONFIGURED_ CONDITION Repository/ClusterRepositorycondition recording whether aMaintenancecovers it (ADR §3.7). Wire-visible: GitOps health checks and the kubectl plugin’sstatusread it.- MANAGED_
BY_ LABEL - The standard
app.kubernetes.io/managed-bylabel key. Stamped on every operator-created object (mover Jobs, work-spec ConfigMaps, cache PVC, minted mover SA/RoleBinding, projected credential Secret, CSI VolumeSnapshots) so Argo/Flux recognize them as controller-owned and neither prune nor report themOutOfSync(ADR-0005 §14(c)). - MANAGED_
BY_ VALUE - The
MANAGED_BY_LABELvalue identifying kopiur-managed objects. - MASS_
DELETION_ BREAKER_ REASON reasonforDELETION_HELD_CONDITION=True.- MASS_
DELETION_ HELD_ CONDITION Repository/ClusterRepositorycondition: pending external destructive deletions for this repository are at/above the breaker threshold and held.- MASS_
DELETION_ THRESHOLD_ EXCEEDED_ REASON reasonforMASS_DELETION_HELD_CONDITION=Trueon aRepository/ClusterRepository: pending external destructive deletions for this repository are at/above its breaker threshold.- MIN_
CATALOG_ REFRESH_ INTERVAL - Floor for
spec.catalog.refreshInterval, enforced at admission. Each re-scan of an object-store repository runs a short mover Job; anything faster than this is Job churn with no operational value. - MIN_
HEALTH_ PROBE_ INTERVAL - Floor for
spec.health.probe.interval, enforced at admission. Each probe runs a short mover Job (object-store / volume-backed) or an in-process connect; anything faster than this is Job churn with no operational value. Shares the 30s floor with the catalog re-scan for the same reason. - MISSING_
CA_ BUNDLE_ REASON reason/Event reason forCREDENTIALS_AVAILABLE_CONDITION=Falsewhen the missing dependency is the backend’stls.caBundleRefConfigMap (or its key): the PEM CA bundle the mover needs to verify a private-CA S3 endpoint. The user creates it; a ConfigMap that never appears never self-heals, so this is a structural gate (crate::gates::MISSING_CA_BUNDLE_GATE), not just a transient retry.- MISSING_
CREDENTIALS_ REASON reason/Event reason forCREDENTIALS_AVAILABLE_CONDITION=False.- MISSING_
SERVICE_ ACCOUNT_ REASON reason/Event reason forCREDENTIALS_AVAILABLE_CONDITION=Falsewhen the missing dependency is the workload-identity ServiceAccount the backend’sauth.workloadIdentitynames (the user creates it; kopiur never does — its cloud annotations are the user’s federation contract).- MOVER_
PERMITTED_ CONDITION Snapshot/Restorecondition surfaced when a privileged mover is requested in a namespace that has not opted in —Falsecarries the actionable message.- OP_
LABEL - Label naming the operation a mover
Jobperforms, for Jobs whose owning CR doesn’t record the Job name in status (e.g.Restore). Values:OP_RESTORE,OP_RESTORE_TARGET. - OP_
RESTORE OP_LABELvalue for aRestore’s mover Job.- OP_
RESTORE_ TARGET OP_LABELvalue for aRestore’s operator-created target PVC.- ORIGIN_
LABEL - Label mirroring a
Snapshot’s origin (scheduled/manual/discovered). - PRIVILEGED_
MOVERS_ ANNOTATION - Namespace annotation a cluster admin sets to allow elevated (root/privileged)
movers in that namespace (ADR §4.11/§G16). Without it, a
SnapshotPolicywhosespec.moverrequests privilege is refused — a tenant could otherwise reuse the minted mover ServiceAccount at that privilege. Mirrors VolSync’svolsync.backube/privileged-movers. - PRIVILEGED_
MOVER_ NOT_ PERMITTED_ REASON reason/Event reason forMOVER_PERMITTED_CONDITION=False.- PRUNED_
BY_ ANNOTATION - Marks a
Snapshotthe OPERATOR is deleting as part of its own lifecycle, stamped immediately before the delete call. Values:PrunedByannotation values (retention,failed-history,policy-cascade,replication-retention). The Snapshot finalizer uses it to distinguish Kopiur’s own prunes from external deletions (GC cascades, kubectl, third-party controllers); external destructive deletions are subject to the schedule-cascade guard and the mass-deletion breaker, operator prunes are not. Any unrecognized value is treated as EXTERNAL (fail-safe). Wire-visible: users and tooling may read it on terminating CRs. - READY_
CONDITION - kstatus-compliant standard condition types (ADR-0005 §2) so
kubectl wait --for=condition=Readyand Flux/Argo health checks work natively against every reconciled kopiur CRD. The headline readiness condition. - RECONCILING_
CONDITION - Set
Truewhile a reconcile is making progress toward Ready. - REPLACEMENT_
NOT_ HELD_ REASON reasonforSCHEDULE_REPLACEMENT_HELD_CONDITION=False(not held).- REPLICATION_
LABEL - Label tying a blob-replication mover Job back to its owning
RepositoryReplication— the single-flight selector the controller uses, and the selectorkubectl kopiur replication runprints when a requested run fails and the user needs the Job’s logs. Lives here, next toSNAPSHOT_REPLICATION_LABEL, because the controller and the CLI must agree on it byte-for-byte; a copy in either would drift silently. - REPOSITORIES_
READY_ CONDITION SnapshotPolicycondition recording whether every repository the policy targets isReady. SetFalse(withREPOSITORY_NOT_READY_REASON) when at least one referencedRepository/ClusterRepositoryis notReady: backups, retention, adoption and verification against the not-ready subset are deferred (the ready subset keeps processing), and nothing about the policy’s phase-less surface would otherwise say so — the silent-wedge shape of #359 for the recipe kind. Structural (crate::gates::POLICY_REPOSITORY_NOT_READY_GATE): a repository that never recovers never self-heals this condition.- REPOSITORY_
NOT_ READY_ REASON reasonforREPOSITORIES_READY_CONDITION=False— the same string theSnapshotreconciler stamps on a child parked behind a not-Ready repository (one string, both surfaces; the controller re-exports it).- REPOSITORY_
READ_ ONLY_ REASON reason/Event reason when a backup or maintenance is refused on aReadOnlyrepository (ADR-0005 §11).- REPOSITORY_
SLOT_ AVAILABLE_ CONDITION - Condition recording whether this run holds a slot in its repository’s
mover-Job pool (
spec.concurrency.maxConcurrentJobs).FalsewithWAITING_FOR_SLOT_REASONmeans the run is parked atphase: Pendingbecause the pool is full;TruewithSLOT_ACQUIRED_REASONmeans it was admitted and its Job launched. - REPOSITORY_
UID_ LABEL - Label keying a discovered
Snapshotto the owning Repository UID (dedup). - REPOSITORY_
WRITABLE_ CONDITION Snapshotcondition recording whether its repository accepts writes (§11). SetFalse(withREPOSITORY_READ_ONLY_REASON) when a backup is refused because the repository ismode: ReadOnly.- REPO_
POOL_ LABEL - Pool-membership label stamped on every mover
Jobthat counts toward its repository’sspec.concurrency.maxConcurrentJobs: backup, restore, and the SOURCE side of aRepositoryReplication/SnapshotReplication. One selector therefore counts a repository’s whole in-flight pool. - RESTORE_
REFERENT_ AVAILABLE_ CONDITION Restorecondition recording whether the object this restore’s repository is DERIVED from exists. SetFalse(withRESTORE_REFERENT_MISSING_REASON) when the readiness gate cannot even look up the repository because a referent is absent: the explicitspec.repositoryobject itself, or thesource.fromPolicySnapshotPolicythe repository ref is read from (issue #393).- RESTORE_
REFERENT_ FOUND_ REASON reasonforRESTORE_REFERENT_AVAILABLE_CONDITION=True: the referent appeared on a later pass (the clear-side counterpart ofRESTORE_REFERENT_MISSING_REASON; controller-internal remediation copy, like theSnapshotgate’sSourcePvcFound). Written ONLY when the condition already exists — the healthy wire never grows the condition.- RESTORE_
REFERENT_ MISSING_ REASON reason/Event reason forRESTORE_REFERENT_AVAILABLE_CONDITION=False. Distinct fromREPOSITORY_NOT_READY_REASONon purpose: that one means “the repository object exists and its backend is unreachable”, which would be a lie for aSnapshotPolicythat was never applied.- RUN_
MODE_ ANNOTATION - Companion annotation selecting the run kind:
quick(default) orfull(seekopiur_api::maintenance::ManualRunMode). - RUN_
REQUESTED_ ANNOTATION - Annotation requesting an out-of-band
Maintenancerun NOW (Flux-style reconcile trigger). Value: an RFC3339 timestamp; a NEW timestamp requests a new run (re-applying the same value is a no-op once handled). Usable from barekubectl annotateorkubectl kopiur maintenance run. - SCHEDULE_
FANOUT_ CAPPED_ CONDITION SnapshotSchedulecondition recording whether a fired slot minted its full members × repositories fan-out.True(withFANOUT_TOO_LARGE_REASON) = the cross-product exceeded the fan-out cap and the slot was SKIPPED — and it will keep skipping every slot until the selector is narrowed or the repository list shrunk, which is squarely “needs a human”. Structural (crate::gates::SCHEDULE_FANOUT_CAPPED_GATE, promoted here by the #368 M10 gates/doctor checklist from a controller-internal condition).- SCHEDULE_
LABEL - Label naming the
SnapshotSchedulethat fired a scheduledSnapshot(selector for a schedule’s own children, distinct fromCONFIG_LABELunderpolicySelectorfan-out). - SCHEDULE_
REPLACEMENT_ HELD_ CONDITION SnapshotSchedulecondition recording thatconcurrencyPolicy: Replaceis holding a due slot because the run it would replace is itself parked behind its repository’s mover-Job concurrency cap (REPOSITORY_SLOT_AVAILABLE_CONDITION=False). Cancelling a queued run frees no capacity and the replacement would re-queue behind it, soReplacedegrades toForbid-like waiting until the pool drains.- SCHEDULE_
RUNNABLE_ CONDITION SnapshotSchedulecondition recording whether the schedule is able to fire its next slot. SetFalse(withBLOCKED_ON_UNREADABLE_RUN_REASON) when the concurrency gate is held by aSnapshotwhosestatus.phasethis build cannot interpret.- SEEDED_
CONDITION Repository/ClusterRepositorycondition reporting the state ofspec.seed— initializing a brand-new repository from an existing replica (issue #380).- SEEDED_
REASON reasonforSEEDED_CONDITION=Truewhen this repository’s content was actually copied in fromspec.seed.from.- SEEDING_
REASON reasonforSEEDED_CONDITION=Falsewhile the seeding bootstrap Job is in flight. Bounded byspec.seed.failurePolicy(default 24h), but a seed legitimately runs for hours, so anything reporting on a repository must be able to say “seeding” rather than “stuck”.- SEED_
FAILURE_ REASONS - Every
Seeded=Falsereason that means “the last seed attempt FAILED”, as opposed to the park/progress reasons (WAITING_FOR_SEED_SOURCE_REASON,SEEDING_REASON). - SEED_
INCOMPLETE_ REASON reasonforSEEDED_CONDITION=Falsewhen a migrate-mode seed’s post-verify found snapshots missing at the destination (kopia snapshot migrateexits 0 on a per-source failure, so the destination listing is the only honest success signal). The next attempt resumes the copy.- SEED_
LEFT_ EMPTY_ REASON reasonforSEEDED_CONDITION=Falsewhen a seed was armed and the repository ended the bootstrap holding ZERO snapshots — an attempt that initialized the backend and then died. The next attempt resumes the copy; nothing at the backend should be deleted.- SEED_
MOVER_ TOO_ OLD_ REASON reasonforSEEDED_CONDITION=Falsewhen the running mover image predatesspec.seed: it dropped the unknown field, fell into the create fallback, and initialized an EMPTY repository. Terminal — only an image upgrade changes it.- SEED_
SOURCE_ AUTH_ CONFLICT_ REASON reasonforSEEDED_CONDITION=Falsewhen a migrate-mode seed’s LOCAL backend and its resolved SOURCE repository disagree on workload identity: one seeding pod runs as exactly one ServiceAccount, so one of the two backends would authenticate as the wrong identity (or not at all).- SEED_
SOURCE_ EMPTY_ REASON reasonforSEEDED_CONDITION=Falsewhen the seed source IS a kopia repository but holds zero snapshots andspec.seed.allowEmptySourceisfalse. Retried automatically, so a mirror that fills up later seeds itself.- SEED_
SOURCE_ NOT_ FOUND_ REASON reasonforSEEDED_CONDITION=Falsewhen the seed SOURCE answered but holds no kopia repository (a mis-pointed bucket/prefix, or a mirror that was never written). Retried automatically.- SESSION_
BROWSE SESSION_LABELvalue for a read-only browse session.- SESSION_
LABEL - Label marking a mover
Jobas an interactive data-plane session pod (spawned bykubectl kopiur browse/ls/cat/download, not by the operator). Value:SESSION_BROWSE. Wire-visible: the CLI finds (and reuses) a warm session by this selector, andsession enddeletes by it. - SESSION_
REPO_ LABEL - Label keying a session
Jobto the repository it holds open, as<kind>-<name>(e.g.Repository-nas). One warm session per repository: the CLI selects on this so two snapshots in the same repository share a pod. - SKIP_
SNAPSHOT_ CLEANUP_ ANNOTATION - Repo-offline escape hatch: when present, the finalizer is removed without
contacting the repository, the snapshot is recorded orphaned, and a
SnapshotOrphanedevent is emitted (ADR §4.5). - SLOT_
ACQUIRED_ REASON reasonforREPOSITORY_SLOT_AVAILABLE_CONDITION=True: this run holds a slot in the repository’s mover-Job pool (or the pool is uncapped).- SNAPSHOT_
CLEANUP_ FINALIZER - The finalizer every
Snapshotcarries so the operator can run snapshot cleanup before the CR is removed (ADR §4.5 / SKILL “Snapshot lifecycle = CR lifecycle”). - SNAPSHOT_
ID_ LABEL - Label keying a discovered
Snapshotto its kopia snapshot id (dedup, §2.1). - SNAPSHOT_
REPLICATION_ LABEL - Label naming the
SnapshotReplicationCR that minted anorigin: replicateddest-side copySnapshot— the selector a replication run (and its pruning pass) uses to find its own copies. Stamped at CREATE, alongsideORIGIN_LABEL/SNAPSHOT_ID_LABEL/REPOSITORY_UID_LABEL. Lives inkopiur-apibecause the CLI and user automation must agree on it byte-for-byte. (Nothing stamps it yet — reserved by the shared-foundations milestone of #368.) - SOURCE_
PVC_ AVAILABLE_ CONDITION Snapshotcondition recording whether the backup’s DIRECT source PVC (spec.sources[].pvc) exists. SetFalse(withSOURCE_PVC_MISSING_REASON) when the PVC named by the recipe is absent at launch time: the backup parks (phase: Pending) and, after the controller’s deadline, fails terminally — a PVC that never reappears never self-heals, the silent-wedge shape of #359. Structural (crate::gates::SOURCE_PVC_MISSING_GATE); only the direct source PVC earns this gate — a vanished operator-staged claim is a restage race and stays a transient retry.- SOURCE_
PVC_ MISSING_ REASON reason/Event reason forSOURCE_PVC_AVAILABLE_CONDITION=False.- STALLED_
CONDITION - Set
Truewhen the resource is stuck and won’t progress without intervention (mapped from a terminalErrorClass::Terminalfailure). - WAITING_
FOR_ REPOSITORY_ SLOT_ REASON reason/Event reason forSCHEDULE_REPLACEMENT_HELD_CONDITION=True.- WAITING_
FOR_ SEED_ SOURCE_ REASON reasonforSEEDED_CONDITION=Falsewhen a migrate-mode seed is parked because its sourceRepository/ClusterRepositoryis notReady(or does not exist). The repository staysPendingand re-checks until the source comes up — an out-of-band change nothing in kopiur can make.- WAITING_
FOR_ SLOT_ REASON reasonforREPOSITORY_SLOT_AVAILABLE_CONDITION=False: the repository’s mover-Job pool is at its cap and this run is queued behind it.
Functions§
- effective_
failed_ jobs_ history_ limit - The effective failed-history limit:
failedJobsHistoryLimitwhen set, elseDEFAULT_FAILED_JOBS_HISTORY_LIMIT.Some(0)keeps no failed snapshots. - effective_
mass_ deletion_ threshold - The effective mass-deletion breaker threshold:
deletionProtection.thresholdwhen set, elseDEFAULT_MASS_DELETION_THRESHOLD.Some(0)disables the breaker. - effective_
max_ concurrent_ jobs - The effective per-repository mover-Job concurrency cap:
concurrency.maxConcurrentJobswhen set to a NON-ZERO value, elseNone(uncapped). An absentconcurrencyblock, an absentmaxConcurrentJobs, and an explicit0all mean the same thing — no limit — so all three collapse toNone.