Expand description
The BlobStore: containers on a filesystem, keys and bookkeeping in
PostgreSQL.
The split is the design. Ciphertext is large, immutable, and content-named, which is what a filesystem is good at; the wrapped DEK, the pins, and the tombstone are small, mutable, and have to change atomically, which is what a database is good at. Putting the key in the row rather than the file is what makes rewrap and crypto-shred single writes (ADR 0003, and the container module’s own docs).
Nothing here takes a path from a caller. A committed blob lives at a path
derived from its validated 64-hex digest, and an upload lives at a path
derived from an id this store minted — is_upload_id re-checks that shape
on the way back in, because a BlobUploadId is a plain string newtype that
a caller can build out of anything.
Write ordering is chosen so every crash window leaves the SAFE remainder:
- commit writes the container, then the row. A crash between them leaves a file whose key exists nowhere — inert ciphertext, and a retried commit overwrites it.
- shred writes the tombstone and drops the key, then unlinks. A crash between them leaves an unreadable blob, never a readable one.
- sweep deletes the row, then unlinks, for the same reason.
Structs§
- PgBlob
Store - Content-addressed blobs: containers under a root directory, key material and
bookkeeping in
gwk_internal.
Constants§
- UPLOAD_
EXPIRY_ SECS - How long an uncommitted upload survives. Enforced on the way in as well as
by
PgBlobStore::expire_uploads, so an expired upload is dead even on a deployment where nothing has swept yet.
Functions§
- is_
upload_ id - Is this the shape this store mints? Lowercase hex of a fixed width — no
separator, no
., no.., so it cannot name anything but a leaf under the directory it is joined to.