Skip to main content

Durability

Struct Durability 

Source
pub struct Durability {
    pub tier: DurabilityTier,
    pub engine: Option<DurabilityEngine>,
    pub store: Option<String>,
    pub subjects: Vec<String>,
    pub rpo_seconds: Option<u32>,
    pub state_mb: Option<u32>,
}
Expand description

A workload’s durability declaration — WorkloadSpec::durability.

Every field but tier defaults, because which of them are required depends on the tier, and that is a rule serde cannot express. Self::check is where it is enforced; read the declaration through WorkloadSpec::durability, which applies it.

@yah:ticket(R960-F6, “[[db]] on WorkloadSpec (name, subject, workbench = none|snapshot|vend) + catalog derives fleet:: from the camp’s own load_workloads”) @yah:status(review) @yah:at(2026-10-08T00:39:23Z) @yah:assignee(agent:bundle-anthropic-miravel) @yah:phase(P2) @yah:parent(R960) @yah:next(“Add db: Vec<WorkloadDb> beside durability/expose.mesh (W358 decision 7). Spec validation refuses a snapshot row whose subject is not in durability.subjects, and snapshot on a spec with no [durability]. vend validation (sql mesh port + verify-key mount) belongs to R960-F8, which also edits this file.”) @yah:next(“Catalog derivation: for each camp workload TOML read by load_workloads (config.rs:3151), each [[db]] with workbench != none yields a Connection constructed (not parsed) with id fleet:: - the same struct R960-F1 parses. Listing makes no fleet call.”) @yah:next(“Regen both generated artifacts by hand: cargo run -p xtask -- emit-schemas and cargo run --manifest-path oss/yah-base/crates/workload-spec/Cargo.toml --bin export-ts (workload-spec-drift-guard).”) @yah:gotcha(“Rolling fleet: nodes on the old binary will see db on specs. Carrying structs don’t deny unknown keys (R896-T4 note on WorkloadSpec), so it should be ignored - but check kamaji-proto’s digest + tests/tolerant_field_policy.rs before assuming a new field leaves existing spec digests (and thus deploy no-ops) unchanged. Unverified.”) @yah:gotcha(“R959 (service -> workload lowering) must round-trip this block; it is design-phase, so no edge - R959 already names [[db]] in its next.”) @arch:see(.yah/docs/working/W358-data-connections-remote-dbs-through-the-vault.md) @yah:depends_on(R960-F1) @yah:files(oss/yah-base/crates/workload-spec/src/lib.rs) @yah:files(packages/yah/workload-spec/index.ts) @yah:files(oss/yubaba/crates/cloud/src/config.rs) @yah:handoff(“Landed (uncommitted, git policy defer): WorkloadSpec.db: Vec<WorkloadDb{name,subject,workbench: WorkbenchKind none|snapshot|vend (default none)}> in workload-spec lib.rs; WorkloadSpec::db_rows() enforces non-blank/unique names, snapshot needs [durability], snapshot subject must be in durability.subjects (DbDeclError); validate::shape calls it (FieldPath::Db). vend validation left to R960-F8. Catalog: Connection::derive_fleet(camp_root) + fleet_from_specs in cloud config.rs read load_workloads over .yah/infra/workloads, construct fleet:: (name = ‘:’, group fleet, Snapshot->public/auth none, vend->Service/mesh/camp-token, policy deny/deny); desktop db_catalog_for_camp extends its connection list with it. All WorkloadSpec literals in oss/yah-base, kamaji, yubaba, roadcase, app/desktop, crates/hub gained db: Vec::new(). Regenerated schemas + packages/yah/workload-spec/index.ts; both drift scripts ok.”) @yah:gotcha(“Digest finding: kamaji spec_digest = SHA-256 over canonical JSON of the Workload, so an always-serialized empty db: [] would change every spec digest (redeploy-everything, and a per-sweep redeploy loop against not-yet-rolled kamajis whose digest is of a struct without db). skip_serializing_if is NOT usable: WorkloadSpec rides positional postcard (tests/round_trip.rs 3 tests broke when I tried it). Fix: db is always serialized; kamaji-proto digest.rs spec_digest drops an empty db array before hashing, so empty db digests byte-identical to pre-field (test an_empty_db_digests_as_if_the_field_did_not_exist). Non-empty db changes the digest; an un-rolled kamaji (ignores db) will mismatch for specs that declare db until rolled - roll kamaji before adding [[db]] to a live spec. tolerant_field_policy.rs unchanged and green (db has serde default, so absence does not break decode).”) @yah:gotcha(“Unrelated pre-existing red seen: oss/yubaba crates/yubaba/tests/raft_appliance_ownership.rs:195/208 E0061 (5 args expected, 4 given) - a peer’s in-flight change, not touched.”) @yah:assumes(“Derived snapshot connection uses auth.kind=none (validate() permits it for snapshot sources); the R2 pair is resolved by the snapshot opener (R960-F7) from durability.store, not named on the Connection. vend derives camp-token auth, channel mesh, source Service{workload, db}.”) @yah:assumes(“Derivation failure (any workload TOML failing shape validation) skips all fleet entries with a tracing warning in the desktop catalog, matching how a bad connections dir is handled.”) @yah:verify(“workload-spec cargo test: 211 lib + 108 integration, 0 failed (before my change the ts_drift test was the only red after adding the field; regenerated). yah-cloud –lib: 1311 passed / 0 failed / 6 ignored incl. fleet_derivation_* x2. kamaji-proto: 44+4+6 passed incl. an_empty_db_digests_as_if_the_field_did_not_exist; tolerant_field_policy green. cargo check -p yah -p desktop EXIT=0. scripts/check-schema-drift.sh and check-workload-spec-ts.sh ok. cargo check –workspace –all-targets green for oss/yah-base and oss/kamaji; oss/yubaba red only at tests/raft_appliance_ownership.rs (not mine). Skew advisory: workload-spec lib.rs was modified mid-run on the last build (peer, likely R960-F8 sharing the file) - re-verify before trusting. Root workspace –all-targets, roadcase, and the desktop catalog test for fleet entries were not run/added.”) @yah:handoff(“Leader (Fable session:62240105) re-verified 2026-10-07 after courier Miravel session:ad659ea1 returned: yah-workload-spec 319 passed / 0 failed (lib + integration), yah-cloud –lib fleet 6/0, kamaji-proto 54/0 incl. the empty-db digest test, check-schema-drift.sh and check-workload-spec-ts.sh both ok. The digest decision (always serialize db; kamaji-proto drops an empty db before hashing; roll kamaji before any live spec declares [[db]]) is accepted and carried to R960-T12’s roll sequencing. The oss/yubaba raft_appliance_ownership.rs E0061 is a peer’s in-flight change, not this ticket’s.”) @yah:verify(“Leader re-run: cargo test –manifest-path oss/yah-base/crates/workload-spec/Cargo.toml -> 319 passed, 0 failed; cd oss/yubaba && cargo test -p yah-cloud –lib fleet -> 6 passed; cd oss/kamaji && cargo test -p kamaji-proto -> 54 passed, 0 failed; scripts/check-schema-drift.sh EXIT=0; scripts/check-workload-spec-ts.sh EXIT=0.”) @yah:handoff(“Validation answer (courier Miravel): yah cloud validate (handle_validate, app/yah/cli/src/cloud.rs:14214) calls CloudConfig::load -> load_workloads (oss/yubaba/crates/cloud/src/config.rs:3195) -> workload_spec::validate::shape, which ends in spec.db_rows() (oss/yah-base/crates/workload-spec/src/validate.rs:654). So no gap: new test cloud::validate_db_rows_tests proves a [[db]] subject outside durability.subjects fails the verb (and a covered one passes). No wiring needed.”)

