

About
“We need to abolish names and places, and replace them with hashes.”
— Joe Armstrong, The Mess We’re In
TribleSpace is an embedded knowledge graph with built-in version control. It combines the queryability of a database with the distributed semantics of a content-addressed storage system — all in a single append-only file or S3-compatible endpoint.
Designed from first principles to overcome the shortcomings of prior triple-store technologies, TribleSpace focuses on simplicity, cryptographic identifiers, and clean CRDT semantics to provide a lightweight yet powerful toolkit for knowledge representation, data management, and data exchange.
Features
- Scales from memory to cloud: In-memory datasets, local pile files, and S3-compatible blob storage all use the same API.
- Distributed by default: Eventually consistent CRDT semantics (based on the CALM principle), compressed zero-copy archives, and built-in version control with branch/merge workflows.
- Predictable performance: An optimizer-free query engine using novel algorithms and data structures removes the need for manual query-tuning and delivers single-digit microsecond latency.
- Datasets as values: Cheap copy-on-write (COW) semantics and fast set operations let you treat entire datasets as ordinary values — diff, merge, and compose them freely.
- Compile-time typed queries: Automatic type inference, type-checking, and auto-completion make writing queries a breeze. Queries can span multiple datasets and native Rust data structures.
- Serverless: No background process needed. A single pile file is completely self-sufficient for local use; add an S3-compatible service when you need distribution.
Getting Started
Add the crate to your project:
cargo add triblespace
Once the crate is installed, you can experiment immediately with the
quick-start program below. It showcases the attribute macros, workspace
staging, queries, and pushing commits to a repository.
use ed25519_dalek::SigningKey;
use rand::rngs::OsRng;
use triblespace::prelude::*;
use triblespace::prelude::blobschemas::LongString;
use triblespace::core::repo::{memoryrepo::MemoryRepo, Repository};
mod literature {
use triblespace::prelude::*;
use triblespace::prelude::blobschemas::LongString;
use triblespace::prelude::valueschemas::{Blake3, GenId, Handle, R256, ShortString};
attributes! {
"A74AA63539354CDA47F387A4C3A8D54C" as pub title: ShortString;
"6A03BAF6CFB822F04DA164ADAAEB53F6" as pub quote: Handle<Blake3, LongString>;
"8F180883F9FD5F787E9E0AF0DF5866B9" as pub author: GenId;
"0DBB530B37B966D137C50B943700EDB2" as pub firstname: ShortString;
"6BAA463FD4EAF45F6A103DB9433E4545" as pub lastname: ShortString;
"FCCE870BECA333D059D5CD68C43B98F0" as pub page_count: R256;
"D2D1B857AC92CEAA45C0737147CA417E" as pub alias: ShortString;
pub prototype_note: Handle<Blake3, LongString>;
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let storage = MemoryRepo::default();
let mut repo = Repository::new(storage, SigningKey::generate(&mut OsRng), TribleSet::new())?;
let branch_id = repo
.create_branch("main", None)
.expect("create branch");
let mut ws = repo.pull(*branch_id).expect("pull workspace");
let author_id = ufoid();
let mut library = TribleSet::new();
library += entity! { &author_id @
literature::firstname: "Frank",
literature::lastname: "Herbert",
};
library += entity! { &author_id @
literature::title: "Dune",
literature::author: &author_id,
literature::quote: ws.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."
),
literature::quote: ws.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."
),
};
ws.commit(library, "import dune");
let catalog = ws.checkout(..)?;
let title = "Dune";
for (f, l, quote) in find!(
(first: String, last: String, quote),
pattern!(&catalog, [
{ _?author @
literature::firstname: ?first,
literature::lastname: ?last
},
{
literature::title: title,
literature::author: _?author,
literature::quote: ?quote
}
])
) {
let quote: View<str> = ws.get(quote)?;
let quote = quote.as_ref();
println!("'{quote}'
- from {title} by {f} {l}.");
}
repo.push(&mut ws).expect("publish initial library");
ws.commit(
entity! { &author_id @ literature::firstname: "Francis" },
"use pen name",
);
let mut collaborator = repo
.pull(*branch_id)
.expect("pull collaborator workspace");
collaborator.commit(
entity! { &author_id @ literature::firstname: "Franklin" },
"record legal first name",
);
repo.push(&mut collaborator)
.expect("publish collaborator history");
if let Some(mut conflict_ws) = repo
.try_push(&mut ws)
.expect("attempt manual conflict resolution")
{
let conflict_catalog = conflict_ws.checkout(..)?;
for first in find!(
first: String,
pattern!(&conflict_catalog, [{
literature::author: &author_id,
literature::firstname: ?first
}])
) {
println!("Collaborator kept the name '{first}'.");
}
ws.merge(&mut conflict_ws)
.expect("merge conflicting history");
ws.commit(
entity! { &author_id @ literature::alias: "Francis" },
"keep pen-name as an alias",
);
repo.push(&mut ws)
.expect("publish merged aliases");
}
Ok(())
}
The Getting Started
chapter of the book breaks this example down line by line, covers project
scaffolding, and introduces more background on how repositories, workspaces,
and queries interact.
Learn More
The Tribles Book is the best place to go deeper:
- Introduction
- Getting Started
- Architecture
- Query Language
- Incremental Queries
- Schemas
- Repository Workflows
- Commit Selectors
- Philosophy
- Identifiers
- Trible Structure
- Pile Format
To build the book locally: cargo install mdbook && ./scripts/build_book.sh
For development setup, see Contributing.
Community
Questions or ideas? Join the Discord.
License
Licensed under either of
at your option.