Expand description
§kopiur-api
Strongly-typed CRD definitions and shared, controller-free logic for Kopiur, the Kopia-native Kubernetes backup operator (ADR-0003).
§Role in the workspace
This crate holds the 7 CRD types in API group kopiur.home-operations.com,
version v1alpha1 — Repository (ns), ClusterRepository (cluster),
SnapshotPolicy, Snapshot, SnapshotSchedule, Restore, and
Maintenance — together with the shared pure logic every consumer needs:
validation (validate), identity resolution (resolve_identity), schedule
jitter (jitter), and GFS retention (select_kept).
It deliberately has no controller-runtime dependencies — no kube::Client,
no tokio. Downstream tools (a custom backup-triggering controller, a CI linter
for SnapshotPolicy manifests, a dashboard) can depend on the API types and shared
logic alone, without pulling in the async runtime or the cluster client
(ADR §5.1). The webhook and the controller both import the same validate/
identity/retention functions, so validation and resolution behave identically
across call sites (“one validator, two callers”).
§The load-bearing idea: type-safety end-to-end (ADR §5.5)
Every discriminated union in the CRD surface is a Rust enum:
Backend, AllowedNamespaces, DeletionPolicy,
RestoreSource, Hook, and friends.
A deserialized value is always exactly one variant — an invalid “two backends
at once” or “no backend” state is unrepresentable — and reconcilers match
exhaustively. A new variant added later cannot compile until every handler
accounts for it. For backup software, where a silently-unhandled case can lose
user data, this eliminates the highest-severity class of “controller silently
dropped data” bugs. This is the whole reason Kopiur is Rust and not Go; preserve
this property in every change (prefer an enum + exhaustive match over
if let / _ => catch-alls).
§Key types
| Type | Purpose |
|---|---|
Repository / ClusterRepository | The kopia repository as a first-class resource (namespaced / cluster-scoped). |
Backend | The storage backend union (s3, azure, gcs, b2, filesystem, sftp, webDav, rclone). |
SnapshotPolicy | The backup recipe (sources, retention, hooks). |
Snapshot | A single backup invocation, owning its snapshot via finalizer. |
SnapshotSchedule | The schedule that emits Snapshots on a cron. |
Restore | A restore request (RestoreSource / RestoreTarget). |
Maintenance | kopia maintenance as a first-class, default-managed concern. |
DeletionPolicy | Delete / Retain / Orphan — ties snapshot lifecycle to the CR. |
Shared pure logic (no controller deps):
| Function | Purpose |
|---|---|
resolve_identity | Render the kopia username@hostname:path identity, pinned to status at admission. |
select_kept | GFS retention: decide which backups to keep. |
jitter_offset / substitute_h | Deterministic H/jitter from (scheduleUID, slot). |
§Conventions
Before editing this crate, read docs/dev/api-conventions.md. The load-bearing
rules:
- Discriminated unions are externally-tagged enums (
backend: { s3: {...} }), not#[serde(tag = "...")]— internally-tagged enums break Kubernetes structural-schema generation. - No
Eqon structs that embedk8s-openapitypes (LabelSelector,ResourceRequirements,SecurityContext, …) — they arePartialEqonly. - Sub-objects, not leaf fields, for every credential/policy/identity/schedule surface, so future fields slot in without API breakage.
§Usage
Construct or deserialize a Backend the way the API server does (JSON value →
typed) and match it exhaustively:
use kopiur_api::Backend;
// External tagging: the variant key selects exactly one backend.
let backend: Backend = serde_json::from_value(serde_json::json!({
"s3": { "bucket": "my-backups", "region": "us-east-1" }
}))
.unwrap();
// A deserialized Backend is always exactly one variant -> exhaustive match.
let summary = match &backend {
Backend::S3(s3) => format!("s3://{}", s3.bucket),
Backend::Azure(_) => "azure".into(),
Backend::Gcs(_) => "gcs".into(),
Backend::B2(_) => "b2".into(),
Backend::Filesystem(_) => "filesystem".into(),
Backend::Sftp(_) => "sftp".into(),
Backend::WebDav(_) => "webdav".into(),
Backend::Rclone(_) => "rclone".into(),
Backend::Gdrive(_) => "gdrive".into(),
};
assert_eq!(summary, "s3://my-backups");
// The stable discriminant is independent of the camelCase wire key.
assert_eq!(backend.kind_str(), "S3");Note: deserialize via
serde_json(the API-server path), neverserde_yamldirectly into a typed value — serde_yaml 0.9 mis-encodes externally-tagged enums. For YAML tests, go YAML →serde_json::Value→ typed.
§See also
- ADR-0003 — the canonical source of truth for the CRD surface, UX, and design.
docs/dev/api-conventions.md— how to encode the ADR’s fields in Rust.
Re-exports§
pub use backend::Backend;pub use backend::NfsVolume;pub use backend::PvcVolume;pub use backend::RepoVolume;pub use cluster_repository::AllowedNamespaces;pub use cluster_repository::ClusterRepoCredentialProjection;pub use cluster_repository::ClusterRepository;pub use cluster_repository::ClusterRepositorySpec;pub use cluster_repository::ClusterRepositoryStatus;pub use common::CacheDefaults;pub use common::CacheVolumeMode;pub use common::CronSpec;pub use common::DeletionPolicy;pub use common::IdentityDefaults;pub use common::InheritSecurityContextFrom;pub use common::MoverDefaults;pub use common::NamespaceDeletePolicy;pub use common::ObjectRef;pub use common::PhaseLabel;pub use common::PodSelector;pub use common::PolicyRef;pub use common::PvcConsumerInherit;pub use common::ResolvedMover;pub use common::SourceColocation;pub use common::SourceColocationMode;pub use common::effective_run_as_group;pub use common::effective_run_as_user;pub use common::hardened_security_context;pub use common::merge_context_pair;pub use common::merge_pod_security_context;pub use common::merge_resources;pub use common::merge_security_context;pub use common::resolve_mover;pub use maintenance::LeaseAction;pub use maintenance::Maintenance;pub use maintenance::MaintenanceSchedule;pub use maintenance::MaintenanceSpec;pub use maintenance::MaintenanceStatus;pub use maintenance::ManualRunMode;pub use maintenance::ManualRunPhase;pub use maintenance::ManualRunStatus;pub use maintenance::Ownership;pub use maintenance::RepositoryMaintenanceSpec;pub use maintenance::TakeoverPolicy;pub use maintenance::default_maintenance_schedule;pub use maintenance::kopia_lease_identity;pub use maintenance::kopia_owner_for_lease;pub use maintenance::lease_action;pub use maintenance::lease_held_by_other;pub use maintenance::managed_lease;pub use maintenance::parse_run_annotations;pub use repository::Repository;pub use repository::RepositoryPhase;pub use repository::RepositorySpec;pub use repository::RepositoryStatus;pub use repository_replication::RepositoryReplication;pub use repository_replication::RepositoryReplicationPhase;pub use repository_replication::RepositoryReplicationSpec;pub use repository_replication::RepositoryReplicationStatus;pub use restore::OnMissingSnapshot;pub use restore::PopulatorTarget;pub use restore::ResolutionOutcome;pub use restore::Restore;pub use restore::RestorePhase;pub use restore::RestoreSource;pub use restore::RestoreSpec;pub use restore::RestoreStatus;pub use restore::RestoreTarget;pub use server::ClusterServerSpec;pub use server::ServerAuth;pub use server::ServerService;pub use server::ServerSpec;pub use server::ServerStatus;pub use server::ServiceType;pub use snapshot::Origin;pub use snapshot::Snapshot;pub use snapshot::SnapshotPhase;pub use snapshot::SnapshotSpec;pub use snapshot::SnapshotStats;pub use snapshot::SnapshotStatus;pub use snapshot::SnapshotTiming;pub use snapshot::StagedSources;pub use snapshot_policy::CopyMethod;pub use snapshot_policy::DeepVerification;pub use snapshot_policy::GroupBy;pub use snapshot_policy::Hook;pub use snapshot_policy::SnapshotPolicy;pub use snapshot_policy::SnapshotPolicySpec;pub use snapshot_policy::SnapshotPolicyStatus;pub use snapshot_policy::SourcePathStrategy;pub use snapshot_policy::StagingSpec;pub use snapshot_policy::Verification;pub use snapshot_schedule::ConcurrencyPolicy;pub use snapshot_schedule::ScheduleSpec;pub use snapshot_schedule::SnapshotSchedule;pub use snapshot_schedule::SnapshotScheduleSpec;pub use snapshot_schedule::SnapshotScheduleStatus;pub use duration::parse_go_duration;pub use duration::render_go_duration;pub use duration::resolve_timeout;pub use error::ValidationError;pub use error::ValidationResult;pub use identity::HostClass;pub use identity::IdentityInputs;pub use identity::classify_hostname;pub use identity::identity_string;pub use identity::resolve_identity;pub use identity::validate_identity_expr;pub use jitter::offset as jitter_offset;pub use jitter::substitute_h;pub use preflight::PreflightCheck;pub use preflight::PreflightInputs;pub use preflight::PreflightSpec;pub use preflight::eval_preflight_expr;pub use preflight::validate_preflight_expr;pub use recorded::KOPIUR_META_SCHEMA_V1;pub use recorded::KOPIUR_META_TAG;pub use recorded::MetaTagDecode;pub use recorded::RecordedSnapshotMeta;pub use recorded::RecordedSrc;pub use recorded::decode_meta_tag;pub use recorded::encode_meta_tag;pub use retention::KeptSet;pub use retention::SnapshotLike;pub use retention::select_kept;pub use success_expr::RestoredStats;pub use success_expr::SuccessExprInputs;pub use success_expr::VerifyStats;pub use success_expr::eval_success_expr;pub use success_expr::validate_success_expr;
Modules§
- backend
- Storage backends for a kopia repository.
- cluster_
repository - The
ClusterRepositoryCRD — a cluster-scoped, shared kopia repository operated by a platform team. ADR-0001 §3.2, ADR-0003 §3.2. - common
- Shared sub-objects reused across multiple CRDs.
- consts
- 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)).
- creds
- Pure credential/volume metadata over the CRD types: which Secrets a
repository’s mover needs. Shared by the controller (envFrom projection,
referent watches) and external tooling (
kubectl kopiur doctor), so the “what credentials does this backend reference” answer cannot fork. - duration
- Go-style duration strings used across the CRDs (
30m,1h,90s). - error
- Typed validation errors shared by the admission webhook and the controller.
- identity
- Kopia identity resolution (ADR §4.2).
- invariants
- Security-context invariants for the resolved mover pod.
- jitter
- Deterministic schedule jitter and Jenkins-style
Hsubstitution (ADR §4.1). - maintenance
- The
MaintenanceCRD — scheduleskopia maintenance runquick + full and manages the ownership lease. At most one per repository. ADR-0001 §3.7. - preflight
- Backup preflight: user-declared CEL preconditions a
Snapshotmust satisfy before its mover Job is launched (the “stronger preflight” ofdocs/repository-health.md). - recorded
- Recorded snapshot metadata — the
kopiur-metakopia tag. - repository
- The
RepositoryCRD — a namespaced kopia repository. ADR-0003 §3.1. - repository_
replication - The
RepositoryReplicationCRD — mirror a repository’s blobs to a second backend on a schedule (ADR-0005 §13(d)). The one net-new CRD: it is the “2” in 3-2-1 backup, wrappingkopia repository sync-to. - restore
- The
RestoreCRD — a restore from a snapshot/identity to a PVC, or a passive populator source. ADR-0001 §3.6, ADR-0003 §4.6. - retention
- Grandfather-father-son (GFS) retention selection (ADR §4.4).
- schema
- Schema helper for embedding a large Kubernetes
core/v1sub-object. - secctx_
compat - Pure, controller-free reasoning about whether a mover’s resolved security context can read a backup source PVC, and the inverse — whether a future workload can read what a restore mover writes to a target PVC.
- server
- Optional kopia web-UI server surface for
Repository/ClusterRepository. - snapshot
- The
SnapshotCRD — a single kopia snapshot as a Kubernetes object. ADR-0001 §3.4, ADR-0003 §4.5. - snapshot_
policy - The
SnapshotPolicyCRD — the recipe. Idempotent; runs nothing on its own. ADR-0001 §3.3, ADR-0003 §4.8. - snapshot_
schedule - The
SnapshotScheduleCRD — when a backup runs. CreatesSnapshotCRs on a cron schedule in theSnapshotPolicy’s namespace. ADR-0001 §3.5, ADR-0003 §4.4. - success_
expr successExpr: a sandboxed CEL pass/fail predicate over a verification result (ADR-0005 §4/§15).- validate
- Cross-field validation the type system can’t express (ADR §2.2 principle 8).