Gnosis
Usage with the SDK
Driving Gnosis through WeaveNode — the recommended entry point for most applications.
Why use the SDK
The weave-sdk crate exposes Gnosis via a namespaced API on WeaveNode. You open a graph by name, add triples, query with patterns. The SDK owns the Strand lifecycle.
Open, add, query
use weave_sdk::prelude::*;
#[tokio::main]
async fn main() -> WeaveResult<()> {
let node = WeaveNode::builder()
.namespace("l1fe")
.identifier("graph-agent")
.storage_dir("/tmp/weave-graph")
.build()
.await?;
node.open_gnosis("kb").await?;
node.gnosis_add("kb", "alice", "trusts", "bob").await?;
node.gnosis_add("kb", "alice", "trusts", "carol").await?;
node.gnosis_add("kb", "bob", "has_role", "admin").await?;
// Query: all facts about alice.
let facts = node.gnosis_query("kb", Some("alice"), None, None).await?;
for t in &facts {
println!("{} {} {}", t.subject, t.predicate, t.object);
}
// Neighbors: walk from alice.
let nbrs = node.gnosis_neighbors("kb", "alice").await?;
for (pred, obj) in nbrs {
println!("alice --[{pred}]--> {obj}");
}
Ok(())
}SDK surface for Gnosis
| Call | Returns | Notes |
|---|---|---|
node.open_gnosis(name) | WeaveResult<()> | Idempotent |
node.gnosis_add(name, s, p, o) | WeaveResult<()> | Strand append + index update |
node.gnosis_query(name, s, p, o) | WeaveResult<Vec<Triple>> | None for wildcards |
node.gnosis_neighbors(name, node_id) | WeaveResult<Vec<(predicate, object)>> | Subject-side walk |
node.gnosis(name) | Option<&Gnosis> (via store) | Direct access |
A small reasoning loop
use weave_sdk::prelude::*;
async fn can_admin(node: &WeaveNode, who: &str) -> WeaveResult<bool> {
let facts = node.gnosis_query("kb", Some(who), Some("has_role"), Some("admin")).await?;
Ok(!facts.is_empty())
}
async fn add_trust(node: &WeaveNode, from: &str, to: &str) -> WeaveResult<()> {
node.gnosis_add("kb", from, "trusts", to).await
}A typical ingestion shape
use weave_sdk::prelude::*;
struct Doc {
id: String,
author: String,
tags: Vec<String>,
}
async fn ingest(node: &WeaveNode, doc: Doc) -> WeaveResult<()> {
node.gnosis_add("docs", &doc.id, "authored_by", &doc.author).await?;
for tag in &doc.tags {
node.gnosis_add("docs", &doc.id, "tagged_with", tag).await?;
}
Ok(())
}
async fn docs_by_tag(node: &WeaveNode, tag: &str) -> WeaveResult<Vec<String>> {
let triples = node.gnosis_query("docs", None, Some("tagged_with"), Some(tag)).await?;
Ok(triples.into_iter().map(|t| t.subject).collect())
}Errors and recovery
| Surface | Error | What to do |
|---|---|---|
open_gnosis on existing name | Returns existing handle | No action |
gnosis_add with disk full | WeaveError::Storage from Strand | Free disk, retry |
gnosis_query after partial index rebuild | Returns whatever the indices currently hold | On a known-cold start, allow a rebuild pass before serving production traffic |
Practical patterns
Time-bound facts
Gnosis has no built-in expiry. Encode it in the predicate:
let now = chrono::Utc::now().timestamp();
node.gnosis_add("kb", "alice", &format!("trusts_until:{}", now + 86400), "bob").await?;A periodic compaction job removes expired triples by writing tombstones (today: re-add with a "revoked" predicate and filter in the caller).
Composition with Basis
Use Gnosis for typed relationships and Basis for similarity over the same identifiers:
use uuid::Uuid;
async fn ingest_doc(node: &weave_sdk::WeaveNode, doc_id: Uuid, author: &str, embedding: &[f32]) -> weave_sdk::error::WeaveResult<()> {
node.gnosis_add("kb", &doc_id.to_string(), "authored_by", author).await?;
node.basis_add("kb-vec", doc_id, embedding).await?;
Ok(())
}The semantic search tutorial walks through the full pattern: Semantic Search.