Rust Integration Tutorial
This tutorial walks you through adding SCHEMABOUND to a Rust project and spinning up a local gRPC server on port 50051. By the end, you will have a running instance that can introspect SQLite databases and enforce policy on queries.
Installation
Add schemabound to your Cargo.toml:
[dependencies]
schemabound = "0.6"
tokio = { version = "1", features = ["full"] }
Start a gRPC Server
use schemabound::grpc_executor::GrpcExecutor;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let executor = GrpcExecutor::new("path/to/database.db")?;
let handle = executor.start_server("0.0.0.0:50051").await?;
handle.await?;
Ok(())
}
Introspect a SQLite Database
#![allow(unused)]
fn main() {
use schemabound::mirror::introspect_sqlite_path;
let schema = introspect_sqlite_path("path/to/database.db")?;
for table in &schema.tables {
println!("Table: {}", table.name);
for col in &table.columns {
println!(" Column: {} ({}){}", col.name, col.sql_type,
if col.primary_key { " PK" } else { "" });
}
for trigger in &table.triggers {
println!(" Trigger: {} {} {}", trigger.name, trigger.timing, trigger.event);
}
}
println!("UDTs: {}", schema.user_defined_types.len());
}
Use The MirrorProvider Trait
MirrorProvider is an async trait for pluggable schema introspection:
#![allow(unused)]
fn main() {
use schemabound::{MirrorProvider, SqliteMirrorProvider};
let provider = SqliteMirrorProvider::new("database.db");
let schema = provider.introspect_schema().await?;
}
Implement the trait to integrate custom databases:
#![allow(unused)]
fn main() {
use schemabound::MirrorProvider;
use schemabound::mirror::SchemaModel;
struct MyCustomProvider;
#[async_trait::async_trait]
impl MirrorProvider for MyCustomProvider {
async fn introspect_schema(&self) -> Result<SchemaModel, String> {
// custom introspection logic
Ok(SchemaModel { tables: vec![], user_defined_types: vec![] })
}
}
}
Register a CoR Handler
Handlers are invoked in registration order for every dispatched event. Use the built-in collection from schemabound::handlers or implement EventHandler yourself:
#![allow(unused)]
fn main() {
use schemabound::{get_event_bus, AuditLogHandler, DefaultHandlerChain, SharedHandler};
use std::sync::Arc;
let chain = DefaultHandlerChain::new();
let bus = get_event_bus();
bus.register_handler(Box::new(AuditLogHandler))?;
bus.register_handler(Box::new(SharedHandler(Arc::clone(&chain.query_metrics))))?;
bus.register_handler(Box::new(SharedHandler(Arc::clone(&chain.session_activity))))?;
let snap = chain.query_metrics.snapshot();
let sessions = chain.session_activity.session_count();
}
Subscribe to Events (fire-and-forget)
#![allow(unused)]
fn main() {
use schemabound::interceptor::{get_event_bus, Event};
let bus = get_event_bus();
let id = bus.register_subscriber(Box::new(|event: &Event| {
println!("Event: {}", event.event_type());
}))?;
bus.unregister_subscriber(id)?;
}
Apply a Policy to a Query
#![allow(unused)]
fn main() {
use schemabound::executor::{QueryServiceImpl, ValidateQueryRequest};
use schemabound::policy_engine::{
AuthorizationContext, AuthorizedSubqueryShape, PolicyContext, SubqueryPolicy, ToolContract, ToolIntent,
};
let mut service = QueryServiceImpl::new();
service.set_db_path("database.db")?;
let request = ValidateQueryRequest {
db_identifier: "db".into(),
query: "SELECT id FROM users WHERE org_id IN (SELECT id FROM organizations)".into(),
parameters: Default::default(),
};
let policy = PolicyContext {
tool: ToolContract {
name: "list-users".into(),
intent: ToolIntent::ReadSelect,
subquery_policy: SubqueryPolicy::AllowListed(vec![
AuthorizedSubqueryShape { table: "organizations".into() },
]),
},
authorization: AuthorizationContext {
allowed_intents: vec![ToolIntent::ReadSelect],
grants: vec!["tool:users.read".into()],
},
};
let response = service.validate_query_with_policy(request, policy).await?;
assert!(response.valid);
}
Next Steps
- Reference: See Rust SDK Reference for crate structure, error handling, and gRPC proto mapping.
- Event Pipeline: See Event Pipeline architecture guide for the full handler reference.
- Contributing: See Contribution Workflow for the TDD contract.