Fields§

§tier: DurabilityTier§engine: Option<DurabilityEngine>

Which engine’s tier vocabulary this is. Some exactly when DurabilityTier::ships_bytes — enforced by Self::check (R850-F1).

§store: Option<String>

Object-store URL the copy lives at. Some (and non-blank) exactly when DurabilityTier::ships_bytes — enforced by Self::check.

§subjects: Vec<String>

Volume-relative paths of the database files this tier covers, in declaration order. Non-empty exactly when DurabilityTier::ships_bytes — enforced by Self::check (R850-F1).

Volume-relative, never absolute: the same string is joined onto the container’s mount target when read as documentation and onto /var/lib/yah/kamaji/volumes/<name> when a hydrate writes it. Each is also the object-store key suffix under Self::store, so the layout an operator sees in the bucket mirrors the layout on the volume.

§rpo_seconds: Option<u32>

Declared recovery-point objective in seconds. DurabilityTier::Stream only; None there means turso_backup::stream::DEFAULT_RPO_TARGET.

§state_mb: Option<u32>

Expected steady-state size of this workload’s state, in MiB — the input a cold-start-from-object-store estimate needs and cannot get anywhere else. The microVM scratch floor (WorkloadSpec::scratch_floor_mb) is not it: that sizes a job’s ephemeral workspace, and a named volume is neither ephemeral nor a workspace.

