Zero-copy, memory-safe AVP implementation for high-performance applications
Efficient secret handling without unnecessary allocations
Secrets are securely zeroed on drop with zeroize
Full async support with tokio runtime
Strong typing prevents credential misuse
# Cargo.toml
[dependencies]
avp = "0.1"
# With all backends
avp = { version = "0.1", features = ["file", "keychain"] }
# Async support
avp = { version = "0.1", features = ["async"] }
use avp::{Client, FileBackend, Result};
fn main() -> Result<()> {
// Create client with file backend
let backend = FileBackend::new("./vault.enc", "your-password")?;
let client = Client::new(backend);
// Authenticate to workspace
let session = client.authenticate("my-agent", None)?;
// Store a secret
client.store(
&session,
"OPENAI_API_KEY",
b"sk-...",
Default::default()
)?;
// Retrieve a secret
let secret = client.retrieve(&session, "OPENAI_API_KEY", None)?;
println!("Key: {}", String::from_utf8_lossy(&secret.value));
Ok(())
}
use avp::{AsyncClient, FileBackend, Result};
#[tokio::main]
async fn main() -> Result<()> {
let backend = FileBackend::new("./vault.enc", "password")?;
let client = AsyncClient::new(backend);
let session = client.authenticate("workspace", None).await?;
client.store(&session, "KEY", b"value", Default::default()).await?;
let secret = client.retrieve(&session, "KEY", None).await?;
Ok(())
}
In-memory storage for testing.
use avp::{Client, MemoryBackend};
let backend = MemoryBackend::new();
let client = Client::new(backend);
Encrypted file storage using AES-256-GCM with Argon2id key derivation.
use avp::FileBackend;
// Create with password
let backend = FileBackend::new("./vault.enc", "password")?;
// Create with custom options
let backend = FileBackend::builder()
.path("./vault.enc")
.password("password")
.argon2_memory_cost(65536)
.argon2_time_cost(3)
.build()?;
OS-native credential storage (requires keychain feature).
use avp::KeychainBackend;
let backend = KeychainBackend::new("my-app-avp")?;
let info = client.discover()?;
println!("Version: {}", info.version);
println!("Backends: {:?}", info.backends);
println!("Operations: {:?}", info.operations);
use std::time::Duration;
// Default TTL (1 hour)
let session = client.authenticate("workspace", None)?;
// Custom TTL
let session = client.authenticate(
"workspace",
Some(Duration::from_secs(7200))
)?;
println!("Session ID: {}", session.id);
println!("Expires: {:?}", session.expires_at);
use avp::StoreOptions;
use std::collections::HashMap;
// Simple store
client.store(&session, "API_KEY", b"sk-...", Default::default())?;
// With options
let mut labels = HashMap::new();
labels.insert("env".to_string(), "production".to_string());
let options = StoreOptions {
labels: Some(labels),
expires_at: None,
};
client.store(&session, "API_KEY", b"sk-...", options)?;
// Latest version
let secret = client.retrieve(&session, "API_KEY", None)?;
// Specific version
let secret = client.retrieve(&session, "API_KEY", Some(1))?;
println!("Value: {:?}", secret.value);
println!("Version: {}", secret.version);
println!("Labels: {:?}", secret.labels);
client.delete(&session, "OLD_KEY")?;
use avp::ListOptions;
// List all
let secrets = client.list(&session, Default::default())?;
// With label filter
let mut filter = HashMap::new();
filter.insert("env".to_string(), "production".to_string());
let options = ListOptions {
label_filter: Some(filter),
};
let secrets = client.list(&session, options)?;
for secret in secrets {
println!("{}: v{}", secret.name, secret.version);
}
let result = client.rotate(&session, "API_KEY", b"sk-new-...")?;
println!("Previous: v{}", result.previous_version);
println!("New: v{}", result.new_version);
use avp::{Error, ErrorKind};
match client.retrieve(&session, "UNKNOWN") {
Ok(secret) => println!("Found: {:?}", secret),
Err(Error { kind: ErrorKind::NotFound, .. }) => {
println!("Secret not found");
}
Err(Error { kind: ErrorKind::SessionExpired, .. }) => {
println!("Session expired, re-authenticate");
}
Err(e) => {
println!("Error: {}", e);
}
}
zeroize crate to securely clear secrets from memory when they go out of scope.
use avp::SecretValue;
{
let secret = client.retrieve(&session, "KEY", None)?;
// Use secret.value...
} // Secret is securely zeroed here
// For manual control:
use zeroize::Zeroize;
let mut sensitive_data = secret.value.clone();
// Use sensitive_data...
sensitive_data.zeroize(); // Explicitly zero
use avp::{Backend, Session, Secret, StoreOptions, ListOptions, Result};
struct MyBackend {
// Your storage implementation
}
impl Backend for MyBackend {
fn discover(&self) -> Result {
// Return capabilities
}
fn authenticate(&self, workspace: &str, ttl: Option) -> Result {
// Create session
}
fn store(&self, session: &Session, name: &str, value: &[u8], options: StoreOptions) -> Result<()> {
// Store secret
}
fn retrieve(&self, session: &Session, name: &str, version: Option) -> Result {
// Retrieve secret
}
fn delete(&self, session: &Session, name: &str) -> Result<()> {
// Delete secret
}
fn list(&self, session: &Session, options: ListOptions) -> Result> {
// List secrets
}
fn rotate(&self, session: &Session, name: &str, new_value: &[u8]) -> Result {
// Rotate secret
}
}
| Feature | Description |
|---|---|
default | Memory backend only |
file | Encrypted file backend |
keychain | OS keychain backend |
async | Async/await support with tokio |
serde | Serde serialization support |
use avp::{Client, FileBackend, StoreOptions, Result};
use std::collections::HashMap;
fn main() -> Result<()> {
// Initialize
let backend = FileBackend::new("./agent-vault.enc", "secure-password")?;
let client = Client::new(backend);
// Authenticate
let session = client.authenticate("openai-agent", None)?;
// Store with labels
let mut labels = HashMap::new();
labels.insert("provider".to_string(), "openai".to_string());
labels.insert("env".to_string(), "production".to_string());
client.store(
&session,
"OPENAI_API_KEY",
b"sk-proj-...",
StoreOptions { labels: Some(labels), expires_at: None }
)?;
// Retrieve and use
let secret = client.retrieve(&session, "OPENAI_API_KEY", None)?;
let api_key = String::from_utf8_lossy(&secret.value);
// Use api_key with OpenAI client...
println!("Using API key v{}", secret.version);
// Rotate when needed
let result = client.rotate(&session, "OPENAI_API_KEY", b"sk-new-...")?;
println!("Rotated from v{} to v{}", result.previous_version, result.new_version);
Ok(())
}