Skip to main content

DejaDB

Struct DejaDB 

Source
pub struct DejaDB { /* private fields */ }
Expand description

The embedded DejaDB store handle — one file per memory.

Implementations§

Source§

impl DejaDB

Source

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.

Source

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

Source

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.

Source

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().

Source

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.

Source

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.

Source

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.

Source

pub fn has_reranker(&self) -> bool

Whether a reranker backend is installed.

Source

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.

Source

pub fn index_text_enabled(&self) -> bool

Whether the BM25 text index is populated on writes (file-declared, honored or re-stamped at open).

Source

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).

Source

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.

Source

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.

Source

pub fn declared_embedding(&self) -> Option<(&str, usize)>

Embedding provenance declared by the file (model, dim), if any vectors were ever written.

Source

pub fn open_warnings(&self) -> &[String]

Reconciliation warnings from open / set_embedder: file declarations vs what this session supplied. Empty when everything agrees.

Source

pub fn add<G: Grain + 'static>(&mut self, grain: &G) -> Result<Hash>

Add one grain (full txn). Returns its content address.

Source

pub fn add_batch(&mut self, grains: &[&dyn AddableDyn]) -> Result<Vec<Hash>>

Batched add — one txn for the whole slice (voice write-back path).

Source

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.

Source

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.

Source

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.

Source

pub fn get(&mut self, hash: &Hash) -> Result<DeserializedGrain>

Fetch a grain by content address.

Source

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.

Source

pub fn latest( &mut self, ns: &str, subject: &str, relation: &str, ) -> Result<Option<DeserializedGrain>>

Current value head for (subject, relation) — the µs point read.

Source

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).

Source

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.

Source

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.

Source

pub fn related( &mut self, ns: &str, start: &str, relations: &[&str], dir: Direction, depth: usize, cap: usize, ) -> Result<Vec<String>>

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.

Source

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).

Source

pub fn entity_at( &mut self, ns: &str, subject: &str, relation: &str, t: i64, axis: Axis, ) -> Result<Option<DeserializedGrain>>

Two-axis as-of read.

Source

pub fn has(&mut self, hash: &Hash) -> Result<bool>

Whether a grain with this content address exists.

Source

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.

Source

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.

Source

pub fn search_vector( &mut self, ns: &str, query: &str, k: usize, ) -> Result<Vec<i64>>

Source

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).

Source

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.

Source

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).

Source

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.

Source

pub fn count(&mut self) -> Result<usize>

Total number of grains in the hot store.

Source

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).

Source

pub fn heads( &mut self, ns: &str, subject: &str, relation: &str, ) -> Result<Vec<(Hash, i64)>>

Source

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).

Source

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).

Source

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).

Source

pub fn stats(&mut self) -> Result<StoreStats>

Store statistics (CLI stats).

Source

pub fn changes_since( &mut self, after_op_seq: i64, limit: usize, ) -> Result<Vec<OpRecord>>

Op-log cursor read — the change feed (backs sync + UIs).

Source§

impl DejaDB

Source

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.

Source

pub fn get_blob(&mut self, uri: &str) -> Result<Vec<u8>>

Fetch bytes by cas://sha256: URI, verifying the hash on read.

Source

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.

Source

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.

Source

pub fn import_bundle(&mut self, path: &str) -> Result<ImportStats>

Import a bundle (idempotent; fast-forward replay in op order).

Source

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> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<K, Q> Comparable<Q> for K
where K: Borrow<Q> + ?Sized, Q: Ord + ?Sized,

Source§

fn compare(&self, key: &Q) -> Ordering

Compare self to key and return their ordering.
Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Converts 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>

Converts 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)

Converts &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)

Converts &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
where T: Any + Send,

Source§

fn into_any_send(self: Box<T>) -> Box<dyn Any + Send>

Converts Box<Trait> (where Trait: DowncastSend) to Box<dyn Any + Send>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_sync(self: Box<T>) -> Box<dyn Any + Send + Sync>

Converts Box<Trait> (where Trait: DowncastSync) to Box<dyn Any + Send + Sync>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Send + Sync>

Converts Arc<Trait> (where Trait: DowncastSync) to Arc<Any>, which can then be downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<K, Q> Equivalent<Q> for K
where K: Borrow<Q> + ?Sized, Q: Eq + ?Sized,

Source§

fn equivalent(&self, key: &Q) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> ErasedDestructor for T
where T: 'static,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Fruit for T
where T: Send + Downcast,

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more