Declared, never measured. Any recovery-time figure derived from it inherits that, and must say so at the point it is printed.

Implementations§

Source§

impl Durability

Source

pub fn check(&self) -> Result<(), DurabilityDeclError>

The rules that span fields, which serde cannot enforce. Checked in the order a human would fix them: where the copy goes, when, what engine, which files.

Trait Implementations§

Source§

impl Clone for Durability

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Durability

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for Durability

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Eq for Durability

Source§

impl PartialEq for Durability

Source§

fn eq(&self, other: &Self) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl Serialize for Durability

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for Durability

Source§

impl TS for Durability

Source§

type WithoutGenerics = Durability

If this type does not have generic parameters, then WithoutGenerics should just be Self. If the type does have generic parameters, then all generic parameters must be replaced with a dummy type, e.g ts_rs::Dummy or ().
The only requirement for these dummy types is that EXPORT_TO must be None. Read more
Source§

type OptionInnerType = Durability

If the implementing type is std::option::Option<T>, then this associated type is set to T. All other implementations of TS should set this type to Self instead.
Source§

fn ident(cfg: &Config) -> String

Identifier of this type, excluding generic parameters.
Source§

fn docs() -> Option<String>

JSDoc comment to describe this type in TypeScript - when TS is derived, docs are automatically read from your doc comments or #[doc = ".."] attributes
Source§

fn name(cfg: &Config) -> String

Name of this type in TypeScript, including generic parameters
Source§

fn decl_concrete(cfg: &Config) -> String

Declaration of this type using the supplied generic arguments. The resulting TypeScript definition will not be generic. For that, see TS::decl(). If this type is not generic, then this function is equivalent to TS::decl().
Source§

fn decl(cfg: &Config) -> String

Declaration of this type, e.g. type User = { user_id: number, ... }. This function will panic if the type has no declaration. Read more
Source§

fn inline(cfg: &Config) -> String

Formats this types definition in TypeScript, e.g { user_id: number }. This function will panic if the type cannot be inlined.
Source§

fn inline_flattened(cfg: &Config) -> String

Flatten a type declaration. This function will panic if the type cannot be flattened.
Source§

fn visit_generics(v: &mut impl TypeVisitor)
where Self: 'static,

Iterates over all type parameters of this type.
Source§

fn output_path() -> Option<PathBuf>

Returns the output path to where T should be exported, relative to the output directory. The returned path does not include any base directory. Read more
Source§

fn visit_dependencies(v: &mut impl TypeVisitor)
where Self: 'static,

Iterates over all dependency of this type.
Source§

fn dependencies(cfg: &Config) -> Vec<Dependency>
where Self: 'static,

Resolves all dependencies of this type recursively.
Source§

fn export(cfg: &Config) -> Result<(), ExportError>
where Self: 'static,

Manually export this type to the filesystem. To export this type together with all of its dependencies, use TS::export_all. Read more
Source§

fn export_all(cfg: &Config) -> Result<(), ExportError>
where Self: 'static,

Manually export this type to the filesystem, together with all of its dependencies. To export only this type, without its dependencies, use TS::export. Read more
Source§

fn export_to_string(cfg: &Config) -> Result<String, ExportError>
where Self: 'static,

Manually generate bindings for this type, returning a String. This function does not format the output, even if the format feature is enabled. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.