Skip to content

Embedding the Proxy

The liter-llm-proxy crate exports ProxyServer as a library, so you can mount the proxy inside a larger Rust binary and control authentication and usage tracking directly.

Build and serve the proxy with custom authentication:

use std::sync::Arc;
use liter_llm_proxy::{ProxyServer, config::ProxyConfig};
use liter_llm::tenant::InMemoryKeyResolver;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = ProxyConfig::from_file("liter-llm-proxy.toml")?;
// Create a custom key resolver (in-memory for this example)
let resolver = Arc::new(InMemoryKeyResolver::with_entries(vec![
(
"sk-my-api-key".to_string(),
liter_llm::tenant::ResolvedKey {
tenant_id: "tenant-123".into(),
allowed_models: vec!["openai/gpt-4o".to_string()],
monthly_budget: None,
currency: None,
metadata: std::collections::HashMap::new(),
active: true,
},
),
]));
// Build and serve with the custom resolver
ProxyServer::new(config)
.with_key_resolver(resolver)
.serve_with_shutdown(None)
.await?;
Ok(())
}

Override the default KeyStore-backed key resolver:

pub fn with_key_resolver(
mut self,
resolver: Arc<dyn KeyResolver>
) -> Self

When set, the supplied resolver is used instead of the built-in KeyStore constructed from ProxyConfig.keys. Use this to plug in database-backed or remote resolvers without forking AppState.

Attach a custom usage sink:

pub fn with_usage_sink<S: UsageSink>(
mut self,
sink: Arc<S>
) -> Self

When set, HooksLayer is pushed outermost in the Tower middleware stack so every completed request (success or error) emits a UsageEvent to the sink. The sink receives request metadata, token counts, latency, and cache state. Default behaviour (no sink) is unchanged when unset.

Combine key resolver and usage sink overrides:

use std::sync::Arc;
use liter_llm_proxy::ProxyServer;
use liter_llm::observability::LoggingUsageSink;
use liter_llm::tenant::InMemoryKeyResolver;
let config = ProxyConfig::from_file("liter-llm-proxy.toml")?;
let resolver = Arc::new(InMemoryKeyResolver::new(my_keys));
let sink = Arc::new(LoggingUsageSink);
ProxyServer::new(config)
.with_key_resolver(resolver)
.with_usage_sink(sink)
.serve_with_shutdown(None)
.await?;

Embed the proxy when you:

  • Control a larger Rust microservice and want the proxy to run in-process
  • Need fine-grained control over auth without implementing a separate HTTP service
  • Want to emit usage events to a custom backend or observability system
  • Already have credential and storage infrastructure to wire in

For standalone deployments, run liter-llm api as a separate binary.

When neither builder is called, the proxy uses:

  • Built-in KeyStore loaded from ProxyConfig.keys (static or etcd-synced)
  • No usage sink (requests complete without emission)

Calling the builders is always optional.