Typed graph
Wire-only operations — entities, statements, relations, schema upload, transactions, and change-feed subscriptions on BrainClient.
Every operation on this page requires the wire client. The HTTP client is a strict subset and does not expose the typed graph, transactions, or subscriptions. All request types live under brain_db_sdk::wire::types, and brain_db_sdk::new_id() mints a fresh 16-byte id for the request_id fields.
Entities
Create a typed entity, then fetch or resolve it:
use brain_db_sdk::new_id;
use brain_db_sdk::wire::types::{EntityCreateRequest, EntityGetRequest};
let created = client
.create_entity(&EntityCreateRequest {
entity_type_id: 1,
canonical_name: "Ada Lovelace".to_string(),
aliases: vec!["Ada".to_string()],
attributes_blob: Vec::new(),
request_id: new_id(),
act_as: None,
})
.await?;
let entity_id = created.entity_id; // [u8; 16]
let fetched = client
.get_entity(&EntityGetRequest { entity_id, act_as: None })
.await?;
println!("{}", fetched.entity.canonical_name);Related entity verbs on BrainClient: update_entity, rename_entity, merge_entities, unmerge_entity, tombstone_entity, resolve_entity, and list_entities (a streaming, flattened list).
Statements
A statement asserts something about a subject entity — a fact, preference, event, and so on:
use brain_db_sdk::new_id;
use brain_db_sdk::wire::types::{
StatementCreateRequest, StatementKindWire, StatementObjectWire, StatementValueWire,
EvidenceRefWire,
};
let stmt = client
.create_statement(&StatementCreateRequest {
kind: StatementKindWire::Preference,
subject: entity_id,
predicate: "prefers".to_string(),
object: StatementObjectWire::Value(StatementValueWire::Text("dark mode".to_string())),
confidence: 0.9,
evidence: EvidenceRefWire::Inline(vec![]),
extractor_id: 0,
valid_from_unix_nanos: 0,
valid_to_unix_nanos: 0,
event_at_unix_nanos: 0,
schema_version: 0,
request_id: new_id(),
act_as: None,
})
.await?;
println!("statement {:?} (chain root {:?})", stmt.statement_id, stmt.chain_root);The response reports any prior statement it auto_superseded and the supersession chain_root. Revise a claim with supersede_statement, retire it with tombstone_statement, scrub a genuine mistake with retract_statement, and walk its full version chain with statement_history.
Relations
A relation connects two entities:
use brain_db_sdk::new_id;
use brain_db_sdk::wire::types::{RelationCreateRequest, EvidenceRefWire};
let rel = client
.create_relation(&RelationCreateRequest {
relation_type: "works_with".to_string(),
from_entity: ada_id,
to_entity: babbage_id,
properties_blob: Vec::new(),
evidence: EvidenceRefWire::Inline(vec![]),
extractor_id: 0,
confidence: 0.8,
valid_from_unix_nanos: 0,
valid_to_unix_nanos: 0,
request_id: new_id(),
act_as: None,
})
.await?;
println!("relation {:?}", rel.relation_id);Traverse the relation graph multi-hop with traverse_relations (flattened) or traverse_relations_frames (raw streamed), and enumerate an entity's relations with list_relations_from / list_relations_to.
Schema
Typed-graph writes are checked against the active schema — a STATEMENT_CREATE or RELATION_CREATE referencing an undeclared predicate or type is rejected. Upload a schema document to declare types:
use brain_db_sdk::new_id;
use brain_db_sdk::wire::types::SchemaUploadRequest;
let resp = client
.upload_schema(&SchemaUploadRequest {
schema_document: schema_text.to_string(),
dry_run: false, // true validates without applying
allow_breaking: false,
request_id: new_id(),
})
.await?;
println!("namespace {} at version {}", resp.namespace, resp.schema_version);
for err in &resp.validation_errors {
eprintln!(" {}:{} {}", err.line, err.column, err.message);
}With dry_run: true the server validates and returns any validation_errors plus a backward_compatible verdict without persisting. validate_schema, get_schema, and list_schemas round out the schema surface.
Transactions
The client mints the txn_id; writes that carry it enroll in the transaction until commit or abort:
use brain_db_sdk::new_id;
use brain_db_sdk::{BrainClient, EncodeBuilder};
use brain_db_sdk::wire::types::{TxnBeginRequest, TxnCommitRequest};
let txn_id = new_id();
client.txn_begin(&TxnBeginRequest { txn_id, timeout_seconds: 30 }).await?;
// Enroll a write by setting its txn_id (the builder leaves it None).
let mut req = EncodeBuilder::new("part of a transaction").build();
req.txn_id = Some(txn_id);
client.encode(&req).await?;
let committed = client.txn_commit(&TxnCommitRequest { txn_id }).await?;
println!("{} operations applied", committed.operations_applied);To discard the buffered work instead, call txn_abort(&TxnAbortRequest { txn_id }).
Subscriptions
Open a long-lived change feed and drain it with next().await:
use brain_db_sdk::wire::types::{SubscribeRequest, SubscriptionFilter};
let mut sub = client
.subscribe(&SubscribeRequest {
filter: SubscriptionFilter {
contexts: None,
kinds: None,
similar_to: None,
agents: None, // None/empty = every event routed to this shard
},
include_history: false,
from_lsn: None,
max_inflight: 64,
})
.await?;
while let Some(event) = sub.next().await {
let event = event?;
println!("event {:?} at lsn {}", event.event_type, event.lsn);
}
sub.unsubscribe().await?; // clean teardownThe server pushes SUBSCRIBE_EVENT frames — memory lifecycle events, typed-graph changes, and completed background stages — until the subscription is torn down.
Because a write returns as soon as its WAL record is durable (the default WaitMode::Ack), the async derivation stages (auto-edge, temporal-edge, extractor) complete afterward. Subscribing keyed on the write's lsn lets you observe those stages as they finish — the alternative to EncodeBuilder::wait_derived().
Was this page helpful?