Wire client
BrainClient — the native BRN0 wire client. Connect, handshake, authenticate, and reach the full Brain surface over one multiplexed TCP connection.
Connect
BrainClient::connect takes a SocketAddr and an Auth credential. It opens the TCP connection, runs the BRN0 handshake, and returns a live client bound to the session the server granted:
use std::net::SocketAddr;
use brain_db_sdk::{Auth, BrainClient};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let addr: SocketAddr = "127.0.0.1:9090".parse()?;
let client = BrainClient::connect(addr, Auth::Token(b"my-token".to_vec())).await?;
println!("connected to shard {}", client.session().bound_shard_id);
Ok(())
}The address is a std::net::SocketAddr, not a URL — parse a host:port string (or construct it directly). Resolve a hostname to an address yourself before calling connect.
Authentication
Auth is the connection's whole identity — there is no anonymous mode. The server resolves (namespace, agent, permissions) from the credential and refuses any connection it cannot resolve:
use brain_db_sdk::Auth;
use brain_db_sdk::wire::types::MtlsClaim;
// A shared bearer token (raw bytes):
let token = Auth::Token(b"my-token".to_vec());
// Or an mTLS subject claim:
let mtls = Auth::Mtls(MtlsClaim {
cert_fingerprint: [0u8; 32],
asserted_subject: "spiffe://arc/agent/ada".to_string(),
});The credential is sent in an AUTH frame after the server's WELCOME. The client never claims an identity — it presents the credential and the server assigns the agent and namespace.
Custom configuration
connect uses default transport settings. For explicit control, build a ClientConfig and call connect_with:
use std::net::SocketAddr;
use std::time::Duration;
use brain_db_sdk::{Auth, BrainClient, ClientConfig};
let addr: SocketAddr = "127.0.0.1:9090".parse()?;
let mut config = ClientConfig::new(Auth::Token(b"my-token".to_vec()));
config.client_id = "my-service".to_string();
config.connect_timeout = Some(Duration::from_secs(5));
config.request_timeout = Some(Duration::from_secs(20));
let client = BrainClient::connect_with(addr, config).await?;ClientConfig::new(auth) takes the mandatory credential and fills the rest with defaults. There is no Default impl — a connection without a credential cannot exist. Its public fields:
client_idStringoptionalFree-form identifier sent in HELLO. Defaults to "brain-db-sdk-rust".
supported_versionsVec<u8>optionalWire versions the client accepts, in preference order. Defaults to [1].
capabilitiesHelloCapabilitiesoptionalCapabilities advertised in HELLO (streaming, zstd, server push). Defaults to streaming on.
authAuthoptionalThe credential. Mandatory — set by ClientConfig::new.
connect_timeoutOption<Duration>optionalDeadline for the TCP connect. None waits indefinitely. Defaults to 10 seconds.
request_timeoutOption<Duration>optionalPer-response read deadline after connecting. Defaults to 30 seconds.
The session
After the handshake, session() returns the SessionInfo the server granted. Convenience accessors surface the two identity fields directly:
let session = client.session();
println!("server={} version={}", session.server_id, session.chosen_version);
println!("shard={}", session.bound_shard_id);
// Convenience accessors:
let agent: [u8; 16] = client.agent_id();
let namespace: &str = client.namespace();SessionInfo carries agent_id, server_id, chosen_version, session_id, bound_shard_id, permissions, namespace, and server_features. The namespace is empty when the connection resolves to the reserved brain system namespace — the client only reads it, it never sends a namespace.
Concurrency
Every verb takes &self, and a background reader task demultiplexes responses by stream id. Share one client across tasks behind an Arc and call freely — many requests run in flight over the single connection:
use std::sync::Arc;
let client = Arc::new(client);
let c1 = client.clone();
let c2 = client.clone();
// c1 and c2 issue requests concurrently over the one connection.For socket-level parallelism (multiple connections), see brain_db_sdk::Pool.
When to reach for the wire client
Use BrainClient when you need anything beyond the HTTP subset:
- Typed graph — entities, statements, relations, schema upload (Typed graph).
- Transactions —
txn_begin/txn_commit/txn_abort. - Subscriptions — long-lived
SUBSCRIBEchange feeds. - Streaming reads —
memory_list,graph_fetch,traverse_relations, and the*_framesstreaming variants. - Self-hosting where you connect straight to a shard's TCP listener.
Closing
Send BYE and drop the connection cleanly:
client.close().await?;Was this page helpful?