arche
The opinionated backend foundation for Axum applications.
Cloud integrations, databases, auth, LLM inference, tool-calling agents, encryption, streaming JSON/CSV, WebSockets, and structured error handling — wired up and ready to go.
arche sits around Axum, not in place of it.
Getting Started · Modules · API Reference · Design Principles
Why arche?
Every backend service re-implements the same infrastructure plumbing — cloud SDK setup, database pools, auth primitives, error handling, config resolution. arche bundles these into a single, cohesive Rust crate built on well-established libraries so you can skip the boilerplate and focus on business logic.
Getting Started
Add arche to your Cargo.toml:
[]
= "2.5.0"
Modules
| Module | What it does |
|---|---|
aws |
S3, SES, and KMS via official AWS SDKs |
gcp |
Google Drive, Sheets, and Vertex AI (Gemini + Claude) |
llm |
Canonical LLM types + LlmProvider trait — backend-agnostic |
agent |
Tool-calling agent engine, session state, SSE streaming |
database |
Postgres and Redis connection pooling with health checks |
jwt |
HS256 token generation, verification, and expiry helpers |
csv |
Async CSV read/write — batch, streaming, and from URL |
json |
Streaming JSON array parsing with metadata extraction |
crypto |
AES-128-CBC encryption with PBKDF2 key derivation |
sockets |
WebSocket connection registry with broadcast |
error |
Axum-compatible structured error responses (400–503) |
utils |
Timestamp validation, date/time conversions, pagination |
Every service module exports a config builder so you can wire up credentials programmatically — or omit it entirely and let arche resolve everything from environment variables.
// Pass None to resolve entirely from env vars
let pool = get_pg_pool.await?;
// Or configure explicitly
let config = default
.host
.port
.build;
let pool = get_pg_pool.await?;
All components are modular and explicit — nothing is hidden or magical.
API Reference
AWS
AWS SDK integrations built on official SDKs. Default region: ap-south-1.
S3
use ;
// From env vars
let client = get_s3_client.await?;
// Or with explicit config
let config = default
.credential_source
.access_key_id
.secret_access_key
.build;
let client = get_s3_client.await?;
| Env Var | Description |
|---|---|
S3_CRED_SOURCE |
"IAM" (default) or "env" |
S3_ACCESS_KEY_ID |
Required when source is "env" |
S3_SECRET_ACCESS_KEY |
Required when source is "env" |
S3_REGION |
AWS region (default: ap-south-1) |
KMS
use KMSClient;
// Default region
let kms = new_with_region.await;
// Encrypt / decrypt
let ciphertext = kms.encrypt.await?;
let plaintext = kms.decrypt.await?;
// Decrypt base64-encoded ciphertext directly
let plaintext = kms.decrypt_base64.await?;
| Env Var | Description |
|---|---|
AWS_REGION |
AWS region (default: ap-south-1) |
SES
use SESClient;
let ses = new_with_region.await;
// Plain email (with optional HTML body)
let message_id = ses.send_email.await?;
// Templated email
let message_id = ses.send_templated_email.await?;
| Env Var | Description |
|---|---|
AWS_REGION |
AWS region (default: ap-south-1) |
GCP
Google Cloud Platform integrations using service account authentication.
Drive
use ;
let drive = get_drive_client.await?;
| Env Var | Description |
|---|---|
GCP_DRIVE_KEY |
Path to service account JSON key file |
Sheets
use ;
let sheets = get_sheets_client.await?;
| Env Var | Description |
|---|---|
GCP_SHEETS_KEY |
Path to service account JSON key file |
Vertex AI
VertexClient implements arche::llm::LlmProvider for Gemini and
Anthropic Claude models on Google Cloud. The provider (Gemini or Anthropic) is
captured at construction; the model is specified per-request.
use ;
use ;
// Create a client bound to Gemini
let client = get_vertex_client.await?;
// Or with explicit config (Anthropic requires service account auth)
let client = get_vertex_client.await?;
let request = new
.with_system
.with_max_tokens
.with_temperature;
// Non-streaming
let response = client.generate.await?;
println!;
println!;
// Streaming
use StreamExt;
let mut stream = client.stream_generate.await?;
while let Some = stream.next.await
Function calling (typed schemas via arche::llm::ParameterSchema):
use ;
let tools = vec!;
let request = new
.with_tools;
let response = client.generate.await?;
for call in response.tool_calls
Using Claude on Vertex AI (requires service account auth):
let client = get_vertex_client.await?;
let request = new
.with_max_tokens;
let response = client.generate.await?;
Authentication:
| Method | When | Env Vars |
|---|---|---|
| API Key | Gemini only | VERTEX_API_KEY or GEMINI_API_KEY |
| Service Account | Gemini + Anthropic | VERTEX_PROJECT_ID, VERTEX_REGION, GOOGLE_APPLICATION_CREDENTIALS |
If an API key is present, it takes priority. Service account auth is required for
Anthropic models. Default region: asia-south1.
LLM
Canonical, provider-agnostic types and the LlmProvider trait that every backend
implements. Use it directly when you just want to call an LLM; build on top of it
when you want tool-calling orchestration (see agent).
use ;
// `client` is anything implementing `LlmProvider` —
// VertexClient, or your own OpenAi/Bedrock/Ollama/local impl.
let request = new
.with_system
.with_temperature;
let response = client.generate.await?;
Types you'll use:
| Type | Purpose |
|---|---|
LlmProvider (trait) |
generate() + stream_generate() on a canonical GenerateRequest. Implement this to add a backend. |
GenerateRequest / GenerateResponse |
Canonical request/response, provider-neutral |
Message, Role, ContentPart |
Conversation turns — text, tool calls, tool results |
StreamChunk |
Text(String) | ToolCall { id, name, arguments } | Done { finish_reason, usage } |
ToolDefinition + ParameterSchema |
Strictly-typed tool descriptions; serializes to valid JSON Schema |
Usage |
Token accounting (input/output/total) |
Writing a custom backend:
use ;
use AppError;
use Future;
use Pin;
Drops into arche::agent::get_agent_engine(my_client, config) with no other changes.
Agent
Tool-calling agent engine: orchestrates LLM rounds, invokes your tools, streams SSE events to the client, manages session history (with optional compaction).
use ;
use ;
use ;
;
// Wire it up
let client = get_vertex_client.await?;
let config = builder.build?;
let engine = get_agent_engine
.with_default_summarizer; // optional, cheap summarization
// Per request
let mut session = new;
let stream = engine.run;
// Map each SseEvent via `to_sse_event(..)` to an axum SSE Event.
What arche provides vs. what you write:
| Arche provides | You write |
|---|---|
| Orchestration loop, streaming, SSE event types, session mutation, tool-calling loop, history compaction | System prompt, tool schemas, tool executors (impl AgentFlow), HTTP handler, session persistence |
Extension points:
| Need | Plug point |
|---|---|
| Different LLM backend | impl LlmProvider for YourClient |
| Custom history compaction (vector recall, server-side memory) | impl HistoryCompactor |
| Custom UI events from tools | ToolOutput::text(..).data(type, payload) → reaches client via SseEvent::Data |
Deeper reading:
docs/agent/architecture.md— module layering, component diagram with hover tooltipsdocs/agent/sequence.md— request lifecycle, error paths, SSE wire formatdocs/agent/extending.md— step-by-step guides for each plug point
Database
Postgres
Connection pooling with sqlx, configurable credentials, and health checks.
use ;
let pool = get_pg_pool.await?;
let is_healthy = test_pg.await?;
| Env Var | Description |
|---|---|
PG_HOST |
Database host |
PG_PORT |
Database port |
PG_DATABASE |
Database name |
PG_MAX_CONN |
Maximum pool connections |
PG_USERNAME |
Username |
PG_PASSWORD |
Password |
PG_CREDENTIALS |
JSON string {"username":"...","password":"..."} (alternative to separate vars) |
Redis
Connection pooling with bb8, optional password auth, and health checks.
use ;
let pool = get_redis_pool.await?;
let is_healthy = test_redis.await?;
| Env Var | Description |
|---|---|
REDIS_HOST |
Redis host |
REDIS_PORT |
Redis port |
REDIS_MAX_CONN |
Maximum pool connections |
REDIS_PASSWORD |
Optional password |
JWT
Token generation and verification using HS256.
use ;
use ;
// Generate an access + refresh token pair
let tokens = generate_tokens?;
// Verify a token
let data = ?;
CSV
Async CSV processing powered by csv-async. Supports reading from bytes, files, and
URLs — with both batch and streaming modes.
use CsvClient;
// Default config (comma-delimited, with headers)
let csv = new;
// Or customize
let csv = new
.delimiter
.has_headers
.flexible;
Reading
use Deserialize;
// From bytes
let records: = csv.read.from_bytes.deserialize.await?;
// From file
let records: = csv.read.from_file.deserialize.await?;
// From URL
let records: = csv.read.from_url
.deserialize.await?;
// Batch processing (memory-efficient for large files)
csv.read.from_file
.deserialize_batched.await?;
Writing
use Serialize;
let records = vec!;
// To bytes
let bytes: = csv.write_all.await?;
// To file
csv.write_file.await?;
Streaming
// Record-by-record reading
let mut stream = csv.read.from_file.stream.await?;
while let Some = stream..await
// Record-by-record writing
let mut writer = csv.writer_to_file.await?;
writer.serialize.await?;
writer.finish.await?;
JSON
Streaming JSON array parsing optimized for large payloads. Extracts metadata fields before the target array and streams array elements one-by-one or in batches — without loading the full document into memory.
use JsonClient;
use Deserialize;
let json = new;
// Stream a root-level JSON array from bytes
let source = json.from_bytes;
let mut stream = source.stream_root_array;
while let Some = stream..await
// Stream a nested array with metadata capture
// Given: {"total": 1000, "items": [{...}, {...}, ...]}
let json = new;
let source = json.from_bytes;
let mut stream = source.stream_array.await;
while let Some = stream..await
let total: u64 = stream.field?;
// Batch iteration
let batch = stream..await;
// Stream directly from S3
let source = new.from_s3.await?;
let mut stream = source.stream_array.await;
Crypto
AES-128-CBC encryption with PBKDF2-HMAC-SHA1 key derivation (65,536 iterations).
use ;
let secret = "my-secret-key";
let salt = "my-salt-value-16"; // minimum 16 bytes
// Encrypt — returns raw ciphertext bytes
let ciphertext = encrypt_cbc?;
// Decrypt — expects base64-encoded ciphertext input
let plaintext = decrypt_cbc?;
Sockets
WebSocket connection registry with broadcast support. Manages a thread-safe map of active connections for fan-out messaging.
use SocketConnectionManager;
let manager = new;
// Register a connection (typically in a WebSocket upgrade handler)
manager.add?;
// Broadcast to all connected clients
manager.broadcast?;
// List active connections
let ids = manager.get_connections?;
// Remove a connection on disconnect
manager.remove?;
Error
Axum-compatible structured error handling. Every variant converts to a JSON response with the appropriate HTTP status code.
use AppError;
async
Variants:
| Variant | Status | Constructor |
|---|---|---|
BadRequest |
400 | AppError::bad_request(errors, message, description) |
Unauthorized |
401 | Direct construction |
Forbidden |
403 | Direct construction |
NotFound |
404 | AppError::not_found("resource") |
Conflict |
409 | AppError::conflict("message") |
UnprocessableEntity |
422 | AppError::unprocessable_entity(errors, message, description) |
DependencyFailed |
424 | AppError::dependency_failed("upstream", "detail") |
InternalError |
500 | AppError::internal_error(error, message) |
Unavailable |
503 | Direct construction |
InternalError responses are sanitized by default — no leaked SQL or infra
details. Enable verbose-errors to expose raw error details (dev/staging only):
= { = "2.5.0", = ["verbose-errors"] }
Utils
Date/time conversion traits and pagination helpers.
use ;
use OffsetDateTime;
// Check if a timestamp is in the future
let is_valid = validate_timestamp?;
// Convert OffsetDateTime to ISO string
let iso = offset_dt.to_iso_string?;
// Pagination query params (for Axum extractors)
let params = PaginationParams ;
Re-exported Dependencies
arche re-exports these crates so you don't need to add them separately:
axum · tokio · serde · serde_json · sqlx · time · tracing · tracing-subscriber · reqwest · jsonwebtoken · nanoid · thiserror · base64 · bb8 · bb8-redis · csv-async · futures · tokio-stream · dotenv · aws-config · aws-sdk-s3 · aws-sdk-sesv2 · aws-sdk-kms · google-drive3 · google-sheets4
Design Principles
- Explicit over implicit — no hidden global state or magic
- Composition over inheritance — thin wrappers you combine as needed
- Production-first defaults — sane defaults, sanitized errors, pooled connections
- Async-native — built on Tokio from the ground up
What arche is not
- A framework that replaces Axum
- A code generator or project template
- A monolithic abstraction over third-party libraries