Brainby arc-labs/docs

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 teardown

The 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?

On this page