Expand description
The blob engine: coven’s single owner of a blob’s whole durability lifecycle.
coven syncs blobs referenced by database rows. It owns the cloud layout and
encryption; the host declares which rows carry blobs, their local plaintext
source, and encryption scope. An opaque home uses
{namespace}/opaque/{locator_hash}. A browsable home uses the consumer’s
BlobRef::cloud_path within
{namespace}/readable/{cloud_path}/.coven-versions/{locator_hash}.
Both layouts name immutable versions through locator::BlobLocator.
§The coven concept tree
A blob has two declared properties — Provenance (its Local story) and
CacheFill (its Remote story) — and one state, locality, flipped by the
transitions. The cache is a mechanism that serves Remote blobs; it is not a
kind of blob.
A blob the host declares with:
provenance — its LOCAL story: where the bytes live when Local, and the
Remote→Local path requirement
├─ user-provided the user's file at a path; coven references it.
│ Remote→Local writes the bytes back to a user file → NEEDS A PATH.
└─ host-provided bae hands coven the data; coven keeps it in its local store.
Remote→Local restores it to the local store → no path.
cache fill — its REMOTE story: how a device gets the bytes when the release is
Remote. A cache-mechanism setting; applies to ANY blob, regardless of
provenance, once it is Remote.
├─ CacheEager fetched into the cache on pull, with the SQL row (covers)
└─ CacheLazy fetched into the cache on first read (audio — big, fetch what you play)
and a current state:
locality
├─ Local bytes on-device — the user's path (user-provided) or coven's local store (host-provided)
└─ Remote bytes in the cloud; each device's local copy is a CACHE copy, filled
per `cache fill`, kept-or-evicted per `pin`
namespace (bucket) the blob's category — release_files · covers · artist_images
transitions
├─ Local → Remote upload the bytes; now cache-distributed to every device per cache fill
└─ Remote → Local bring the bytes back to a local file — path required iff user-provided
cache budget per-NAMESPACE size limit; each namespace evicts independently, so
evicting release_files (big) never touches covers (small reserved slice)
pin keep one specific Remote blob's cache copy from eviction (e.g. a
release the user pinned for offline)§The cache vs local files
The cache holds local copies of Remote blobs (filled per cache fill,
evicted per budget unless pinned). It is segmented by namespace: each
namespace has its own configurable cache budget and evicts independently, so
evicting release_files (big) never touches covers (a small reserved slice). A
CacheEager cover that falls out of its namespace budget shows a placeholder
until the next read re-fetches it — covers are not pinned. A Local blob is not
in the cache: a user-provided Local blob is the user’s file at its path (an
external ref); a host-provided Local blob is in coven’s local store, whose
paths and file operations are owned by coven_foundation::store_dir::StoreDir. The cache is the mechanism for remoteness — so
CacheEager/CacheLazy/pin/budget describe a blob only while it is Remote, never
while it is Local.
§The engine’s halves
This module is the engine; its halves move a blob through its lifecycle:
blob::cache— the device-local cache for Remote blobs: bytes on disk keyed by exact locator hash, with the folder a file lives in as the only retention truth (storage/pinned/protected,storage/cache/evictable). Reads — one-shot whole, which checks the plaintext against the row’s hash because it reads every byte anyway, or an opened stream whose ranges each cost their own bytes: a positioned read of a local file, or the sealed chunks covering the range fetched from the cloud object and opened — plus pin/unpin, clear, and budget eviction.coven_foundation::store_dir::StoreDir— coven’s own copy of a host-provided Local blob, instorage/local/<namespace>/<id>. Never evicted; the budget sweep never walks it.blob::upload— the cloud-write half: drain the durable upload queue, sealing each blob under its scope and writing it to the cloud with coalesced progress, so a local-only blob becomes uploaded. The sync cycle calls the drain each round before it pushes.blob::delete— the cloud-delete half: turn a queued deletion into a signed cloud tombstone, hold the blob for a convergence grace so a lagging peer isn’t stranded, then GC the blob once the grace has passed. The sync cycle drains tombstones and runs the GC each round after it pulls.
The types below (BlobRef, BlobScope, Provenance,
CacheFill, BlobTransitionObserver) are the vocabulary both halves and
the host speak. Which rows carry blobs is not a runtime callback but a per-table
declaration (crate::synced_schema::BlobDecl) coven resolves into a
the database’s BlobDecls each cycle to derive the blob set itself.
coven also owns the two locality transitions (blob::transition): make_remote
(Local → Remote: upload the bytes, then flip the gate) and make_local
(Remote → Local: bring each blob back to a local file, then retract). The
The upload drain advances the durable make-Remote intent after every exact
object lands. The Store publication activates the resulting gate change;
hosts observe both handoffs through the durable cloud-outbox query.
Modules§
Structs§
- BlobRef
- A blob a row references: its logical identity, encryption scope, and the two
declared properties (
provenance+fill). coven derives it from the row’s declared columns (crate::synced_schema::BlobDecl) via the database’sBlobDecls. Where its bytes live depends on its locality and provenance: a user-provided Local blob is the user’s file at its path; a host-provided Local blob is in coven’s local store (storage/local/<namespace>/<id>); a Remote blob’s device-local copy is a cache copy (storage/pinned/<namespace>/<ab>/<cd>/<locator-hash>orstorage/cache/<namespace>/<ab>/<cd>/<locator-hash>). The shard’sabandcdare the first two byte-pairs of the locator hash. - Content
Hasher - An incremental SHA-256 over a blob’s plaintext, so the streaming download path
verifies a blob’s content hash without holding the whole plaintext in memory:
feed each decrypted chunk to
update, callfinish, and compare the returned digest with the row’s hash before committing the bytes to the cache. The hex-encoded digest matchescontent_hashover the same bytes. - Deferred
Local Blob Disposition Error - Deferred
Local Blob Drop - RowBlob
Ref - One exact blob-bearing row version. A reference becomes stale when the live row stamp or any declared blob value changes.
- Transfer
Limits - How many blob transfers coven runs at once in each of its two transfer loops:
the upload drain and the pin/download loop. An
open-time blob-engine tunable the host sets on the builder,
carried on the
Databasealongside the other open-time blob config and read back by each loop, which holds&Database.
Enums§
- Blob
Opening Authority - Blob
Opening Authority Error - Blob
Replacement - Whether changeset updates may repoint a blob-bearing row at another blob id.
Orthogonal to
ProvenanceandCacheFill. Both variants uselocator::BlobLocatorto identify immutable cloud objects independently of the row’s replacement policy. - Blob
Scope - Which key encrypts a blob, as a host names it on a
BlobRef. - Cache
Fill - A blob’s Remote story: how a device gets the bytes once the blob is Remote.
A cache-mechanism setting — it describes a blob only while Remote — that applies
to ANY blob regardless of
Provenance. Orthogonal to provenance; a blob declares both. - Deferred
Local Blob Disposition - Provenance
- A blob’s Local story: where its bytes live while the blob is Local, and
whether bringing it back from Remote needs a destination path. Orthogonal to
CacheFill(the Remote story) — a blob declares both. - RowBlob
Authority - The authority state that determines where one row version’s blob lives.
A remote-audience blob remains
PendingRemotewhile its verified plaintext is local and no cloud object has been created;Remotecarries the exact package authority needed to open its committed object. - RowBlob
RefError
Constants§
- BLOB_
TOMBSTONE_ GRACE - The default convergence window a host gets if it configures none: how long a
deleted blob is kept after its tombstone is written, before a GC pass
reclaims it. The host overrides it on the coven builder; the writer’s
tombstone collection evaluates whatever grace it is handed against the
tombstone’s
deleted_at.
Traits§
- Blob
Transition Observer - Notified about coven’s blob transitions, for host-specific bookkeeping and UI: per-blob upload progress while a make_remote uploads, per-blob materialize progress while a make_local copies files back, and the synchronous make-local completion the host turns into its own UI event.
Functions§
- content_
hash - The content hash a blob-bearing row carries: the lowercase-hex SHA-256 of the blob’s plaintext bytes, computed at import and stored in the row’s blob columns alongside the declared size. The row is carried in a signed changeset (and in a signed snapshot), so this hash is signed by the row’s author — that is what makes it authoritative: on download coven hashes the decrypted plaintext and requires equality with the row’s hash, so the bytes are pinned by the author, not by the cloud key they happened to arrive under. A host computes this over a blob’s plaintext at import and writes it into the row’s declared hash column, the same way it writes the plaintext length into the size column.
- locator_
describes_ row - Whether the object supplies this row’s plaintext and encryption scope.
The readable path and audience may differ when this object is the source
for a rename or an audience move; retaining it as the destination upload
additionally requires
locator_is_this_rows_upload. - locator_
is_ this_ rows_ upload - Whether this row can retain its uploaded object for the destination audience. Content, encryption scope, audience, and a Browsable locator’s readable path must agree. A Store key rotation alone does not require uploading the same bytes again: the accepted locator retains its original sealing key.