pub struct DejaDB { /* private fields */ }Expand description
The embedded DejaDB store handle — one file per memory.
Implementations§
Source§impl DejaDB
impl DejaDB
Sourcepub fn derive_key_for(
path: &str,
passphrase: &str,
) -> Result<Zeroizing<[u8; 32]>>
pub fn derive_key_for( path: &str, passphrase: &str, ) -> Result<Zeroizing<[u8; 32]>>
Derive a 32-byte AES-256 key from a passphrase using Argon2id. The salt
and cost parameters live in a non-secret <path>.kdf sidecar created on
first use. The returned key zeroizes on drop.
Losing the passphrase destroys the key (crypto-erasure); losing the
.kdf sidecar means the passphrase can no longer re-derive the key, so
back it up alongside the database.
Sourcepub fn open_with_passphrase(path: &str, passphrase: &str) -> Result<Self>
pub fn open_with_passphrase(path: &str, passphrase: &str) -> Result<Self>
Open (or create) an encrypted memory using a passphrase-derived key
(Argon2id + AES-256-GCM at rest). Convenience over
DejaDB::derive_key_for + DejaDB::open_with.
Source§impl DejaDB
impl DejaDB
Sourcepub fn open(path: &str) -> Result<Self>
pub fn open(path: &str) -> Result<Self>
Open honoring the file’s own declarations (meta table) when
present. A fresh file is stamped with the defaults. This is the
file-truth path: settings like text_index travel with the file,
so the same memory behaves identically on any host.
Sourcepub fn open_with(path: &str, opts: DejaDbOptions) -> Result<Self>
pub fn open_with(path: &str, opts: DejaDbOptions) -> Result<Self>
Open with explicit options. Explicit options are deliberate: they
re-stamp the file’s declarations, and a change to an existing
declaration is recorded in open_warnings().
Sourcepub fn open_encrypted(path: &str, key: [u8; 32]) -> Result<Self>
pub fn open_encrypted(path: &str, key: [u8; 32]) -> Result<Self>
Open (or create) an encrypted memory: AES-256-GCM at rest with a
host-supplied 32-byte key (Turso page cipher). The key lives only in
the caller’s process — never written to the file — so a bare open()
of this path cannot read it, and destroying the key destroys the
memory (crypto-erasure). Default index/relation options otherwise.
Sourcepub fn set_embedder(&mut self, e: Box<dyn EmbedBackend>)
pub fn set_embedder(&mut self, e: Box<dyn EmbedBackend>)
Install an embedding backend; subsequent adds embed their text and the vector leg joins hybrid recall.
The first installed backend is recorded in the file’s meta table
as embedding provenance (model + dim). A later open that injects a
different-dim backend gets a reconciliation warning instead of
silently mixing vector spaces.
Sourcepub fn set_reranker(&mut self, r: Box<dyn RerankBackend>)
pub fn set_reranker(&mut self, r: Box<dyn RerankBackend>)
Install a cross-encoder reranker (Tier-2). Opt-in per query via
RecallTuning::rerank; with none installed, requesting rerank is a
no-op (fusion order stands). Host owns the model — no ML dep in-engine.
Sourcepub fn has_reranker(&self) -> bool
pub fn has_reranker(&self) -> bool
Whether a reranker backend is installed.
Sourcepub fn set_query_expander(&mut self, e: Box<dyn QueryExpander>)
pub fn set_query_expander(&mut self, e: Box<dyn QueryExpander>)
Install a custom query expander (Tier-1). When unset, requesting
RecallTuning::query_expansion falls back to the built-in English
EnglishExpander. Install your own for other languages/domains.
Sourcepub fn index_text_enabled(&self) -> bool
pub fn index_text_enabled(&self) -> bool
Whether the BM25 text index is populated on writes (file-declared, honored or re-stamped at open).
Sourcepub fn defer_text_index(&mut self) -> Result<bool>
pub fn defer_text_index(&mut self) -> Result<bool>
Drop the FTS index ahead of a bulk load. Turso’s experimental FTS
costs ~150ms of commit bookkeeping per write transaction while the
index exists — a tax bulk imports cannot amortize. With the index
dropped, the text column keeps populating at full write speed; call
Self::rebuild_text_index after the load to re-create the index
(Turso indexes all existing rows at CREATE INDEX time — milliseconds,
not per-row). Crash-safe: if the process dies in between, the next
open re-creates the index and backfills it.
Index-layer only — stored blobs are never touched. Returns false
when there was nothing to defer (text indexing off, or already
deferred).
Sourcepub fn rebuild_text_index(&mut self) -> Result<usize>
pub fn rebuild_text_index(&mut self) -> Result<usize>
(Re)build the FTS index. Backfills the text column for rows written
while text indexing was off — deriving the same projected_text
the inline write path uses — then re-creates the index, which indexes
every existing row. Pairs with Self::defer_text_index around bulk
loads, and turns a file that flipped --index-text true after the
fact into a fully searchable one. Index-layer only — stored blobs are
never touched; forgotten grains are gone from grains and cannot be
resurrected. Returns the number of rows whose text was backfilled.
Errors when the file declares text indexing off — reopen with
index_text: true (CLI --index-text true) first.
Sourcepub fn embedder_dim(&self) -> Option<usize>
pub fn embedder_dim(&self) -> Option<usize>
Dimension of the installed embedding backend, if any. None means
the vector recall leg is off for this store.
Sourcepub fn declared_embedding(&self) -> Option<(&str, usize)>
pub fn declared_embedding(&self) -> Option<(&str, usize)>
Embedding provenance declared by the file (model, dim), if any vectors were ever written.
Sourcepub fn open_warnings(&self) -> &[String]
pub fn open_warnings(&self) -> &[String]
Reconciliation warnings from open / set_embedder: file declarations vs what this session supplied. Empty when everything agrees.
Sourcepub fn add<G: Grain + 'static>(&mut self, grain: &G) -> Result<Hash>
pub fn add<G: Grain + 'static>(&mut self, grain: &G) -> Result<Hash>
Add one grain (full txn). Returns its content address.
Sourcepub fn add_batch(&mut self, grains: &[&dyn AddableDyn]) -> Result<Vec<Hash>>
pub fn add_batch(&mut self, grains: &[&dyn AddableDyn]) -> Result<Vec<Hash>>
Batched add — one txn for the whole slice (voice write-back path).
Sourcepub fn add_if_novel<G: Grain + 'static>(
&mut self,
grain: &G,
) -> Result<(Hash, bool)>
pub fn add_if_novel<G: Grain + 'static>( &mut self, grain: &G, ) -> Result<(Hash, bool)>
Value-level idempotent add. When the grain carries a full
(subject, relation, object) triple and the current head for
(ns, subject, relation) already holds this exact object, nothing is
written and the existing head’s hash is returned with false.
Otherwise it behaves like add and returns true.
This collapses a re-learned value, not merely a byte-identical
replay: unlike content addressing it ignores created_at and the rest
of the envelope, keying only on (ns, subject, relation, object)
against the current provisional head. Grains without a full triple
always insert. Paraphrased near-duplicates are a different object and
out of scope here — those need a host-side (embedding) novelty check.
Sourcepub fn grains_derived_from(
&mut self,
parent: &Hash,
) -> Result<Vec<DeserializedGrain>>
pub fn grains_derived_from( &mut self, parent: &Hash, ) -> Result<Vec<DeserializedGrain>>
Reverse provenance: every grain whose derived_from is exactly
parent, newest first. This is the credit-assignment / episode-unlearn
query — “which lessons were distilled from this observation?” or “what
did the agent learn from this bad session?”. Superseded versions are
included so the full derived lineage is visible; the caller can revise
or forget each hash. Provenance is not a hot path, so this scans
stored grains rather than maintaining a dedicated index.
Sourcepub fn recent(
&mut self,
ns: &str,
gtype: Option<GrainType>,
limit: usize,
) -> Result<Vec<DeserializedGrain>>
pub fn recent( &mut self, ns: &str, gtype: Option<GrainType>, limit: usize, ) -> Result<Vec<DeserializedGrain>>
Recent grains in a namespace, newest first, bounded by limit. With
gtype = None, every type is returned. This is the “reflect over recent
experience” read path — recent Events / Observations that have no
subject or free-text anchor to hang a structural or BM25 leg on.
Sourcepub fn get(&mut self, hash: &Hash) -> Result<DeserializedGrain>
pub fn get(&mut self, hash: &Hash) -> Result<DeserializedGrain>
Fetch a grain by content address.
Sourcepub fn recall(
&mut self,
ns: &str,
subject: &str,
relation: Option<&str>,
k: usize,
) -> Result<Vec<DeserializedGrain>>
pub fn recall( &mut self, ns: &str, subject: &str, relation: Option<&str>, k: usize, ) -> Result<Vec<DeserializedGrain>>
Structural recall: current grains about subject (optionally filtered
by relation), newest first, k-bounded. The voice hot path.
Sourcepub fn latest(
&mut self,
ns: &str,
subject: &str,
relation: &str,
) -> Result<Option<DeserializedGrain>>
pub fn latest( &mut self, ns: &str, subject: &str, relation: &str, ) -> Result<Option<DeserializedGrain>>
Current value head for (subject, relation) — the µs point read.
Sourcepub fn thread_tail(
&mut self,
ns: &str,
session: &str,
n: usize,
) -> Result<Vec<DeserializedGrain>>
pub fn thread_tail( &mut self, ns: &str, session: &str, n: usize, ) -> Result<Vec<DeserializedGrain>>
Last n events of a session, oldest→newest (transcript tail).
Sourcepub fn supersede<G: Grain + 'static>(
&mut self,
old: &Hash,
new_grain: &mut G,
) -> Result<Hash>
pub fn supersede<G: Grain + 'static>( &mut self, old: &Hash, new_grain: &mut G, ) -> Result<Hash>
Supersede old with new_grain (atomic, OMS L2 semantics).
Sets derived_from on the new grain; the old grain’s blob is never
touched — only its index-layer fields change.
Sourcepub fn forget(&mut self, hash: &Hash) -> Result<()>
pub fn forget(&mut self, hash: &Hash) -> Result<()>
Forget (erase from hot store) — writes a tombstone to the op-log. File-level crypto-erasure remains the strong path.
Bounded k-hop traversal over the given relations.
Returns reached entity terms (excluding the start), BFS order.
Direction::In/Both use the selective OSP index, so reverse
expansion only sees entity-valued relations.
Sourcepub fn path(
&mut self,
ns: &str,
from: &str,
to: &str,
relations: &[&str],
max_depth: usize,
) -> Result<Option<Vec<String>>>
pub fn path( &mut self, ns: &str, from: &str, to: &str, relations: &[&str], max_depth: usize, ) -> Result<Option<Vec<String>>>
Bounded bidirectional-ish path search (forward BFS with parents).
Sourcepub fn entity_at(
&mut self,
ns: &str,
subject: &str,
relation: &str,
t: i64,
axis: Axis,
) -> Result<Option<DeserializedGrain>>
pub fn entity_at( &mut self, ns: &str, subject: &str, relation: &str, t: i64, axis: Axis, ) -> Result<Option<DeserializedGrain>>
Two-axis as-of read.
Sourcepub fn has(&mut self, hash: &Hash) -> Result<bool>
pub fn has(&mut self, hash: &Hash) -> Result<bool>
Whether a grain with this content address exists.
Sourcepub fn search_text(
&mut self,
ns: &str,
query: &str,
k: usize,
) -> Result<Vec<i64>>
pub fn search_text( &mut self, ns: &str, query: &str, k: usize, ) -> Result<Vec<i64>>
BM25 leg: FTS MATCH over grain text (facts as “s r o”, event
content). Returns current-grain seqs in match order.
Sourcepub fn nearest_semantic(
&mut self,
ns: &str,
subject: Option<&str>,
relation: Option<&str>,
text: &str,
k: usize,
) -> Result<Vec<(Hash, f32)>>
pub fn nearest_semantic( &mut self, ns: &str, subject: Option<&str>, relation: Option<&str>, text: &str, k: usize, ) -> Result<Vec<(Hash, f32)>>
Vector leg: cosine top-k over embedded grain text (brute force —
exact search at per-memory scale, per M0 measurements).
Semantic nearest-neighbours to text among current grains, optionally
scoped to (subject, relation), returned as (hash, cosine_similarity)
most-similar first. This is the advise half of a write-time novelty
gate: a reflection harness calls it before writing a distilled lesson
and, if the top similarity clears its own threshold, supersedes the
near-duplicate instead of adding a paraphrase — the paraphrase-rot the
exact-value idempotent add (add_if_novel) can’t catch. It never
mutates: the host stays in control (advise, don’t drop).
Novelty is a vector operation, so this requires an installed
embedder and errors loudly without one rather than silently returning
nothing. text is embedded as-is and compared against each grain’s
stored embedding (subject·relation·object + content); scoping to
(subject, relation) keeps the constant prefix out of the way so the
object phrasing dominates the score.
pub fn search_vector( &mut self, ns: &str, query: &str, k: usize, ) -> Result<Vec<i64>>
Sourcepub fn recall_hybrid(
&mut self,
ns: &str,
subject: Option<&str>,
relation: Option<&str>,
query: Option<&str>,
k: usize,
deadline: Option<Duration>,
) -> Result<Vec<DeserializedGrain>>
pub fn recall_hybrid( &mut self, ns: &str, subject: Option<&str>, relation: Option<&str>, query: Option<&str>, k: usize, deadline: Option<Duration>, ) -> Result<Vec<DeserializedGrain>>
Hybrid recall: structural leg + BM25 leg fused
with Reciprocal Rank Fusion; optional deadline makes it fail-open
(returns whatever is gathered when the budget expires). This is the
plain path — see recall_hybrid_tuned for
the Tier-1/Tier-2 refinements (query expansion, MMR, rerank).
Sourcepub fn recall_hybrid_tuned(
&mut self,
ns: &str,
subject: Option<&str>,
relation: Option<&str>,
query: Option<&str>,
k: usize,
deadline: Option<Duration>,
tuning: RecallTuning,
) -> Result<Vec<DeserializedGrain>>
pub fn recall_hybrid_tuned( &mut self, ns: &str, subject: Option<&str>, relation: Option<&str>, query: Option<&str>, k: usize, deadline: Option<Duration>, tuning: RecallTuning, ) -> Result<Vec<DeserializedGrain>>
Hybrid recall with post-fusion refinements. Same three
legs and RRF fusion as recall_hybrid, plus the
opt-in tuning stages:
- query expansion (Tier-1): extra BM25 legs from rule-based query variants, RRF-fused — bridges vocabulary gaps with no embedder.
- rerank (Tier-2): a cross-encoder re-scores a widened candidate
pool via the installed
RerankBackend. Takes precedence over MMR. - diversity (Tier-1): MMR reorders the pool to cut near-duplicates, using the query embedding + stored candidate vectors.
Every stage is fail-open: past the deadline, or with its backend/data
absent, it degrades to plain fusion order rather than erroring. All
default off, so this is a strict superset of recall_hybrid.
Sourcepub fn subjects_with_relation(
&mut self,
ns: &str,
relation: &str,
) -> Result<Vec<String>>
pub fn subjects_with_relation( &mut self, ns: &str, relation: &str, ) -> Result<Vec<String>>
Distinct subjects holding relation in ns (POS-index scan).
Backs directory-style listings (memory-tool view on a dir).
Sourcepub fn remember(
&mut self,
ns: &str,
content: &str,
observer: &str,
extractor: Option<&dyn Fn(&str) -> Vec<FactDraft>>,
) -> Result<RememberResult>
pub fn remember( &mut self, ns: &str, content: &str, observer: &str, extractor: Option<&dyn Fn(&str) -> Vec<FactDraft>>, ) -> Result<RememberResult>
The remember() seam: store raw content as an
Observation grain, run the caller-supplied extraction function
(typically an LLM callback — the host owns the model relationship),
and store each returned draft as a Fact with derived_from
provenance back to the observation.
Sourcepub fn open_forks(&mut self) -> Result<Vec<ForkGroup>>
pub fn open_forks(&mut self) -> Result<Vec<ForkGroup>>
Open supersession tips for (subject, relation) — normally one; more
than one means a fork (v4 grain-git model). Ordered provisional-first.
Enumerate every open fork in the file — each (ns, subject, relation)
whose heads table holds more than one live tip. This is the honest
structural conflict signal: a true fork only arises from concurrent
supersession of the same value (typically edits synced from two
writers). Recall never surfaces this to stay off the hot path; operators
call deja forks to find and merge them. Not a hot path (scans the
heads table + reverse term lookups).
pub fn heads( &mut self, ns: &str, subject: &str, relation: &str, ) -> Result<Vec<(Hash, i64)>>
Sourcepub fn merge_heads<G: Grain + 'static>(
&mut self,
ns: &str,
subject: &str,
relation: &str,
merged: &mut G,
) -> Result<Hash>
pub fn merge_heads<G: Grain + 'static>( &mut self, ns: &str, subject: &str, relation: &str, merged: &mut G, ) -> Result<Hash>
Close a fork: write merged superseding EVERY open tip, with all
parents recorded in the provenance chain (git merge commit).
Sourcepub fn history(
&mut self,
ns: &str,
subject: &str,
relation: &str,
) -> Result<Vec<HistoryEntry>>
pub fn history( &mut self, ns: &str, subject: &str, relation: &str, ) -> Result<Vec<HistoryEntry>>
Supersession-chain history for (namespace, subject, relation), newest first — the HISTORY statement’s backing read (§5.13).
Sourcepub fn verify(&mut self) -> Result<VerifyReport>
pub fn verify(&mut self) -> Result<VerifyReport>
Verify store integrity: Turso’s own integrity check plus a full content-address re-verification (every blob re-hashed and compared to its stored hash — the tamper-evidence read).
Sourcepub fn stats(&mut self) -> Result<StoreStats>
pub fn stats(&mut self) -> Result<StoreStats>
Store statistics (CLI stats).
Source§impl DejaDB
impl DejaDB
Sourcepub fn put_blob(&mut self, bytes: &[u8]) -> Result<String>
pub fn put_blob(&mut self, bytes: &[u8]) -> Result<String>
Store bytes in the per-memory CAS; returns the cas://sha256: URI.
Idempotent — content addressing dedupes by construction.
Sourcepub fn get_blob(&mut self, uri: &str) -> Result<Vec<u8>>
pub fn get_blob(&mut self, uri: &str) -> Result<Vec<u8>>
Fetch bytes by cas://sha256: URI, verifying the hash on read.
Sourcepub fn gc_blobs(&mut self) -> Result<usize>
pub fn gc_blobs(&mut self) -> Result<usize>
Remove CAS blobs not referenced by any live grain’s content_refs.
Returns the number of blobs removed.
Sourcepub fn bundle_since(
&mut self,
after_op_seq: i64,
path: &str,
) -> Result<BundleStats>
pub fn bundle_since( &mut self, after_op_seq: i64, path: &str, ) -> Result<BundleStats>
Export all ops after after_op_seq to a bundle file.
Record: op(u8) · hlc(i64 LE) · hash(32) · blob_len(u32 LE) · blob.
Blobs of later-forgotten grains export as len 0 — the importer
relies on the subsequent tombstone for net-equivalence.
Sourcepub fn import_bundle(&mut self, path: &str) -> Result<ImportStats>
pub fn import_bundle(&mut self, path: &str) -> Result<ImportStats>
Import a bundle (idempotent; fast-forward replay in op order).
Sourcepub fn import_bundle_until(
&mut self,
path: &str,
max_hlc: Option<i64>,
) -> Result<ImportStats>
pub fn import_bundle_until( &mut self, path: &str, max_hlc: Option<i64>, ) -> Result<ImportStats>
Import, applying only ops with hlc <= max_hlc when set — the
point-in-time restore primitive (§5.10b): replay history to T.
Auto Trait Implementations§
impl !Freeze for DejaDB
impl !RefUnwindSafe for DejaDB
impl !UnwindSafe for DejaDB
impl Send for DejaDB
impl Sync for DejaDB
impl Unpin for DejaDB
impl UnsafeUnpin for DejaDB
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<K, Q> Comparable<Q> for K
impl<K, Q> Comparable<Q> for K
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<K, Q> Equivalent<Q> for K
impl<K, Q> Equivalent<Q> for K
Source§fn equivalent(&self, key: &Q) -> bool
fn equivalent(&self, key: &Q) -> bool
key and return true if they are equal.impl<T> ErasedDestructor for Twhere
T: 'static,
impl<T> Fruit for T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more