The object store behind a trait — L2 step 4 (design review A13, with A6's conditional-publish contract baked into the signatures).
Only generation ASSEMBLY is backend-specific; everything above this trait (interval log, flush pipeline, eviction, hydration, epoch state machine) is backend-neutral. Backends:
memory— the in-process test double, implementing the FULL conditional semantics (412/409, stamps, CRC validation, MPU state, epoch CAS). Every backend-neutral tier test runs here.s3— MPU + UploadPartCopy + conditional PUT/Complete per A6/A8.- Azure Blob (second, per A13): Put Block List with committed-block reuse and NATIVE leases for the epoch. GCS later.
Contract points the signatures enforce:
- No unconditional publish exists. Every write carries either
IfMatch(update of a known generation) orIfNoneMatchAny(first generation of a new key). A caller that wants an unguarded overwrite has made a design error upstream (A6). - The multipart ETag is NOT a content hash and never appears in any integrity role; content identity is the full-object CRC-64/NVME, computed by the flusher from LOCAL truth and validated server-side at publish (a mismatch fails the publish, it does not warn).
- Assembly failure aborts the assembly (A9): no backend may leave a partial MPU behind on an error path it can reach.
Extracted from spdk-csi-driver/src/tier/store/ (2026-08-25) so
flint-lean can depend on the store WITHOUT the hub crate's build
(13 binaries, the aws/kube/nfs dep trees). The hub crate re-exports
this crate at its old path (crate::tier::store::*), so every
existing import keeps working. The s3 backend is feature-gated —
test loops that only need the memory double never build the AWS
SDK.