1import{j as e}from"./vendor-CR4GEDXU.js";import{H as m,L as n}from"./index-DEH-EspT.js";import{f as o}from"./firmware-versions.generated-DU3mt0lW.js";import{N as p}from"./Navbar-tOzc1LfG.js";import{F as h}from"./Footer-D5psJso-.js";import{S as x,b as g}from"./sdkNav-BqoeXEOd.js";import{T as t}from"./TabsBar-De5UaXKx.js";import{A as f}from"./AgenticSdkSection-DGmhWeZG.js";import{R as b}from"./ReleaseValidationNotice-B_ENU5zk.js";import{P as l}from"./package-uxgXzK9w.js";import{E as c}from"./external-link-KY1DdKsQ.js";import"./ui-C4oFTs1l.js";import"./query-DOtdOdQ4.js";import"./input-DokEs6ju.js";import"./CogDetailModal-DG7TMr0D.js";import"./lock-DvwRkkXo.js";import"./cognitum-logo-nav-Ab5-TKQ3.js";import"./search-DqP_1Y0P.js";import"./chevron-right-BX5NHfjR.js";import"./check-CqweXUBF.js";import"./code-C6nbiYvx.js";import"./sparkles-_bU26kwM.js";import"./gauge-CagVqRJN.js";import"./loader-circle-DzNOjQOo.js";import"./bot-CmTw7lot.js";import"./arrow-right-DHxjqloZ.js";import"./boxes-DrYmbB5j.js";import"./book-open-6GoY8iOl.js";import"./file-text-BT0Z1DR3.js";import"./users-BsIOobnr.js";import"./store-Bt_NW7kz.js";import"./mail-D8JM50xB.js";import"./menu-BVmwL0l-.js";import"./form-ByOlb__1.js";import"./label-BVgsVZnH.js";import"./ContactForm-DepiRU5V.js";import"./user-4wq7CL7j.js";import"./send-fpPRiXLF.js";import"./copy-BYO_mxRP.js";import"./shield-check-huqoaCnu.js";import"./triangle-alert-X0cjaitZ.js";import"./circle-check-CxnIeXQm.js";const r=o.cognitumSdkRust.replace(/^v/,""),y=[{id:"status",label:"Status"},{id:"install",label:"Install"},{id:"config",label:"Configuration"},{id:"quickstart",label:"Quickstart"},{id:"agentic-platform",label:"Agentic platform · v0.3"},{id:"cloud-catalog",label:"Cloud · Catalog"},{id:"cloud-orders",label:"Cloud · Orders"},{id:"cloud-leads-contact",label:"Cloud · Leads / Contact"},{id:"cloud-devices",label:"Cloud · Devices / OTA"},{id:"cloud-mcp",label:"Cloud · MCP"},{id:"cloud-brain",label:"Cloud · Brain"},{id:"seed-connect",label:"Seed · Connect"},{id:"seed-call-options",label:"Seed · Per-call options"},{id:"seed-tls",label:"Seed · TLS"},{id:"seed-pair",label:"Seed · Pair"},{id:"seed-status",label:"Seed · Status / Identity"},{id:"seed-store",label:"Seed · Store"},{id:"seed-witness",label:"Seed · Witness / Custody"},{id:"seed-ota",label:"Seed · OTA"},{id:"seed-mesh",label:"Seed · Mesh"},{id:"discovery",label:"Discovery"},{id:"tokens",label:"Tokens"},{id:"errors",label:"Errors"},{id:"features",label:"Cargo features"},{id:"related",label:"Related"}];function le(){const d={"@context":"https://schema.org","@type":"TechArticle",headline:"Cognitum Rust SDK",description:`Async Rust SDK source/package snapshot ${o.cognitumSdkRust}. Cloud and Seed clients plus feature-gated agentic integrations; staging validation pending.`,url:"https://cognitum.one/sdks/rust"};return e.jsxs(e.Fragment,{children:[e.jsxs(m,{children:[e.jsx("title",{children:"Rust SDK · cognitum-one | Cognitum"}),e.jsx("meta",{name:"description",content:"cognitum-one Rust SDK source contract. Async cloud and feature-gated Seed/agentic clients; registry snapshot and staging status are called out separately."}),e.jsx("link",{rel:"canonical",href:"https://cognitum.one/sdks/rust"}),e.jsx("meta",{property:"og:type",content:"website"}),e.jsx("meta",{property:"og:url",content:"https://cognitum.one/sdks/rust"}),e.jsx("meta",{property:"og:title",content:"cognitum-one · Rust SDK"}),e.jsx("meta",{property:"og:description",content:"Async (tokio) Rust SDK for Cognitum. Cloud catalog/orders/MCP/brain/devices, mesh routing, TLS pinning, mDNS / Tailscale discovery."}),e.jsx("meta",{name:"twitter:title",content:"cognitum-one · Rust SDK"}),e.jsx("meta",{name:"twitter:description",content:"cargo add cognitum-one â async Rust client for the Cognitum platform."}),e.jsx("script",{type:"application/ld+json",children:JSON.stringify(d)})]}),e.jsxs("div",{className:"min-h-screen bg-background",children:[e.jsx(p,{}),e.jsx("main",{role:"main","aria-label":"Rust SDK",children:e.jsx("div",{className:"pt-28 pb-20",children:e.jsx(x,{title:"Rust SDK",sdkNav:g("rust"),pageNav:y,children:e.jsxs("div",{className:"space-y-10",children:[e.jsxs("header",{children:[e.jsxs("div",{className:"flex items-center gap-2 text-xs text-muted-foreground mb-2",children:[e.jsx(l,{className:"h-3.5 w-3.5"}),e.jsx("code",{className:"font-mono",children:"cognitum-one"}),e.jsxs("span",{className:"text-[10px] font-medium px-2 py-0.5 rounded-full bg-amber-500/10 text-amber-400 border border-amber-500/20",children:["Registry snapshot · ",o.cognitumSdkRust]})]}),e.jsx("h1",{className:"text-3xl md:text-4xl font-bold tracking-tight text-foreground mb-2",children:"Rust SDK"}),e.jsxs("p",{className:"text-base text-muted-foreground max-w-2xl",children:["Async Rust client (tokio) for Cognitum. Talk to the ",e.jsx("strong",{children:"Cognitum Cloud"})," control plane (catalog, orders, leads, contact, devices/OTA, MCP, brain) with the default build, or enable the"," ",e.jsx("code",{className:"font-mono text-xs",children:"seed"})," feature to talk directly to a ",e.jsx("strong",{children:"Seed"})," ","appliance â mesh routing, TLS pinning, mDNS / Tailscale discovery, redaction-safe pairing tokens, per-call routing knobs. Ships an MCP client with both HTTP and stdio transports."]})]}),e.jsx(b,{localEvidence:`The local Cargo metadata identifies cognitum-one ${o.cognitumSdkRust}, and its source/tests define the direct Seed and feature-gated agentic namespaces shown here.`,pendingEvidence:"The root cloud client still calls legacy raw-function paths such as /health and /listTemplates instead of the canonical /v1 routes. Do not treat its cloud examples as release-ready until route alignment and staging E2E pass.",title:"Rust SDK compatibility"}),e.jsxs("section",{children:[e.jsx("h2",{id:"status",className:"text-2xl font-bold text-foreground mb-3 scroll-mt-24",children:"Status"}),e.jsx("div",{className:"border border-amber-500/30 bg-amber-500/5 rounded-xl p-4 text-sm text-amber-100/90",children:e.jsxs("p",{children:[e.jsxs("strong",{children:[e.jsx("code",{className:"font-mono",children:"cognitum-one"})," ",o.cognitumSdkRust," is the local source/package version."]})," ","The generated registry snapshot records the same version, but this pre-deploy review did not perform a live registry install or staging cloud-client journey."]})})]}),e.jsxs("section",{children:[e.jsx("h2",{id:"install",className:"text-2xl font-bold text-foreground mb-3 scroll-mt-24",children:"Install"}),e.jsxs("p",{className:"text-sm text-muted-foreground mb-3",children:["Cloud-only is the default; the seed surface is gated behind the ",e.jsx("code",{className:"font-mono text-xs",children:"seed"})," ","feature so cloud users don't pay the URL parser / rustls-pinning weight."]}),e.jsx(t,{tabs:[{label:"cargo add",lang:"bash",code:`# Cloud only (catalog, orders, MCP, brain, devices, leads, contact) 2cargo add cognitum-one 3 4# + direct-to-seed surface 5cargo add cognitum-one --features seed 6 7# + mDNS discovery for LAN seeds 8cargo add cognitum-one --features seed,mdns`},{label:"Cargo.toml",lang:"toml",code:`[dependencies] 9# Cloud only 10cognitum-one = "${r}" 11 12# Seed surface 13cognitum-one = { version = "${r}", features = ["seed"] } 14 15# Seed + mDNS discovery 16cognitum-one = { version = "${r}", features = ["seed", "mdns"] } 17 18# Cloud + blocking client (no tokio runtime needed) 19cognitum-one = { version = "${r}", features = ["blocking"] } 20 21# Use native-tls instead of the default rustls 22cognitum-one = { version = "${r}", default-features = false, features = ["native-tls"] }`}]})]}),e.jsxs("section",{children:[e.jsx("h2",{id:"config",className:"text-2xl font-bold text-foreground mb-3 scroll-mt-24",children:"Configuration (Cloud)"}),e.jsxs("p",{className:"text-sm text-muted-foreground mb-3",children:["The cloud ",e.jsx("code",{className:"font-mono text-xs",children:"Client"})," is constructed from a"," ",e.jsx("code",{className:"font-mono text-xs",children:"ClientConfig"})," or via the fluent"," ",e.jsx("code",{className:"font-mono text-xs",children:"ClientBuilder"}),". Authentication is"," ",e.jsx("code",{className:"font-mono text-xs",children:"X-API-Key"})," per ADR-0003; a deprecated"," ",e.jsx("code",{className:"font-mono text-xs",children:"Authorization: Bearer"})," can be enabled for one deprecation window."]}),e.jsx(t,{tabs:[{label:"Builder",lang:"rust",code:`use cognitum_one::Client; 23 24let client = Client::builder() 25 .api_key(std::env::var("COGNITUM_API_KEY")?) 26 .base_url("https://api.cognitum.one") // default 27 .timeout_secs(30) // default 30s per attempt 28 .max_retries(3) // default 3 (ADR-0005) 29 .build()?; 30 31let health = client.health().await?; 32println!("api status: {}", health.status);`},{label:"ClientConfig",lang:"rust",code:`use cognitum_one::{Client, ClientConfig}; 33 34// Full struct shape â every field except api_key has a default. 35let config = ClientConfig { 36 api_key: std::env::var("COGNITUM_API_KEY")?, 37 base_url: None, // -> https://api.cognitum.one 38 timeout_secs: 30, 39 max_retries: 3, 40 use_bearer: false, // ADR-0003 deprecation flag
41 insecure: false, // dev-only: accept self-signed 42 trust_root_pem: None, // pin a custom root CA (mutually exclusive with insecure) 43}; 44 45let client = Client::try_with_config(config)?;`},{label:"Self-signed CA",lang:"rust",code:`// Pin a single root CA â system trust store is disabled. 46// For self-signed staging or on-prem deployments. 47let client = Client::builder() 48 .api_key(std::env::var("COGNITUM_API_KEY")?) 49 .trust_root_pem_file("./cognitum-ca.pem")? 50 .build()?;`},{label:"Insecure (dev only)",lang:"rust",code:`// Skips cert verification entirely. Emits a one-shot stderr warning. 51// Mutually exclusive with .trust_root_pem(_file) â build() returns 52// Error::Validation if both are set. 53let client = Client::builder() 54 .api_key("dev-key") 55 .danger_accept_invalid_certs(true) 56 .build()?;`},{label:"Bearer (deprecated)",lang:"rust",code:`// Sends \`Authorization: Bearer\` in addition to X-API-Key, 57// for compatibility with pre-ADR-0003 servers. Will be removed 58// in 2 minor releases. Set COGNITUM_SUPPRESS_BEARER_WARNING=1 59// to silence the stderr deprecation notice. 60let client = Client::builder() 61 .api_key(std::env::var("COGNITUM_API_KEY")?) 62 .deprecated_bearer_auth(true) 63 .build()?;`}]}),e.jsxs("p",{className:"text-[12px] text-muted-foreground mt-3",children:["The retry loop honours ",e.jsx("code",{className:"font-mono text-xs",children:"Retry-After"})," on 429s (header seconds, HTTP-date, body ",e.jsx("code",{className:"font-mono text-xs",children:"retry_after_us"}),', or "retry after Ns" text), and falls back to ADR-0005 equal-jitter backoff for 500 / 503. Body signal wins over header so proxies that strip ',e.jsx("code",{className:"font-mono text-xs",children:"Retry-After"})," still surface a sane delay."]})]}),e.jsxs("section",{children:[e.jsx("h2",{id:"quickstart",className:"text-2xl font-bold text-foreground mb-3 scroll-mt-24",children:"Quickstart"}),e.jsx(t,{tabs:[{label:"Cloud",lang:"rust",code:`use cognitum_one::{Client, Error}; 64 65#[tokio::main] 66async fn main() -> Result<(), Error> { 67 let client = Client::new(&std::env::var("COGNITUM_API_KEY").unwrap()); 68 69 let catalog = client.catalog().browse().await?; 70 println!("{} products", catalog.products.len()); 71 Ok(()) 72}`},{label:"Seed (direct)",lang:"rust",code:`use cognitum_one::seed::{SeedClient, SeedTls}; 73 74#[tokio::main] 75async fn main() -> Result<(), cognitum_one::Error> { 76 let client = SeedClient::builder() 77 .endpoint("https://cognitum.local:8443") 78 .tls(SeedTls::Insecure) // dev-only â see "Seed · TLS" 79 .build()?; 80 81 let status = client.status().await?; 82 println!("seed {} epoch {} ({} vectors)", 83 status.device_id, status.epoch, status.total_vectors); 84 Ok(()) 85}`},{label:"curl",lang:"bash",code:`# Cloud 86curl https://api.cognitum.one/v1/catalog 87curl -H "X-API-Key: $COGNITUM_API_KEY" \\ 88 -H "Content-Type: application/json" \\ 89 -X POST https://api.cognitum.one/v1/payment \\ 90 -d '{"email":"[email protected]","quantity":1}' 91 92# Seed (direct, allowlisted reads) 93curl -k https://cognitum.local:8443/api/v1/status 94curl -k https://cognitum.local:8443/api/v1/store/status`}]})]}),e.jsx(f,{language:"rust"}),e.jsxs("section",{children:[e.jsx("h2",{id:"cloud-catalog",className:"text-2xl font-bold text-foreground mb-3 scroll-mt-24",children:"Cloud · Catalog"}),e.jsxs("p",{className:"text-sm text-muted-foreground mb-3",children:["Hangs off ",e.jsx("code",{className:"font-mono text-xs",children:"client.catalog()"})," â"," ",e.jsx("code",{className:"font-mono text-xs",children:"CatalogResource"}),". Returns a"," ",e.jsx("code",{className:"font-mono text-xs",children:"CatalogResponse"})," with a typed"," ",e.jsx("code",{className:"font-mono text-xs",children:"Vec<Product>"}),"."]}),e.jsx(t,{tabs:[{label:"Browse",lang:"rust",code:`let catalog = client.catalog().browse().await?; 95for p in &catalog.products {
96 let cents = p.price_cents.unwrap_or(0); 97 println!("{:24} \${}.{:02}", p.name, cents / 100, cents % 100); 98} 99 100// Filter by category â encodes the value with a minimal percent-encoder. 101let hardware = client.catalog().browse_with_category("hardware").await?;`},{label:"Product type",lang:"rust",code:`pub struct Product { 102 pub id: String, 103 pub name: String, 104 pub description: Option<String>, 105 pub category: Option<String>, 106 pub price_cents: Option<u64>, 107 pub image_url: Option<String>, 108 pub available: Option<bool>, 109}`}]})]}),e.jsxs("section",{children:[e.jsx("h2",{id:"cloud-orders",className:"text-2xl font-bold text-foreground mb-3 scroll-mt-24",children:"Cloud · Orders"}),e.jsx(t,{tabs:[{label:"Create + status",lang:"rust",code:`// Create a presale payment intent. 110let created = client.orders() 111 .create("[email protected]", 1) 112 .await?; 113println!("client_secret = {}", created.client_secret); 114if let Some(id) = created.order_id { 115 println!("order id = {id}"); 116} 117 118// Look up existing orders by email â returns Vec<Order>. 119let orders = client.orders().status("[email protected]").await?; 120for o in orders { 121 println!("{:>10} {:8} {}", o.order_id, o.status, o.email); 122}`},{label:"Types",lang:"rust",code:`pub struct Order { 123 pub order_id: String, 124 pub email: String, 125 pub status: String, 126 pub quantity: Option<u32>, 127 pub amount_cents: Option<u64>, 128 pub created_at: Option<String>, 129} 130 131pub struct OrderCreateResponse { 132 pub client_secret: String, 133 pub order_id: Option<String>, 134}`}]})]}),e.jsxs("section",{children:[e.jsx("h2",{id:"cloud-leads-contact",className:"text-2xl font-bold text-foreground mb-3 scroll-mt-24",children:"Cloud · Leads / Contact"}),e.jsx(t,{tabs:[{label:"Leads",lang:"rust",code:`// Subscribe an email to the notify-me / waitlist for a product. 135let resp = client.leads() 136 .subscribe("[email protected]", "seed-v1") 137 .await?; 138assert!(resp.success);`},{label:"Contact",lang:"rust",code:`let resp = client.contact() 139 .send( 140 "Dev", // name 141 "[email protected]", // email 142 "Hi â question about ordering.", // message 143 "general", // inquiry_type ("general", "sales", "support", ...) 144 ) 145 .await?; 146assert!(resp.success);`}]})]}),e.jsxs("section",{children:[e.jsx("h2",{id:"cloud-devices",className:"text-2xl font-bold text-foreground mb-3 scroll-mt-24",children:"Cloud · Devices / OTA"}),e.jsxs("p",{className:"text-sm text-muted-foreground mb-3",children:["Three operations on ",e.jsx("code",{className:"font-mono text-xs",children:"client.devices()"}),":"," ",e.jsx("code",{className:"font-mono text-xs",children:"register"}),", ",e.jsx("code",{className:"font-mono text-xs",children:"check_update"}),","," ",e.jsx("code",{className:"font-mono text-xs",children:"heartbeat"}),"."]}),e.jsx(t,{tabs:[{label:"Register",lang:"rust",code:`// Register a new device with its Ed25519 public key. 147let device = client.devices() 148 .register("ed25519:f00ba4...") 149 .await?; 150println!("device {}", device.device_id);`},{label:"Check update",lang:"rust",code:`let r = client.devices() 151 .check_update("seed-001", "0.20.1") 152 .await?; 153if r.update_available { 154 println!("update available: {:?}", r.version); 155 if let Some(url) = r.download_url { 156 println!("download: {url}"); 157 } 158}`},{label:"Heartbeat",lang:"rust",code:`// Periodic liveness ping; updates last-seen timestamp. 159let r = client.devices().heartbeat("seed-001").await?; 160assert!(r.success);`}]})]}),e.jsxs("section",{children:[e.jsx("h2",{id:"cloud-mcp",className:"text-2xl font-bold text-foreground mb-3 scroll-mt-24",children:"Cloud · MCP"}),e.jsxs("p",{className:"text-sm text-muted-foreground mb-3",children:["Two distinct surfaces ship under ",e.jsx("code",{className:"font-mono text-xs",children:"cognitum_one::mcp"}),":"," ",e.jsx("code",{className:"font-mono text-xs",children:"McpResource"})," (cloud HTTP proxy hanging off"," ",e.jsx("code",{className:"font-mono text-xs",children:"client.mcp()"}),") and the protocol-level"," ",e.jsx("code",{className:"font-mono text-xs",children:"McpClient"})," parameterised over a"," ",e.jsx("code",{className:"font-mono text-xs",children:"Transport"})," with concrete"," ",e.jsx("code",{className:"font-mono text-xs",children:"HttpTransport"})," and ",e.jsx("code",{className:"font-mono text-xs",children:"StdioTransport"}),"."]}),e.jsx(t,{tabs:[{label:"Cloud proxy",lang:"rust",code:`use serde_json::json; 161 162// List + call against the cloud MCP endpoint. 163let tools = client.mcp().list_tools().await?; 164println!("{} tools", tools.len()); 165 166let result = client.mcp() 167 .call_tool("catalog_browse", json!({ "category": "hardware" })) 168 .await?; 169 170// Search docs index. 171let hits = client.mcp().search_docs("authentication patterns", Some(5)).await?; 172for h in hits { 173 println!("{}: {}", h.title, h.snippet.unwrap_or_default()); 174} 175
176// Initialize handshake (returns server protocolVersion / capabilities). 177let init = client.mcp().initialize().await?;`},{label:"Protocol-level (HTTP)",lang:"rust",code:`use cognitum_one::mcp::{HttpTransport, McpClient}; 178use serde_json::json; 179 180let transport = HttpTransport::new( 181 "https://api.cognitum.one/v1/mcp/sse", 182 &std::env::var("COGNITUM_API_KEY")?, 183)?; 184let mut mcp = McpClient::new(transport); 185 186mcp.initialize().await?; 187let tools = mcp.list_tools().await?; 188let out = mcp.call_tool("health_check", json!({})).await?; 189println!("{out}"); 190mcp.close().await?;`},{label:"Protocol-level (stdio)",lang:"rust",code:`use cognitum_one::mcp::{McpClient, StdioTransport}; 191use serde_json::json; 192 193// Spawn a local MCP server subprocess (any newline-delimited 194// JSON-RPC server). Stderr is drained to a background tokio task 195// so the child can never wedge the pipe. 196let transport = StdioTransport::builder() 197 .command("npx") 198 .args(["-y", "@some/mcp-server"]) 199 .env("SOME_KEY", "secret") 200 .spawn()?; 201 202let mut mcp = McpClient::new(transport); 203mcp.initialize().await?; 204let r = mcp.call_tool("ping", json!({})).await?; 205mcp.close().await?; // graceful: drops stdin, waits 5s, then SIGKILL`},{label:"Custom transport",lang:"rust",code:`use async_trait::async_trait; 206use cognitum_one::mcp::{JsonRpcMessage, McpError, Transport}; 207 208struct WebsocketTransport { /* ... */ } 209 210#[async_trait] 211impl Transport for WebsocketTransport { 212 async fn send(&mut self, msg: JsonRpcMessage) -> Result<(), McpError> { 213 // serialize + write to the WS sink 214 Ok(()) 215 } 216 async fn recv(&mut self) -> Result<JsonRpcMessage, McpError> { 217 // read + deserialize 218 Ok(JsonRpcMessage::default()) 219 } 220 async fn close(&mut self) -> Result<(), McpError> { 221 Ok(()) 222 } 223}`}]})]}),e.jsxs("section",{children:[e.jsx("h2",{id:"cloud-brain",className:"text-2xl font-bold text-foreground mb-3 scroll-mt-24",children:"Cloud · Brain"}),e.jsx("p",{className:"text-sm text-muted-foreground mb-3",children:"Shared knowledge base â share memories, search semantically, vote on usefulness."}),e.jsx(t,{tabs:[{label:"Share",lang:"rust",code:`let r = client.brain() 224 .share( 225 "fall back to seed-stream when esp32-uart goes silent for >2s", 226 Some("ops"), // namespace (optional) 227 Some(vec!["sensor".into(), "fallback".into()]), // tags (optional) 228 ) 229 .await?; 230println!("memory id = {:?}", r.id);`},{label:"Search",lang:"rust",code:`let r = client.brain() 231 .search("fallback patterns", Some("ops"), Some(10)) 232 .await?; 233for m in r.results { 234 let score = m.score.unwrap_or(0.0); 235 println!("{:.3} {}", score, m.content); 236}`},{label:"Vote",lang:"rust",code:`// vote: 1 (up) or -1 (down). The endpoint accepts an i8. 237client.brain().vote("memory-id-abc", 1).await?; 238client.brain().vote("memory-id-xyz", -1).await?;`}]})]}),e.jsxs("section",{children:[e.jsx("h2",{id:"seed-connect",className:"text-2xl font-bold text-foreground mb-3 scroll-mt-24",children:"Seed · Connect"}),e.jsxs("p",{className:"text-sm text-muted-foreground mb-3",children:["Behind ",e.jsx("code",{className:"font-mono text-xs",children:"--features seed"}),". Six resource accessors hang off"," ",e.jsx("code",{className:"font-mono text-xs",children:"SeedClient"}),": ",e.jsx("code",{className:"font-mono text-xs",children:"pair"}),","," ",e.jsx("code",{className:"font-mono text-xs",children:"store"}),", ",e.jsx("code",{className:"font-mono text-xs",children:"witness"}),","," ",e.jsx("code",{className:"font-mono text-xs",children:"custody"}),", ",e.jsx("code",{className:"font-mono text-xs",children:"ota"}),","," ",e.jsx("code",{className:"font-mono text-xs",children:"mesh"}),". Plus top-level ",e.jsx("code",{className:"font-mono text-xs",children:"status()"})," ","/ ",e.jsx("code",{className:"font-mono text-xs",children:"identity()"}),", ",e.jsx("code",{className:"font-mono text-xs",children:"session()"}),","," ",e.jsx("code",{className:"font-mono text-xs",children:"peers()"}),", ",e.jsx("code",{className:"font-mono text-xs",children:"rediscover()"}),"."," ",e.jsx("code",{className:"font-mono text-xs",children:"SeedClient"})," is ",e.jsx("code",{className:"font-mono text-xs",children:"Clone"})," â its inner state lives behind an ",e.jsx("code",{className:"font-mono text-xs",children:"Arc"}),", so cloning is cheap."]}),e.jsx(t,{tabs:[{label:"Single endpoint",lang:"rust",code:`use cognitum_one::seed::{SeedAuth, SeedClient, SeedTls}; 239 240let client = SeedClient::builder() 241 .endpoint("https://cognitum.local:8443") 242 .tls(SeedTls::Insecure) // dev-only â see Seed · TLS 243 .auth(SeedAuth::pairing_token( 244 std::env::var("SEED_BEARER")?, 245 )) 246 .max_retries(3) 247 .build()?; 248 249let status = client.status().await?; 250println!("seed {} v{} epoch {}", 251 status.device_id, /* firmware via identity */ "", status.epoch); 252
253// SeedClient is Clone â cheap to share across tasks. 254let bg = client.clone(); 255tokio::spawn(async move { 256 let _ = bg.status().await; 257});`},{label:"Multiple peers (mesh)",lang:"rust",code:`use cognitum_one::seed::{SeedClient, SeedTls, Routing}; 258 259// Phase 1.5: closest-first session-sticky reads, cycle-on-5xx 260// failover, pin-on-429 backoff. Order is preserved as the 261// list-index tie-breaker. 262let client = SeedClient::builder() 263 .endpoints(&[ 264 "https://10.0.0.10:8443", 265 "https://10.0.0.11:8443", 266 "https://10.0.0.12:8443", 267 ]) 268 .routing(Routing::Session) // default 269 .tls(SeedTls::Insecure) 270 .build()?; 271 272// Pinned session for read-your-writes within a logical scope: 273let session = client.session(); 274let _ss = session.store().status().await?;`},{label:"Inspect / lifecycle",lang:"rust",code:`// What peers does the client know about? 275let peers = client.peers(); 276println!("{} peers configured", peers.len()); 277 278// Re-run the configured Discovery provider (or, when none is 279// installed, reset all peers to Healthy and clear EMAs). 280client.rediscover().await?; 281 282// Pinned session â see "Seed · Connect"; sticky reads come from 283// here (read-your-writes within the session lifetime). 284let session = client.session(); 285println!("pinned to {}", session.pinned_peer()); 286 287// SeedClient drops the per-process active health probe on Drop; 288// no explicit close() needed.`},{label:"Active health probe",lang:"rust",code:`use std::time::Duration; 289 290// Opt-in background poller (ADR-0016a §D7). Pings GET /api/v1/status 291// on every peer at the given interval and feeds the routing layer's 292// PeerSet so latency / health stay fresh even when the caller is idle. 293let client = SeedClient::builder() 294 .endpoints(&["https://s1:8443", "https://s2:8443"]) 295 .tls(SeedTls::Insecure) 296 .health_interval(Duration::from_secs(30)) 297 .build()?;`}]})]}),e.jsxs("section",{children:[e.jsx("h2",{id:"seed-call-options",className:"text-2xl font-bold text-foreground mb-3 scroll-mt-24",children:"Seed · Per-call options"}),e.jsxs("p",{className:"text-sm text-muted-foreground mb-3",children:["Every resource method has a ",e.jsx("code",{className:"font-mono text-xs",children:"_with(opts: CallOptions)"})," twin that takes a typed ",e.jsx("code",{className:"font-mono text-xs",children:"CallOptions"})," bag."," ",e.jsx("code",{className:"font-mono text-xs",children:"CallOptions::default()"})," is a no-op â calling"," ",e.jsx("code",{className:"font-mono text-xs",children:"_with"})," with default options behaves identically to its parameterless twin."]}),e.jsx(t,{tabs:[{label:"Type",lang:"rust",code:`use std::time::Duration; 298use cognitum_one::seed::{CallOptions, Consistency, Prefer}; 299 300pub struct CallOptions { 301 /// Pin this one call to a specific peer URL. 302 /// Must be in the configured PeerSet â otherwise Error::Validation 303 /// with prefix "config: peer not in mesh: ...". 304 pub peer: Option<String>, 305 306 /// Override the closest-first picker for this call only. 307 pub prefer: Option<Prefer>, // Closest | LocalFirst | Random | Any 308 309 /// Consistency posture. Strong returns Error::Validation 310 /// with prefix "unsupported:" (the seed has no quorum layer). 311 pub consistency: Option<Consistency>, // Session | Eventual | Strong 312 313 /// Per-call read timeout override. Connect / total budgets are 314 /// untouched. 315 pub timeout: Option<Duration>, 316 317 /// Per-call retry override. None = builder default, 318 /// Some(0) = no retries. 319 pub retries: Option<u32>, 320}`},{label:"Examples",lang:"rust",code:`use std::time::Duration; 321use cognitum_one::seed::{CallOptions, Consistency, Prefer, StoreQuery}; 322 323// Pin to a specific peer: 324let opts = CallOptions::new() 325 .peer("https://10.0.0.11:8443"); 326let r = client.store().query_with( 327 StoreQuery { vector: emb.clone(), k: 5 }, 328 opts, 329).await?; 330 331// Prefer LAN peers: 332let opts = CallOptions::new().prefer(Prefer::LocalFirst); 333client.store().query_with(StoreQuery { vector: emb.clone(), k: 5 }, opts).await?; 334 335// Drop session-stickiness for one call: 336let opts = CallOptions::new().consistency(Consistency::Eventual); 337client.store().status_with(opts).await?; 338 339// Custom timeout + zero retries: 340let opts = CallOptions::new() 341 .timeout(Duration::from_secs(60)) 342 .retries(0); 343// (returns Err for non-idempotent paths if the call fails)
344client.pair().create_with(req, opts).await?;`},{label:"Strong is unsupported",lang:"rust",code:`use cognitum_one::Error; 345use cognitum_one::seed::{CallOptions, Consistency}; 346 347// The seed has no quorum layer. Asking for Strong fails fast, 348// before any network I/O, with a clean validation error. 349let opts = CallOptions::new().consistency(Consistency::Strong); 350match client.store().status_with(opts).await { 351 Err(Error::Validation(msg)) if msg.starts_with("unsupported:") => { 352 eprintln!("seed cannot do strong reads: {msg}"); 353 } 354 other => panic!("unexpected: {other:?}"), 355}`}]})]}),e.jsxs("section",{children:[e.jsx("h2",{id:"seed-tls",className:"text-2xl font-bold text-foreground mb-3 scroll-mt-24",children:"Seed · TLS"}),e.jsxs("p",{className:"text-sm text-muted-foreground mb-3",children:[e.jsx("code",{className:"font-mono text-xs",children:"SeedTls"})," has three modes â ",e.jsx("code",{className:"font-mono text-xs",children:"System"})," ","(webpki / OS roots, default), ",e.jsx("code",{className:"font-mono text-xs",children:"Pinned(Vec<u8>)"})," (single PEM CA, disables system roots), and ",e.jsx("code",{className:"font-mono text-xs",children:"Insecure"})," (dev-only). When discovery surfaces ",e.jsx("code",{className:"font-mono text-xs",children:"fp=sha256:<hex>"})," TXT entries, the SDK installs a custom"," ",e.jsx("code",{className:"font-mono text-xs",children:"FingerprintPinVerifier"})," on top of the chosen mode."]}),e.jsx(t,{tabs:[{label:"Pinned CA",lang:"rust",code:`use cognitum_one::seed::{SeedClient, SeedTls}; 356 357let pem = std::fs::read("./cognitum-ca.pem")?; 358let client = SeedClient::builder() 359 .endpoint("https://cognitum.local:8443") 360 .tls(SeedTls::Pinned(pem)) // single root; system trust off 361 .build()?;`},{label:"Fingerprint pin (mDNS)",lang:"rust",code:`// When mDNS-discovered peers advertise \`fp=sha256:<hex>\`, the SDK 362// builds a per-peer pin map and installs a rustls verifier that 363// strict-checks pinned peers and falls back to webpki-roots for 364// unpinned peers. fp= advertisements are NORMALIZED â colons and 365// case are stripped â so "sha256:AA:BB:CC" and "aabbcc" compare equal. 366// 367// A peer presenting a cert whose SHA-256 doesn't match the pin 368// fails the handshake with rustls::Error::General("fingerprint pin 369// mismatch for <host>"); the SDK surfaces the exact shape in 370// Error::Validation("tls_pin: fingerprint mismatch for ..."). 371 372#[cfg(feature = "mdns")] 373{ 374 use cognitum_one::seed::{MdnsDiscovery, SeedClient, SeedTls}; 375 376 let client = SeedClient::builder() 377 .discovery(MdnsDiscovery::new()) // emits fp= per peer 378 .tls(SeedTls::System) // pinned peers + webpki for the rest 379 .build()?; 380}`},{label:"Insecure (dev only)",lang:"rust",code:`use cognitum_one::seed::{SeedClient, SeedTls}; 381 382// One-shot stderr warning per process. Mutually exclusive with 383// SeedTls::Pinned â the rustls verifier wiring is different. 384// 385// NEVER use this in production. Prefer SeedTls::Pinned for 386// self-signed seeds, or fp=-pinning via mDNS. 387let client = SeedClient::builder() 388 .endpoint("https://localhost:18443") 389 .tls(SeedTls::Insecure) 390 .build()?;`}]})]}),e.jsxs("section",{children:[e.jsx("h2",{id:"seed-pair",className:"text-2xl font-bold text-foreground mb-3 scroll-mt-24",children:"Seed · Pair"}),e.jsxs("p",{className:"text-sm text-muted-foreground mb-3",children:["A pairing token is required for any write â store ingest, OTA actions, mutating mesh ops. The seed enforces a 30-second pairing window opened by the operator (e.g. via the on-device"," ",e.jsx("code",{className:"font-mono text-xs",children:"/store"})," UI or the ",e.jsx("code",{className:"font-mono text-xs",children:"POST /api/v1/pair/window"})," ","endpoint). Inside that window, ",e.jsx("code",{className:"font-mono text-xs",children:"pair().create()"})," mints a token."]}),e.jsx(t,{tabs:[{label:"Status",lang:"rust",code:`let s = client.pair().status().await?; 391println!("paired={}, clients={}, window_open={}, secs_left={}", 392 s.paired, s.client_count, s.pairing_window_open, s.window_remaining_secs);`},{label:"Create token",lang:"rust",code:`use cognitum_one::seed::PairCreate; 393 394let resp = client.pair().create(PairCreate { 395 client_name: "my-laptop".into(), 396}).await?; 397 398// resp.token is a SecretString â the Debug impl redacts. Use 399// .as_str() at the request boundary; never println!("{:?}", resp.token) 400// unredacted. 401let token = resp.token.as_str().to_owned(); 402let expires_at = resp.expires_at.as_deref().unwrap_or("never"); 403println!("paired as {} (expires {})", resp.client_name, expires_at); 404 405// Re-construct the client with the bearer for paired ops: 406use cognitum_one::seed::{SeedAuth, SeedClient, SeedTls}; 407 408let paired = SeedClient::builder() 409 .endpoint("https://cognitum.local:8443") 410 .tls(SeedTls::Insecure) 411 .auth(SeedAuth::pairing_token(token)) 412 .build()?;`},{label:"Delete (unpair)",lang:"rust",code:`// Revoke by client_name. The seed deletes the per-client record. 413client.pair().delete("my-laptop").await?;`},{label:"Per-peer tokens",lang:"rust",code:`use cognitum_one::seed::{InMemoryTokenBook, SecretString, SeedClient, SeedTls}; 414 415// Build a per-peer TokenBook for a multi-seed mesh â the 416// per-peer entry takes priority over the client-wide SeedAuth. 417let book = InMemoryTokenBook::from_map([ 418 ("https://10.0.0.10:8443", "bearer-1"), 419 ("https://10.0.0.11:8443", "bearer-2"), 420]); 421 422let client = SeedClient::builder() 423 .endpoints(&["https://10.0.0.10:8443", "https://10.0.0.11:8443"]) 424 .tls(SeedTls::Insecure) 425 .token_book(book) 426 .build()?;`}]})]}),e.jsxs("section",{children:[e.jsx("h2",{id:"seed-status",className:"text-2xl font-bold text-foreground mb-3 scroll-mt-24",children:"Seed · Status / Identity"}),e.jsx(t,{tabs:[{label:"Status",lang:"rust",code:`let s = client.status().await?; 427println!("device_id = {}", s.device_id); 428println!("uptime_secs = {}", s.uptime_secs); 429println!("epoch = {}", s.epoch); 430println!("vectors = {} (deleted {})", s.total_vectors, s.deleted_vectors); 431println!("dimension = {}", s.dimension); 432println!("paired = {}", s.paired); 433println!("roles = {:?}", s.roles); 434// Forward-compat: any field the firmware adds later is captured here. 435if !s.extras.is_empty() { 436 println!("extras = {:?}", s.extras); 437}`},{label:"Identity",lang:"rust",code:`let id = client.identity().await?; 438println!("device_id = {}", id.device_id); 439println!("public_key = {}", id.public_key); 440println!("firmware_version = {}", id.firmware_version);`}]})]}),e.jsxs("section",{children:[e.jsx("h2",{id:"seed-store",className:"text-2xl font-bold text-foreground mb-3 scroll-mt-24",children:"Seed · Store (RVF vector store)"}),e.jsx(t,{tabs:[{label:"Status",lang:"rust",code:`let s = client.store().status().await?; 441println!("{} vectors (deleted {}), dim {}, file_size {} bytes", 442 s.total_vectors, s.deleted_vectors, s.dimension, s.file_size_bytes);`},{label:"Query (kNN)",lang:"rust",code:`use cognitum_one::seed::StoreQuery; 443 444let r = client.store().query(StoreQuery { 445 vector: vec![0.1, 0.2, /* ... */ 0.8], 446 k: 5, 447}).await?; 448 449println!("query_ms = {:.2}", r.query_ms); 450for hit in &r.results { 451 println!("id={:>20}
451 distance={:.4} metadata={}", 452 hit.id, hit.distance, hit.metadata); 453}`},{label:"Ingest",lang:"rust",code:`use cognitum_one::seed::{StoreIngest, StoreIngestEntry}; 454use serde_json::json; 455 456let r = client.store().ingest(StoreIngest { 457 vectors: vec![ 458 StoreIngestEntry { 459 id: "doc:1".into(), 460 values: vec![0.1, 0.2, 0.3], 461 metadata: Some(json!({ "source": "report-q1.pdf" })), 462 }, 463 StoreIngestEntry { 464 id: "doc:2".into(), 465 values: vec![0.4, 0.5, 0.6], 466 metadata: Some(json!({ "source": "report-q2.pdf" })), 467 }, 468 ], 469}).await?; 470println!("ingested {}", r.ingested);`},{label:"Idempotency",lang:"rust",code:`// store.query() is declared idempotent in the SDK â it's a 471// read-with-body and the seed has no per-request side effects. The 472// SDK retries it on 5xx / network errors automatically. 473// 474// store.ingest() is NOT idempotent: an ingest produces a witness 475// chain entry, so retrying changes server state. The SDK never 476// auto-retries non-idempotent POSTs; if you've designed your batch 477// to be safe to retry (e.g. content-hashed IDs), opt in via 478// CallOptions. 479use cognitum_one::seed::CallOptions; 480 481let opts = CallOptions::new().retries(0); // explicit no-retry 482client.store().ingest_with(batch, opts).await?;`}]})]}),e.jsxs("section",{children:[e.jsx("h2",{id:"seed-witness",className:"text-2xl font-bold text-foreground mb-3 scroll-mt-24",children:"Seed · Witness / Custody"}),e.jsx("p",{className:"text-sm text-muted-foreground mb-3",children:"Both endpoints are allowlisted reads â no pairing required."}),e.jsx(t,{tabs:[{label:"Witness chain",lang:"rust",code:`// The wire shape changes per firmware (depth/epoch/head_hash on v0.20.x); 483// the SDK exposes a forward-compat catch-all via .extras instead of 484// baking field names that may churn. 485let chain = client.witness().chain().await?; 486for (key, value) in &chain.extras.0 { 487 println!("{key} = {value}"); 488}`},{label:"Custody epoch",lang:"rust",code:`let e = client.custody().epoch().await?; 489println!("custody epoch = {}", e.epoch);`}]})]}),e.jsxs("section",{children:[e.jsx("h2",{id:"seed-ota",className:"text-2xl font-bold text-foreground mb-3 scroll-mt-24",children:"Seed · OTA"}),e.jsx(t,{tabs:[{label:"Read config",lang:"rust",code:`let cfg = client.ota().config().await?; 490println!("OTA enabled = {} ({}, every {}s)", 491 cfg.enabled, cfg.channel, cfg.check_interval_secs);`},{label:"Check now",lang:"rust",code:`// v0.20.0+. Requires pairing token. Repeat-safe â the seed dedupes 492// internally â so the SDK marks it idempotent and may auto-retry on 5xx. 493let r = client.ota().check_now().await?; 494println!("triggered = {}", r.triggered); 495println!("message = {}", r.message); 496println!("channel = {}", r.channel);`}]})]}),e.jsxs("section",{children:[e.jsx("h2",{id:"seed-mesh",className:"text-2xl font-bold text-foreground mb-3 scroll-mt-24",children:"Seed · Mesh"}),e.jsxs("p",{className:"text-sm text-muted-foreground mb-3",children:["Phase 2 observability surface â four allowlisted reads on v0.20.0+. All four shapes carry a"," ",e.jsx("code",{className:"font-mono text-xs",children:"#[serde(flatten)] extras"})," sink so newer firmwares don't break the SDK."]}),e.jsx(t,{tabs:[{label:"Status",lang:"rust",code:`let m = client.mesh().status().await?; 497println!("ap_active={}, auto_mesh={}, peer_count={}", 498 m.ap_active, m.auto_mesh, m.peer_count);`},{label:"Peers",lang:"rust",code:`let p = client.mesh().peers().await?; 499println!("count={}, discovery={}", p.count, p.discovery_active); 500for raw in &p.peers { 501 println!(" {raw}"); // shape free-form on the wire today 502}`},{label:"Swarm + cluster",lang:"rust",code:`let s = client.mesh().swarm_status().await?; 503println!("swarm: epoch={} peers={} vectors={} uptime={}s", 504 s.epoch, s.peer_count, s.total_vectors, s.uptime_secs); 505 506let c = client.mesh().cluster_health().await?; 507println!("cluster_enabled={}, auto_sync={}s, last_attempt={}", 508 c.cluster_enabled, c.auto_sync_interval_secs, c.last_sync_attempt);`}]})]}),e.jsxs("section",{children:[e.jsx("h2",{id:"discovery",className:"text-2xl font-bold text-foreground mb-3 scroll-mt-24",children:"Discovery providers"}),e.jsxs("p",{className:"text-sm text-muted-foreground mb-3",children:["Pluggable peer discovery via the ",e.jsx("code",{className:"font-mono text-xs",children:"Discovery"})," trait. Pass a provider via ",e.jsx("code",{className:"font-mono text-xs",children:".discovery(...)"})," instead of (or alongside)"," ",e.jsx("code",{className:"font-mono text-xs",children:".endpoints(...)"}),"; the builder calls"," ",e.jsx("code",{className:"font-mono text-xs",children:"discover()"})," at construction, and"," ",e.jsx("code",{className:"font-mono text-xs",children:"SeedClient::rediscover()"})," re-runs it. Three providers ship today."]}),e.jsx(t,{tabs:[{label:"Explicit",lang:"rust",code:`use cognitum_one::seed::{Explicit, SeedClient, SeedTls}; 509 510let discovery = Explicit::new(&[ 511 "https://10.0.0.10:8443", 512 "https://10.0.0.11:8443", 513]); 514 515let client = SeedClient::builder() 516 .discovery(discovery) 517 .tls(SeedTls::Insecure) 518 .build()?;`},{label:"mDNS",lang:"rust",code:`// Requires --features seed,mdns 519#[cfg(feature = "mdns")] 520{ 521 use cognitum_one::seed::{MdnsDiscovery, SeedClient, SeedTls}; 522 use std::time::Duration; 523 524 let discovery = MdnsDiscovery::builder() 525 .service_type("_cognitum._tcp.local.") 526 .browse_duration(Duration::from_secs(2)) 527 .build(); 528
529 let client = SeedClient::builder() 530 .discovery(discovery) 531 .tls(SeedTls::System) // fp= pins from TXT records strict-checked 532 .build()?; 533}`},{label:"Tailscale",lang:"rust",code:`// Always available â only depends on std::process::Command. 534// Shells out to \`tailscale status --json\` and walks Peer{}. 535use cognitum_one::seed::{SeedClient, SeedTls, TailscaleDiscovery}; 536 537let discovery = TailscaleDiscovery::new() 538 .with_prefix("cognitum-") // default 539 .with_port(8443); // default 540 541let client = SeedClient::builder() 542 .discovery(discovery) 543 .tls(SeedTls::System) 544 .build()?; 545 546// Predicates (kept-or-dropped) â useful when your tailnet has 547// non-cognitum hosts whose names happen to match the prefix. 548let custom = TailscaleDiscovery::new() 549 .with_predicate(|p| { 550 p.online.unwrap_or(false) 551 && p.host_name.as_deref().is_some_and(|h| h.starts_with("seed-")) 552 });`},{label:"Custom provider",lang:"rust",code:`use async_trait::async_trait; 553use cognitum_one::seed::{DiscoveredPeer, Discovery}; 554use cognitum_one::Error; 555 556#[derive(Debug)] 557struct ConsulDiscovery { /* ... */ } 558 559#[async_trait] 560impl Discovery for ConsulDiscovery { 561 async fn discover(&self) -> Result<Vec<DiscoveredPeer>, Error> { 562 // hit Consul's /v1/catalog/service/cognitum-seed 563 Ok(vec![ 564 DiscoveredPeer::new("https://10.0.0.10:8443") 565 .with_device_id("seed-a") 566 .with_latency_ms(12), 567 ]) 568 } 569}`},{label:"Rediscover",lang:"rust",code:`// Re-run the configured Discovery provider. New peers join the 570// PeerSet in Healthy state; peers no longer in the discovered list 571// are dropped. TokenBook entries for dropped peers are KEPT â a 572// transient discovery flap won't lose pairings. Session pins 573// survive if the pinned URL is still in the new list. 574client.rediscover().await?; 575 576// Without a Discovery provider configured, rediscover() resets 577// every existing peer to Healthy and clears latency EMAs / last_used_at. 578// No network I/O.`}]})]}),e.jsxs("section",{children:[e.jsx("h2",{id:"tokens",className:"text-2xl font-bold text-foreground mb-3 scroll-mt-24",children:"Tokens (redaction-safe)"}),e.jsxs("p",{className:"text-sm text-muted-foreground mb-3",children:["Pairing tokens are wrapped in ",e.jsx("code",{className:"font-mono text-xs",children:"SecretString"}),". The custom ",e.jsx("code",{className:"font-mono text-xs",children:"Debug"})," impl prints"," ",e.jsx("code",{className:"font-mono text-xs",children:"SecretString(<redacted, N bytes>)"}),"; the underlying buffer is best-effort zeroed on ",e.jsx("code",{className:"font-mono text-xs",children:"Drop"}),". Use"," ",e.jsx("code",{className:"font-mono text-xs",children:".as_str()"})," only at the request boundary."]}),e.jsx(t,{tabs:[{label:"SecretString",lang:"rust",code:`use cognitum_one::seed::SecretString; 579 580let tok = SecretString::new(std::env::var("SEED_BEARER")?); 581println!("{tok:?}"); // SecretString(<redacted, 64 bytes>) 582let bytes = tok.as_str(); // explicit unwrap â use sparingly`},{label:"InMemoryTokenBook",lang:"rust",code:`use cognitum_one::seed::{InMemoryTokenBook, SecretString, TokenBook}; 583 584let mut book = InMemoryTokenBook::new(); 585book.set("https://10.0.0.10:8443", SecretString::new("bearer-1")); 586book.set("https://10.0.0.11:8443", SecretString::new("bearer-2")); 587 588// Trailing slashes are normalized. 589assert_eq!( 590 book.get("https://10.0.0.10:8443/").unwrap().as_str(), 591 "bearer-1", 592); 593 594// Or build from any (peer, token) iterator: 595let book = InMemoryTokenBook::from_map([ 596 ("https://a:8443", "tok-a"), 597 ("https://b:8443", "tok-b"), 598]);`},{label:"Custom TokenBook (keychain)",lang:"rust",code:`use cognitum_one::seed::{SecretString, TokenBook}; 599use std::fmt; 600 601// Implement TokenBook to back tokens with the OS keychain, an 602// encrypted file, etc. The SDK calls get/set/delete under a Mutex 603// so single-threaded impls are fine. 604#[derive(Debug, Default)] 605struct KeychainBook; 606 607impl TokenBook for KeychainBook { 608 fn get(&self, peer_url: &str) -> Option<SecretString> { 609 // keychain.find(peer_url).ok().map(SecretString::new) 610 None 611 } 612 fn set(&mut self, peer_url: &str, token: SecretString) { 613 // keychain.set(peer_url, token.as_str()) 614 let _ = (peer_url, token); 615 } 616 fn delete(&mut self, peer_url: &str) { 617 // keychain.delete(peer_url) 618 let _ = peer_url; 619 } 620}`}]})]}),e.jsxs("section",{children:[e.jsx("h2",{id:"errors",className:"text-2xl font-bold text-foreground mb-3 scroll-mt-24",children:"Errors"}),e.jsxs("p",{className:"text-sm text-muted-foreground mb-3",children:["A single ",e.jsx("code",{className:"font-mono text-xs",children:"Error"})," enum (",e.jsx("code",{className:"font-mono text-xs",children:"cognitum_one::Error"}),") captures every failure shape. Variants are stable;
620 the seed module encodes additional metadata (auth reason, validation prefix, etc.) into the existing ",e.jsx("code",{className:"font-mono text-xs",children:"String"})," payloads via documented prefixes (",e.jsx("code",{className:"font-mono text-xs",children:"not_paired:"}),","," ",e.jsx("code",{className:"font-mono text-xs",children:"trust_score_blocked:"}),","," ",e.jsx("code",{className:"font-mono text-xs",children:"unsupported:"}),", ",e.jsx("code",{className:"font-mono text-xs",children:"tls_pin:"}),")."]}),e.jsx("div",{className:"border border-border/50 rounded-xl bg-card/30 overflow-hidden mb-4",children:e.jsxs("table",{className:"w-full text-sm",children:[e.jsx("thead",{className:"bg-card/50 text-muted-foreground text-xs uppercase tracking-wider",children:e.jsxs("tr",{children:[e.jsx("th",{className:"text-left px-4 py-2 font-medium",children:"Variant"}),e.jsx("th",{className:"text-left px-4 py-2 font-medium",children:"When"})]})}),e.jsx("tbody",{children:[["Error::Auth(String)","401 / 403. Seed module prefixes with a reason: invalid_credentials, not_paired, pairing_window_closed, lockdown_mtls_required, trust_score_blocked."],["Error::RateLimit { retry_after_ms }","429. retry_after_ms is parsed from Retry-After header, body retry_after_us, body 'retry after Ns', or ADR-0005 equal-jitter fallback. Use err.retry_after() for a Duration."],["Error::Validation(String)","400 / 422 / 405. Also fired client-side for bad config. Seed module prefixes: 'config:', 'unsupported:', 'tls_pin:', 'not_implemented:'."],["Error::NotFound(String)","404. Seed module includes the request path in the message."],["Error::Api { code, message }","Catch-all for unmapped HTTP statuses (incl. 503 Service Unavailable). For seed transport: code=0 + message='seed: â¦' on transport-exhausted / lock-poisoned paths."],["Error::Http(reqwest::Error)","Underlying transport â DNS failure, connect refused, TLS handshake. Auto-converted via #[from]."],["Error::Json(serde_json::Error)","Response wasn't valid JSON or didn't deserialize into the expected shape. Auto-converted via #[from]."]].map(([s,i],a)=>e.jsxs("tr",{className:a%2?"bg-card/20":"",children:[e.jsx("td",{className:"px-4 py-2 align-top",children:e.jsx("code",{className:"font-mono text-xs text-primary",children:s})}),e.jsx("td",{className:"px-4 py-2 text-muted-foreground",children:i})]},s))})]})}),e.jsx(t,{tabs:[{label:"Match",lang:"rust",code:`use cognitum_one::Error; 621 622match client.orders().status("ord_does_not_exist").await { 623 Ok(orders) => println!("{} orders", orders.len()), 624 Err(Error::NotFound(msg)) => println!("no such order: {msg}"), 625 Err(Error::Auth(msg)) => println!("bad api key: {msg}"), 626 Err(Error::RateLimit { retry_after_ms }) => { 627 println!("slow down; retry in {retry_after_ms}ms"); 628 // Or use err.retry_after() to get a Duration. 629 } 630 Err(Error::Validation(msg)) => println!("bad request: {msg}"), 631 Err(Error::Http(e)) => println!("net error: {e}"), 632 Err(Error::Json(e)) => println!("decode error: {e}"), 633 Err(Error::Api { code, message }) => println!("API {code}: {message}"), 634}`},{label:"Seed prefix detection",lang:"rust",code:`use cognitum_one::Error; 635 636// Seed-specific reasons are encoded as documented prefixes inside the 637// existing String variants. Detect them with starts_with â the prefixes 638// are stable across SDK versions. 639match client.store().ingest(req).await { 640 Err(Error::Auth(msg)) if msg.starts_with("not_paired:") => { 641 eprintln!("open a pairing window first"); 642 } 643 Err(Error::Auth(msg)) if msg.starts_with("trust_score_blocked:") => { 644 // 3rd consecutive auth failure on this peer â the seed has 645 // locked us out. Don't retry. 646 eprintln!("peer locked us out: {msg}"); 647 } 648 Err(Error::Validation(msg)) if msg.starts_with("tls_pin:") => { 649 eprintln!("cert fingerprint mismatch: {msg}"); 650 } 651 Err(Error::Validation(msg)) if msg.starts_with("unsupported:") => { 652 eprintln!("seed cannot do this: {msg}"); 653 } 654 Err(Error::Validation(msg)) if msg.starts_with("not_implemented:") => { 655 eprintln!("endpoint missing on this firmware: {msg}"); 656 } 657 other => { 658 let _ = other?; 659 } 660}`},{label:"Retry-After helper",lang:"rust",code:`use cognitum_one::Error; 661 662if let Err(err) = client.health().await { 663 if let Some(delay) = err.retry_after() { 664 // Wait the server-suggested delay, then retry. 665 tokio::time::sleep(delay).await; 666 let _ = client.health().await; 667 } else { 668 return Err(err); 669 } 670}`}]})]}),e.jsxs("section",{children:[e.jsx("h2",{id:"features",className:"text-2xl font-bold text-foreground mb-3 scroll-mt-24",children:"Cargo features"}),e.jsx("div",{className:"border border-border/50 rounded-xl bg-card/30 overflow-hidden",children:e.jsxs("table",{className:"w-full text-sm",children:[e.jsx("thead",{className:"bg-card/50 text-muted-foreground text-xs uppercase tracking-wider",children:e.jsxs("tr",{children:[e.jsx("th",{className:"text-left px-4 py-2 font-medium",children:"Feature"}),e.jsx("th",{className:"text-left px-4 py-2 font-medium",children:"Default"}),e.jsx("th",{className:"text-left px-4 py-2 font-medium",children:"Effect"})]})}),e.jsx("tbody",{children:[["rustls","yes","rustls TLS via reqwest/rustls-tls. Rust-native, no OpenSSL dependency."],["native-tls","no","Use the platform's TLS stack instead of rustls (SChannel on Windows, SecureTrans
670port on macOS, OpenSSL on Linux). Pair with default-features = false."],["seed","no","Enables the cognitum_one::seed module â SeedClient, mesh routing, TLS pinning, CallOptions, Discovery trait, TokenBook, SeedSession, SecretString. Pulls in url for endpoint parsing."],["mdns","no","mDNS / DNS-SD discovery via mdns-sd (one-shot _cognitum._tcp.local. browse). Implies seed."],["stream","no","SSE streaming via eventsource-stream for the seed delta-stream endpoint. Implies seed."],["blocking","no","reqwest's blocking client (no tokio runtime needed for cloud calls)."],["live-seed-tests","no","Opt-in integration tests that hit a real seed at $SEED_URL. Implies seed. CI-only."]].map(([s,i,a],u)=>e.jsxs("tr",{className:u%2?"bg-card/20":"",children:[e.jsx("td",{className:"px-4 py-2 align-top whitespace-nowrap",children:e.jsx("code",{className:"font-mono text-xs text-primary",children:s})}),e.jsx("td",{className:"px-4 py-2 align-top text-muted-foreground whitespace-nowrap",children:i}),e.jsx("td",{className:"px-4 py-2 text-muted-foreground",children:a})]},s))})]})}),e.jsxs("p",{className:"text-[12px] text-muted-foreground mt-3",children:["The Tailscale discovery provider has ",e.jsx("em",{children:"no"})," feature flag â it only depends on"," ",e.jsx("code",{className:"font-mono text-xs",children:"std::process::Command"})," and"," ",e.jsx("code",{className:"font-mono text-xs",children:"tokio::task::spawn_blocking"}),", both unconditionally available under ",e.jsx("code",{className:"font-mono text-xs",children:"--features seed"}),". On platforms without a"," ",e.jsx("code",{className:"font-mono text-xs",children:"tailscale"})," binary on PATH, calls return"," ",e.jsx("code",{className:"font-mono text-xs",children:"Error::Validation"}),"."]})]}),e.jsxs("section",{children:[e.jsx("h2",{id:"related",className:"text-2xl font-bold text-foreground mb-3 scroll-mt-24",children:"Related"}),e.jsxs("ul",{className:"text-sm text-muted-foreground space-y-2 list-disc pl-5",children:[e.jsxs("li",{children:[e.jsx(n,{to:"/sdks/javascript",className:"text-primary hover:underline",children:"JavaScript SDK"})," â same surface, Node.js / TypeScript, mDNS / USB / LAN discovery."]}),e.jsxs("li",{children:[e.jsx(n,{to:"/sdks/python",className:"text-primary hover:underline",children:"Python SDK"})," â sync + async with the same shape."]}),e.jsxs("li",{children:[e.jsx(n,{to:"/sdks/claude-code",className:"text-primary hover:underline",children:"Claude Code plugins"})," â marketplace plugins for cloud + seed."]}),e.jsxs("li",{children:[e.jsx(n,{to:"/api",className:"text-primary hover:underline",children:"REST API reference"})," â underlying HTTP surface."]})]}),e.jsxs("div",{className:"mt-5 flex flex-wrap gap-2",children:[e.jsxs("a",{href:"https://crates.io/crates/cognitum-one",target:"_blank",rel:"noopener noreferrer",className:"inline-flex items-center gap-2 px-3 py-2 rounded-lg border border-border/50 bg-card/50 hover:border-border text-sm text-foreground transition-colors",children:[e.jsx(l,{className:"h-4 w-4"}),"crates.io/crates/cognitum-one",e.jsx(c,{className:"h-3 w-3 text-muted-foreground"})]}),e.jsxs("a",{href:"https://docs.rs/cognitum-one",target:"_blank",rel:"noopener noreferrer",className:"inline-flex items-center gap-2 px-3 py-2 rounded-lg border border-border/50 bg-card/50 hover:border-border text-sm text-foreground transition-colors",children:[e.jsx(c,{className:"h-4 w-4"}),"docs.rs/cognitum-one"]})]})]})]})})})}),e.jsx(h,{})]})]})}export{le as SdkRustPage,le as default};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.