Blobs
Blobs are immutable sequences of bytes used whenever data no longer fits into
the fixed 256‑bit value slot of a trible. Instead of treating these payloads as
untyped binary blobs, Tribles keeps track of their structure via BlobSchema.
Much like ValueSchema drives how values are serialized into a trible, a
BlobSchema defines how to encode and decode rich data into a byte sequence.
When to reach for blobs
Values and tribles capture compact facts – identifiers, timestamps, counters –
in a fixed width. Whenever information grows beyond that footprint, blobs carry
the payload while tribles continue to reference it. Common use cases include
documents, media assets, serialized entity archives, or even domain specific
binary formats. Because blobs are content addressed, the same payload stored
twice automatically deduplicates to the same handle. In the in-memory
implementation this falls straight out of the code: MemoryBlobStore::insert
keeps a BTreeMap keyed by the handle and simply reuses the existing entry
when the same digest shows up again.
Handles, schemas, and stores
Blobs live in a BlobStore. The store provides persistent storage and a
content hash, determined by the selected HashProtocol, that acts as a stable
handle. Handles can be embedded into tribles just like any other value so they
benefit from the existing querying machinery. A handle couples the blob's hash
with its BlobSchema so consumers always know how to deserialize the
referenced bytes.
Converting Rust types to blobs is infallible in practice, therefore the ToBlob
and TryFromBlob traits are the most common helpers. The TryToBlob and
FromBlob variants have been dropped to keep the API surface small without
losing ergonomics.
End‑to‑end example
The following example demonstrates creating blobs, archiving a TribleSet and
signing its contents:
#![allow(unused)] fn main() { use triblespace::prelude::*; use triblespace::examples::literature; use triblespace::repo; use valueschemas::{Handle, Blake3}; use blobschemas::{SimpleArchive, LongString}; use rand::rngs::OsRng; use ed25519_dalek::{Signature, Signer, SigningKey}; // Build a BlobStore and fill it with some data. let mut memory_store: MemoryBlobStore<Blake3> = MemoryBlobStore::new(); let book_author_id = fucid(); let quote_a: Value<Handle<Blake3, LongString>> = memory_store .put("Deep in the human unconscious is a pervasive need for a logical universe that makes sense. But the real universe is always one step beyond logic.") .unwrap(); let quote_b = memory_store .put("I must not fear. Fear is the mind-killer. Fear is the little-death that brings total obliteration. I will face my fear. I will permit it to pass over me and through me. And when it has gone past I will turn the inner eye to see its path. Where the fear has gone there will be nothing. Only I will remain.") .unwrap(); let set = entity!{ literature::title: "Dune", literature::author: &book_author_id, literature::quote: quote_a, literature::quote: quote_b }; // Serialize the TribleSet and store it as another blob. The resulting // handle points to the archived bytes and keeps track of its schema. let archived_set_handle: Value<Handle<Blake3, SimpleArchive>> = memory_store.put(&set).unwrap(); let mut csprng = OsRng; let commit_author_key: SigningKey = SigningKey::generate(&mut csprng); let signature: Signature = commit_author_key.sign( &memory_store .reader() .unwrap() .get::<Blob<SimpleArchive>, SimpleArchive>(archived_set_handle) .unwrap() .bytes, ); // Store the handle in another TribleSet so the archived content can be // referenced alongside metadata and cryptographic proofs. let _meta_set = entity!{ repo::content: archived_set_handle, repo::short_message: "Initial commit", repo::signed_by: commit_author_key.verifying_key(), repo::signature_r: signature, repo::signature_s: signature, }; }
Blobs complement tribles and values by handling large payloads while keeping the core data structures compact. Embedding handles into entities ties together structured metadata and heavyweight data without breaking immutability or introducing duplication. This division of labor lets tribles focus on querying relationships while BlobStores take care of storage concerns such as hashing, deduplication, and retrieval.