An idiomatic Rust client library for SpiceDB, the open-source, Google Zanzibar-inspired authorization system.
- Type-safe: Wraps the SpiceDB gRPC API with idiomatic Rust types — no raw protobufs
- Async-first: Built on tonic and tokio
- 3-state permissions:
PermissionResultcorrectly modelsAllowed,Denied, andConditional(caveated) outcomes - Streaming: All streaming RPCs return
impl Stream<Item = Result<T, Error>> - Shareable:
ClientisClone + Send + Sync— clone it freely across tasks - Feature-gated:
watch,experimental,serde, TLS backends
use prescience::{Client, ObjectReference, SubjectReference, Consistency, PermissionResult}; #[tokio::main] async fn main() -> Result<(), prescience::Error> { let client = Client::new("http://localhost:50051", "my-token").await?; let result = client .check_permission( &ObjectReference::new("document", "doc-123")?, "view", &SubjectReference::new(ObjectReference::new("user", "alice")?, None::<String>)?, ) .consistency(Consistency::FullyConsistent) .await?; match result { PermissionResult::Allowed => println!("access granted"), PermissionResult::Denied => println!("access denied"), PermissionResult::Conditional { missing_fields } => { println!("need caveat context: {:?}", missing_fields); } } Ok(()) }
use prescience::{Client, ObjectReference, SubjectReference, Relationship, RelationshipUpdate}; # async fn example(client: &Client) -> Result<(), prescience::Error> { let token = client .write_relationships(vec![ RelationshipUpdate::create(Relationship::new( ObjectReference::new("document", "doc-123")?, "viewer", SubjectReference::new(ObjectReference::new("user", "bob")?, None::<String>)?, )), ]) .await?; // Use the token for subsequent reads let result = client .check_permission( &ObjectReference::new("document", "doc-123")?, "view", &SubjectReference::new(ObjectReference::new("user", "bob")?, None::<String>)?, ) .consistency(prescience::Consistency::AtLeastAsFresh(token)) .await?; # Ok(()) # }
use tokio_stream::StreamExt; # async fn example(client: &prescience::Client) -> Result<(), prescience::Error> { let subject = prescience::SubjectReference::new( prescience::ObjectReference::new("user", "alice")?, None::<String>, )?; let mut stream = client .lookup_resources("document", "view", &subject) .consistency(prescience::Consistency::FullyConsistent) .send() .await?; while let Some(result) = stream.next().await { let item = result?; println!("resource: {}, permission: {:?}", item.resource_id, item.permission); } # Ok(()) # }
| Feature | Default | Description |
|---|---|---|
watch |
No | WatchService for streaming relationship changes |
experimental |
No | Bulk APIs: BulkCheckPermission, BulkImport/Export |
serde |
No | Serialize/Deserialize on ZedToken and domain types |
tls-rustls |
No | Use rustls for TLS |
tls-native |
No | Use native/system TLS |
All methods return Result<T, prescience::Error>. The error type provides:
- Structured matching:
Error::Transport,Error::Status,Error::InvalidArgument, etc. - Retryability:
error.is_retryable()returnstrueforUNAVAILABLEandDEADLINE_EXCEEDED - gRPC code access:
error.code()returns the gRPC status code
This project uses devenv for a reproducible development environment:
# Enter the dev shell (provides Rust, protoc, SpiceDB) devenv shell # Build cargo build --all-features # Test cargo test --all-features # Lint cargo clippy --all-features -- -D warnings # Start a local SpiceDB for integration testing spicedb serve --grpc-preshared-key "test-key" --datastore-engine memory
Apache-2.0