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: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: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: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
impl Durability
Sourcepub fn check(&self) -> Result<(), DurabilityDeclError>
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
impl Clone for Durability
Source§impl Debug for Durability
impl Debug for Durability
Source§impl<'de> Deserialize<'de> for Durability
impl<'de> Deserialize<'de> for Durability
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
impl Eq for Durability
Source§impl PartialEq for Durability
impl PartialEq for Durability
Source§impl Serialize for Durability
impl Serialize for Durability
impl StructuralPartialEq for Durability
Source§impl TS for Durability
impl TS for Durability
Source§type WithoutGenerics = Durability
type WithoutGenerics = Durability
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 moreSource§type OptionInnerType = Durability
type OptionInnerType = Durability
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 docs() -> Option<String>
fn docs() -> Option<String>
TS is derived, docs are
automatically read from your doc comments or #[doc = ".."] attributesSource§fn decl_concrete(cfg: &Config) -> String
fn decl_concrete(cfg: &Config) -> String
TS::decl().
If this type is not generic, then this function is equivalent to TS::decl().Source§fn decl(cfg: &Config) -> String
fn decl(cfg: &Config) -> String
type User = { user_id: number, ... }.
This function will panic if the type has no declaration. Read moreSource§fn inline(cfg: &Config) -> String
fn inline(cfg: &Config) -> String
{ user_id: number }.
This function will panic if the type cannot be inlined.Source§fn inline_flattened(cfg: &Config) -> String
fn inline_flattened(cfg: &Config) -> String
Source§fn visit_generics(v: &mut impl TypeVisitor)where
Self: 'static,
fn visit_generics(v: &mut impl TypeVisitor)where
Self: 'static,
Source§fn output_path() -> Option<PathBuf>
fn output_path() -> Option<PathBuf>
T should be exported, relative to the output directory.
The returned path does not include any base directory. Read moreSource§fn visit_dependencies(v: &mut impl TypeVisitor)where
Self: 'static,
fn visit_dependencies(v: &mut impl TypeVisitor)where
Self: 'static,
Source§fn dependencies(cfg: &Config) -> Vec<Dependency>where
Self: 'static,
fn dependencies(cfg: &Config) -> Vec<Dependency>where
Self: 'static,
Source§fn export(cfg: &Config) -> Result<(), ExportError>where
Self: 'static,
fn export(cfg: &Config) -> Result<(), ExportError>where
Self: 'static,
TS::export_all. Read moreSource§fn export_all(cfg: &Config) -> Result<(), ExportError>where
Self: 'static,
fn export_all(cfg: &Config) -> Result<(), ExportError>where
Self: 'static,
TS::export. Read moreSource§fn export_to_string(cfg: &Config) -> Result<String, ExportError>where
Self: 'static,
fn export_to_string(cfg: &Config) -> Result<String, ExportError>where
Self: 'static,
Auto Trait Implementations§
impl Freeze for Durability
impl RefUnwindSafe for Durability
impl Send for Durability
impl Sync for Durability
impl Unpin for Durability
impl UnsafeUnpin for Durability
impl UnwindSafe for Durability
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.