The production-grade AI agent runtime for Rust.
Type-safe. Async-first. Relentlessly engineered.
Most AI agent frameworks are scaffolding with aspirations. Aeolus is a production runtime.
It handles everything between your business logic and the real world: provider routing with automatic failover across nineteen LLMs, capability-based security, layered policy enforcement, durable execution with exactly-once step semantics, unified agent memory via cairn (aeolus-cairn), multi-agent DAG orchestration, the AG-UI protocol for frontend integration, and full OpenTelemetry observability — compiled to a single Rust binary, or to wasm32-wasip2 for the edge.
You write agents. Aeolus keeps them running.
- 19 LLM providers — OpenAI, Anthropic, Gemini, Mistral, Bedrock, Azure OpenAI, Ollama, DeepSeek, xAI (Grok), Groq, Together AI, Fireworks AI, Cohere, Perplexity, HuggingFace, SambaNova, Cerebras, Databricks, DashScope — with automatic failover and capability-aware routing. Switch models in config, not code.
- Capability-based security — zero-trust from spawn. Every agent operation requires an explicit, cryptographically-issued token. No capability, no action.
- Layered policy engine — native TOML DSL, OPA/Rego via WASM, budget tracking, rate limiting, content filters, circuit breakers, and human-in-the-loop approval workflows. Composable.
- Unified agent memory —
aeolus-cairnbacked by cairn provides five persistent tiers (working, episodic, semantic, procedural, artifact), fused vector+lexical+graph retrieval with token-budgeted deterministic context packs, bitemporal storage, zero-downtime embedder migration, and dream-time consolidation. Zero daemons, zero external dependencies. - AG-UI protocol —
POST /agui/v1/:agent_typeSSE endpoint compatible with CopilotKit and any AG-UI client. Real-time state sync, interrupts, and reasoning streams. - Durable execution —
StepExecutorwith idempotent replay. Crash mid-workflow,resume()from the last checkpoint. Exactly-once semantics by design. - Multi-agent orchestration — DAG workflows, supervisor patterns, fan-out/fan-in parallelism, and the Agent-to-Agent (A2A) protocol for inter-runtime communication.
- 7 built-in tools — web search, code interpreter (WASM-sandboxed), terminal, log tailer, filesystem, database, email. Plus a
StreamingToolinterface for bidirectional sessions. - Full observability — OpenTelemetry traces and Prometheus metrics on every operation, automatically. Zero configuration for stdout; OTLP for production.
- Kubernetes-native — CRDs for
AgentRun,AgentRunTemplate,AgentCronJob, andAgentTrigger. Operator ships with leader election and pod security hardening. - Enterprise-ready — multi-tenant isolation, RBAC/ABAC policy engines, AES-256-GCM encryption at rest, KMS integration, and compliance profiles for SOC 2, HIPAA, GDPR, DORA, and FedRAMP.
- Edge-ready — compiles to
wasm32-wasip2. Deploy to Cloudflare Workers, Fastly Compute, or any WASI runtime.
Add Aeolus to your Cargo.toml (crates.io publication is planned; until then, use the git dependency):
[dependencies]
aeolus = { git = "https://github.com/anemoi-ai/aeolus", tag = "v0.1.0", features = ["provider-anthropic", "provider-openai"] }
tokio = { version = "1", features = ["full"] }Define an agent:
use aeolus::prelude::*;
struct Analyst;
#[async_trait]
impl Agent for Analyst {
const NAME: &'static str = "analyst";
type Input = String; // the question
type Output = String; // the answer
type State = (); // no persistent state needed
async fn run(
&self,
question: Self::Input,
ctx: &mut AgentContext<Self::State>,
) -> Result<Self::Output, AgentError> {
let response = ctx
.complete(
CompletionRequest::builder()
.model("claude-sonnet-4-6")
.system("You are a concise, precise analyst.")
.user(&question)
.build(),
&ctx.auth(),
)
.await
.map_err(|e| AgentError::fatal(Self::NAME, e.to_string()))?;
Ok(response.content_text())
}
}Build a runtime and spawn:
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let runtime = AgentRuntime::builder()
.with_llm_provider(AnthropicProvider::from_env()) // reads ANTHROPIC_API_KEY
.build()
.await?;
let handle = runtime
.spawn(
Analyst,
"What are the key risks in deploying LLM agents in production?".into(),
SpawnContext::new(Principal::user("alice")),
)
.await?;
println!("{}", handle.await?);
runtime.shutdown();
Ok(())
}That's it. The runtime starts, the agent runs, the handle resolves. No global state, no ambient context, no surprises.
An agent is anything that implements the Agent trait. The three associated types make the contract explicit:
impl Agent for MyAgent {
const NAME: &'static str = "my-agent"; // used in traces, logs, and metrics
type Input = MyInput; // what the agent receives
type Output = MyOutput; // what it produces
type State = MyState; // checkpointed between steps (use () if stateless)
async fn run(&self, input: Self::Input, ctx: &mut AgentContext<Self::State>)
-> Result<Self::Output, AgentError> { ... }
}The #[agent] proc-macro generates boilerplate for tool declarations, capability requirements, and reasoning configuration:
#[agent(
name = "researcher",
tools = ["web_search", "read_file"],
memory = "episodic",
budget = { max_tokens = 100_000, max_cost_usd = "2.00" },
)]
struct Researcher;AgentContext<S> is the agent's interface to everything outside its own logic. It never leaks subsystem internals:
async fn run(&self, input: Query, ctx: &mut AgentContext<ResearchState>) -> ... {
// LLM calls — capability-checked
let response = ctx.complete(request, &auth).await?;
// For streaming, use the raw router (returns a CompletionStream)
let stream = ctx.llm().complete_stream(request, &auth).await?;
// Tool dispatch — fully typed, policy-checked
let result: serde_json::Value = ctx.call_tool("web_search", json!({ "query": "..." })).await?;
// Streaming tools — bidirectional terminal sessions, log tailers
let mut session = ctx.stream_tool("terminal", json!({ "shell": "bash" })).await?;
session.send("cargo test --workspace").await?;
while let Some(chunk) = session.next().await { ... }
// Memory — unified agent memory via aeolus-cairn
if let Some(handle) = ctx.cairn_handle() {
handle.set("last_query", &input).map_err(|e| AgentError::Other(e.to_string()))?;
}
// Checkpointing — snapshot state, resume on crash
let checkpoint_id = ctx.checkpoint().await?;
// Spawn a child agent — full concurrency, full isolation
let child_handle = ctx.spawn_child(SummariserAgent, summary_input, spawn_ctx).await?;
// Human-in-the-loop — pause and wait for approval
let decision = ctx.request_approval("About to delete production data").await?;
if decision == ApprovalDecision::Denied { return Err(...); }
}Tools are typed, async, and policy-checked. Define one:
use aeolus_tools::prelude::*;
#[derive(Serialize, Deserialize, JsonSchema)]
struct SearchInput { query: String }
#[derive(Serialize, Deserialize, JsonSchema)]
struct SearchOutput { results: Vec<String>, total: usize }
struct WebSearch;
#[async_trait]
impl Tool for WebSearch {
const NAME: &'static str = "web_search";
const DESCRIPTION: &'static str = "Search the web and return ranked results";
type Input = SearchInput;
type Output = SearchOutput;
async fn execute(&self, input: SearchInput) -> Result<SearchOutput, ToolError> {
// your HTTP call here
Ok(SearchOutput { results: vec![], total: 0 })
}
}Register it once at runtime construction:
AgentRuntime::builder()
.with_tool(WebSearch)
.with_tool(CodeInterpreter::wasm_sandboxed())
.build()
.await?Tools can run in-process, inside a Wasmtime WASM sandbox (with fuel limits and WASI syscall filtering), or as isolated subprocesses. You decide the isolation level; the dispatch interface is identical from the agent's perspective.
Aeolus routes across providers with automatic failover. Configure a chain:
AgentRuntime::builder()
.with_llm_provider(AnthropicProvider::from_env()) // primary
.with_llm_provider(OpenAiProvider::from_env()) // fallback #1
.with_llm_provider(OllamaProvider::local(11434)) // fallback #2 (self-hosted)
.build()
.await?If the primary provider returns a rate-limit or transient error, the router tries the next in chain — transparently, without any change to agent code.
| Provider | Feature flag | Streaming | Tool use | Vision | Reasoning |
|---|---|---|---|---|---|
| Anthropic Claude | provider-anthropic ✓ |
✓ | ✓ | ✓ | ✓ |
| OpenAI GPT / o-series | provider-openai ✓ |
✓ | ✓ | ✓ | ✓ |
| Google Gemini | provider-gemini |
✓ | ✓ | ✓ | ✓ |
| Mistral | provider-mistral |
✓ | ✓ | ||
| Amazon Bedrock | provider-bedrock |
✓ | ✓ | ✓ | |
| Azure OpenAI | provider-azure |
✓ | ✓ | ✓ | ✓ |
| Ollama (self-hosted) | provider-ollama |
✓ | ✓ | ||
| DeepSeek | provider-deepseek |
✓ | ✓ | ✓ | |
| xAI (Grok) | provider-xai |
✓ | ✓ | ✓ | |
| Groq | provider-groq |
✓ | ✓ | ||
| Together AI | provider-together |
✓ | ✓ | ✓ | ✓ |
| Fireworks AI | provider-fireworks |
✓ | ✓ | ||
| Cohere | provider-cohere |
✓ | ✓ | ||
| Perplexity | provider-perplexity |
✓ | ✓ | ✓ | |
| HuggingFace | provider-huggingface |
✓ | |||
| SambaNova | provider-sambanova |
✓ | |||
| Cerebras | provider-cerebras |
✓ | |||
| Databricks | provider-databricks |
✓ | ✓ | ||
| DashScope (Qwen) | provider-dashscope |
✓ | ✓ | ✓ | |
| Helix (in-process, local) | provider-helix |
✓ | ✓ | ✓¹ | ✓¹ |
¹ Helix runs a local GGUF model in-process (libhelix) — no network. Vision is advertised only when an mmproj projector is loaded and reasoning only when a reasoning format is set; it also guarantees grammar-constrained (schema-valid) output. Native-linked, so it is kept out of default/all-providers.
The FeatureMatrix tracks which capabilities each model actually supports at runtime. Requesting vision on a text-only model fails fast with a clear error rather than a confusing API response.
Every agent starts with zero privileges. Operations require explicit capability tokens, cryptographically issued by the runtime and verified before each action:
// At runtime construction, declare what the default agent set can do
let runtime = AgentRuntime::builder()
.with_initial_capabilities(vec![
CapabilityKind::LlmCall,
CapabilityKind::ToolExecute,
CapabilityKind::MemoryRead,
])
.build()
.await?;
// Capabilities can be attenuated — you can only give away what you have
let child_cap = parent_cap.attenuate(vec![
CapabilityConstraint::NoAmplification,
CapabilityConstraint::MaxCostUsd("0.50".parse()?),
]);
// Issue extra capabilities for privileged operations
let write_cap = runtime.issue_capability(CapabilityKind::CheckpointWrite, &principal);Attenuation is strictly monotonic: a child capability can only have a subset of its parent's permissions. The constraint set is hash-chained and cryptographically verified on every use.
The PolicyStack evaluates every LLM call and tool dispatch through a layered chain. Each layer can Allow, Deny, RequireApproval, or Throttle. Layers compose:
# policy.toml — live-reloaded, no restart required
[budget]
max_tokens = 500_000
max_cost_usd = "10.00"
exceed_action = "RequestApproval"
[rate_limit]
requests_per_minute = 60
scope = "per-principal"
[[content_filters]]
category = "pii"
action = "Redact"
[[tool_filters]]
pattern = "terminal.*"
action = "RequireApproval"
[[hitl]]
trigger = "cost_exceeds_usd"
value = "5.00"For complex, auditable decisions, plug in OPA/Rego:
let opa_engine = OpaEngine::from_bundle("policies/agent-policy.tar.gz").await?;
AgentRuntime::builder()
.with_policy_engine(opa_engine)
.build()
.await?PromptInjectionGuard scans all LLM inputs for instruction injection, homoglyph attacks (аdmin vs admin), Unicode bidirectional tricks, and base64-encoded payloads:
AgentRuntime::builder()
.with_injection_guard(PromptInjectionGuard::strict())
.build()
.await?Every significant operation is appended to a SHA-256 hash-chained audit log. Each entry links to its predecessor — tamper with one entry and the chain breaks:
// Audit records are written automatically. Read them back:
let entries = ctx.audit_log()
.query(AuditQuery::for_run(&run_id))
.await?;Aeolus provides exactly-once step semantics via StepExecutor. Steps that have already completed are replayed from the checkpoint store without re-executing their closures. Crash, restart, and resume() — the agent picks up exactly where it left off:
async fn run(&self, input: Input, ctx: &mut AgentContext<Self::State>) -> ... {
// This step is idempotent — if the process crashed after it completed,
// resuming will return the stored result without calling the closure again.
let analysis = ctx
.step("analyse-document", &input.document, |doc| async move {
expensive_analysis(doc).await
})
.await?;
// Checkpoint state before the next expensive operation
ctx.checkpoint().await?;
let report = ctx
.step("generate-report", &analysis, |a| async move {
generate_report(a).await
})
.await?;
Ok(report)
}Resume a crashed run by its ID:
let handle = runtime
.resume(MyAgent, crashed_run_id, original_input, spawn_ctx)
.await?;Checkpoint backends: in-memory (default), aeolus-cairn (recommended), PostgreSQL, SQLite, S3 / GCS / Azure Blob Storage.
// Agents can spawn other agents. The child runs concurrently.
let summary_handle = ctx
.spawn_child(Summariser, long_text, SpawnContext::new(principal))
.await?;
let translation_handle = ctx
.spawn_child(Translator, long_text, SpawnContext::new(principal))
.await?;
// Fan-in — collect results
let (summary, translation) = tokio::join!(summary_handle, translation_handle);For complex pipelines with dependencies, use the workflow builder:
use aeolus_orchestration::prelude::*;
let workflow = Workflow::builder()
.node("ingest", IngestAgent, ingest_input)
.node("analyse", AnalystAgent, ()) // depends on ingest
.node("summarise", SummariserAgent, ()) // depends on analyse
.node("translate", TranslatorAgent, ()) // can run parallel with summarise
.edge("ingest", "analyse", passthrough())
.edge("analyse", "summarise", extract_field("report"))
.edge("analyse", "translate", extract_field("report"))
.fan_in(["summarise", "translate"], "publish", merge_results())
.build()?;
let output = workflow.run(&runtime, spawn_ctx).await?;Reach agents running in other Aeolus instances — or any A2A-compatible runtime — over HTTP:
let remote = A2aClient::new("https://translation-service.internal")
.with_auth(A2aAuth::bearer(secret_token));
let result = remote
.call::<TranslationRequest, TranslationResult>(
"translate",
TranslationRequest { text: report, target: "ja" },
)
.await?;The AgentRuntime server exposes /.well-known/agent.json and /a2a/v1/ endpoints automatically when the a2a feature is enabled.
aeolus-server \
--http-addr 0.0.0.0:8080 \
--grpc-addr 0.0.0.0:9090 \
--policy /etc/aeolus/policy.tomlREST API:
Public endpoints (no auth required):
| Method | Path | Description |
|---|---|---|
GET |
/healthz |
Liveness check |
GET |
/readyz |
Readiness check |
GET |
/metrics |
Prometheus metrics |
GET |
/.well-known/agent.json |
A2A agent card discovery |
Protected endpoints (Bearer token / API key):
| Method | Path | Description |
|---|---|---|
POST |
/v1/runs |
Spawn an agent run |
GET |
/v1/runs |
List agent runs |
GET |
/v1/runs/:id |
Get run status and output |
GET |
/v1/runs/:id/stream |
Stream run events (SSE) |
POST |
/v1/runs/:id/cancel |
Cancel a running agent |
POST |
/v1/runs/:id/resume |
Resume a checkpointed run |
GET |
/v1/tools |
List registered tools |
POST |
/v1/tools/:name/test |
Test-invoke a tool |
GET |
/v1/policies |
List loaded policies |
POST |
/v1/policies/validate |
Validate a policy file |
POST |
/v1/policies/reload |
Hot-reload policies |
GET |
/v1/approvals |
List pending HITL approvals |
POST |
/v1/approvals/:id/approve |
Grant an approval |
POST |
/v1/approvals/:id/deny |
Deny an approval |
GET |
/v1/audit/tail |
Tail the live audit log |
GET |
/v1/audit/export |
Export audit log entries |
POST |
/v1/audit/verify |
Verify audit chain integrity |
GET |
/v1/features |
List model feature matrix |
GET |
/v1/features/:model |
Features for a specific model |
POST |
/agui/v1/:agent_type |
AG-UI protocol endpoint (SSE stream) |
A gRPC interface (StartRun, GetRun, CancelRun, ResumeRun, StreamRun) is available in preview.
# Start a run
aeolusctl run start --agent researcher --input '{"topic":"Rust safety"}'
# Tail live events
aeolusctl run stream <run-id>
# Manage policies
aeolusctl policies validate --file policy.toml
aeolusctl policies reload
# Inspect approvals
aeolusctl approvals list
aeolusctl approvals approve <approval-id>
# Audit trail
aeolusctl audit tail --run <run-id>
# Tools
aeolusctl tools list
# Memory (aeolus-cairn)
aeolusctl cairn migrate-checkpoints --dry-run
aeolusctl cairn inspect <scope-path>Apply the CRDs and let the operator manage the lifecycle:
apiVersion: aeolus.anemoi.ltd/v1
kind: AgentRun
metadata:
name: quarterly-analysis
namespace: production
spec:
agent: financial-analyst
input:
period: "2026-Q1"
focus: "cost-efficiency"
budget:
maxTokens: 200_000
maxCostUsd: "5.00"
policy:
configMapRef: aeolus-policy
checkpointing:
backend: postgres
secretRef: aeolus-db-credentialsScheduled runs with AgentCronJob, reusable templates with AgentRunTemplate, event-driven execution with AgentTrigger. The operator runs with leader election for high availability.
[dependencies]
aeolus = { git = "https://github.com/anemoi-ai/aeolus", tag = "v0.1.0", features = ["wasm-edge", "provider-anthropic"] }cargo build --target wasm32-wasip2 --release
# deploy the .wasm to Cloudflare Workers, Fastly Compute, etc.The edge module provides EdgeRequest / EdgeResponse types and AgentRuntime::handle_edge_request() as the WASM entry point. Network-dependent backends (Redis, PostgreSQL, S3) are not available in this target; in-memory equivalents are selected automatically by the wasm-edge feature.
Aeolus ships a purpose-built test runtime. No real LLM calls, no real tools, no flaky network:
use aeolus_testing::prelude::*;
#[tokio::test]
async fn researcher_calls_search_and_returns_summary() {
let rt = TestRuntime::builder()
.mock_llm(MockLlm::scripted([
// First LLM call → ask it to use the search tool
MockResponse::tool_call("web_search", json!({ "query": "Rust memory safety" })),
// Second LLM call → synthesise results into a response
MockResponse::text("Rust achieves memory safety through ownership and borrowing."),
]))
.mock_tool(
"web_search",
MockTool::returns(json!({
"results": [
{ "title": "The Rust Book", "url": "https://doc.rust-lang.org/book/", "snippet": "..." }
]
})),
)
.build();
let handle = rt.spawn(Researcher, "Rust memory safety".into()).await.unwrap();
let output = handle.await.unwrap();
assert!(output.contains("ownership"));
rt.assert_tool_called("web_search", 1);
rt.assert_llm_calls(2);
rt.assert_within_budget();
}MockLlm supports scripted response sequences, conditional branching on request content, and streaming chunks. MockTool can return fixed values, return errors, or capture its inputs for assertion.
The test runtime participates in the full policy stack — budget limits, content filters, and HITL workflows all fire in tests, so policy violations are caught before they reach production.
All operations emit OpenTelemetry spans and Prometheus metrics automatically. Zero instrumentation code required in agent logic.
Metrics:
| Metric | Type | Description |
|---|---|---|
agent_runs_total |
Counter | Runs started, labelled by agent name and status |
agent_run_duration_ms |
Histogram | End-to-end run latency |
llm_calls_total |
Counter | LLM API calls by provider and model |
llm_tokens_total |
Counter | Tokens consumed, split by prompt / completion |
tool_invocations_total |
Counter | Tool dispatches by tool name and isolation mode |
policy_denials_total |
Counter | Policy rejections by engine and reason |
injection_detections_total |
Counter | Prompt injection guard triggers by attack type |
active_agents |
Gauge | Currently executing agent runs |
budget_utilisation_pct |
Gauge | Budget consumption as a percentage of limit |
checkpoint_writes_total |
Counter | Checkpoint operations by backend |
Traces:
Every agent run produces a root span with child spans for each LLM call, tool dispatch, policy evaluation, and checkpoint operation. Spans carry run_id, agent_name, principal, and model as attributes — filter and correlate in Jaeger, Grafana Tempo, or any OTLP backend.
Configuration:
// stdout for development
AgentRuntime::builder()
.with_telemetry(TelemetryConfig::stdout())
.build()
.await?
// OTLP for production
AgentRuntime::builder()
.with_telemetry(
TelemetryConfig::otlp("http://otel-collector:4317")
.with_service_name("my-agent-service")
.with_batch_export(),
)
.build()
.await?Enable the enterprise feature for multi-tenant deployments.
let registry = TenantRegistry::new();
registry.register(TenantConfig {
id: TenantId::new("acme-corp"),
budget_caps: BudgetCaps { max_cost_usd: "100.00".parse()? },
llm_credentials: Some(acme_credentials),
capability_allowlist: vec![CapabilityKind::LlmCall, CapabilityKind::ToolExecute],
..Default::default()
})?;
// All memory operations for this tenant are transparently namespaced
let store = TenantScopedMemoryStore::new(base_store, tenant_id);let rbac = RbacPolicyEngine::new()
.allow(Role::Operator, Action::SpawnAgent)
.allow(Role::Auditor, Action::ReadAuditLog)
.deny(Role::Viewer, Action::DeleteRun);
let abac = AbacPolicyEngine::new()
.with_extractor(TimeAttributeExtractor::business_hours_only())
.with_rule(AbacRule::deny_outside_region(DataResidencyRegion::Eu));// Wrap any episodic store with transparent AES-256-GCM encryption
let encrypted_store = EncryptedStore::new(
postgres_store,
AwsKmsKeyProvider::new("alias/aeolus-memory-key"),
);
// Key rotation is non-disruptive — old data is re-encrypted lazily
encrypted_store.rotate_key().await?;let profile = ComplianceProfile::Hipaa;
let (policy_config, audit_config) = profile.generate_configs();
// policy_config enforces PHI detection, redaction, and access controls
// audit_config sets 7-year retention and SIEM export formatAvailable profiles: Soc2Type2, Hipaa, Gdpr, Dora, FedRampModerate, and Custom.
Aeolus uses Cargo features to keep binaries lean. Enable only what you need:
All feature flags
| Feature | Description | Default |
|---|---|---|
| LLM Providers | ||
provider-anthropic |
Anthropic Claude (all models) | ✓ |
provider-openai |
OpenAI GPT + o-series + embeddings | ✓ |
provider-gemini |
Google Gemini | |
provider-mistral |
Mistral AI | |
provider-bedrock |
Amazon Bedrock (Converse API) | |
provider-azure |
Azure OpenAI | |
provider-ollama |
Ollama (self-hosted, local discovery) | |
provider-deepseek |
DeepSeek (reasoning models) | |
provider-xai |
xAI Grok | |
provider-groq |
Groq (LPU, ultra-low latency) | |
provider-together |
Together AI (200+ models) | |
provider-fireworks |
Fireworks AI (throughput-optimised) | |
provider-cohere |
Cohere (enterprise RAG) | |
provider-perplexity |
Perplexity (search-grounded) | |
provider-huggingface |
HuggingFace Inference API | |
provider-sambanova |
SambaNova (SN40L silicon) | |
provider-cerebras |
Cerebras (CS-3 wafer-scale) | |
provider-databricks |
Databricks Foundation Model APIs | |
provider-dashscope |
DashScope / Alibaba Qwen | |
all-providers |
All nineteen providers | |
provider-helix |
In-process Helix (libhelix) local inference — native-linked, excluded from default/all-providers/full |
|
provider-helix-embed |
provider-helix + the HelixEmbedder (aeolus_cairn::Embedder) |
|
| Tool Execution | ||
tools-inprocess |
In-process execution | ✓ |
tools-wasm |
Wasmtime sandbox (fuel limits, WASI filtering) | |
tools-subprocess |
Subprocess isolation | |
tools-streaming |
Bidirectional streaming tool sessions | |
| Memory | ||
memory-inmemory |
In-memory store (dev / testing) | ✓ |
memory-cairn |
Unified memory via cairn (recommended) | |
memory-redis |
Redis episodic store (legacy) | |
memory-postgres |
PostgreSQL episodic store (legacy) | |
memory-qdrant |
Qdrant vector store (legacy) | |
memory-lancedb |
LanceDB embedded vector store (legacy) | |
all-memory |
All memory backends | |
| Checkpointing | ||
checkpoint-inmemory |
In-memory (dev / testing) | ✓ |
checkpoint-postgres |
PostgreSQL | |
checkpoint-sqlite |
SQLite | |
checkpoint-s3 |
S3 / GCS / Azure Blob Storage | |
all-checkpoints |
All checkpoint backends | |
| Policy & Security | ||
policy-native |
TOML policy DSL with hot-reload | ✓ |
policy-opa |
OPA/Rego via Wasmtime | |
security-full |
Capabilities, audit log, injection guard, network guard | |
secrets-vault |
HashiCorp Vault | |
secrets-aws |
AWS Secrets Manager | |
secrets-gcp |
GCP Secret Manager | |
| Observability | ||
telemetry-otel |
OpenTelemetry traces + Prometheus metrics | ✓ |
| Platform | ||
enterprise |
Multi-tenancy, RBAC/ABAC, encryption at rest, compliance | |
a2a |
Agent-to-Agent protocol (serve + client) | |
k8s |
Kubernetes operator CRDs | |
wasm-edge |
wasm32-wasip2 edge target |
|
full |
Everything enabled |
Benchmarks run with cargo bench -p aeolus-benchmarks on an AMD Ryzen 9 5900X against in-memory backends (release profile, LTO thin). Every stated target is met. See BENCHMARKS.md for the full Criterion output with confidence intervals.
| Operation | Target | Measured (mean) |
|---|---|---|
| Agent spawn | < 500 µs | 9.4 µs ✅ 53× under |
| Policy eval — native TOML | < 100 µs | 127 ns ✅ 787× under |
| Policy eval — OPA/Rego WASM | < 500 µs | ~380 ns serde + WASM eval* |
| Tool dispatch — in-process | < 200 µs | — |
| WASM tool cold start | < 5 ms | — |
| StreamingTool first chunk | < 500 µs | — |
| Checkpoint write — in-memory | < 5 ms | 1.57 µs ✅ 3,185× under |
| Checkpoint write — PostgreSQL | < 5 ms | — |
| Memory read — Redis | < 1 ms | — |
| FeatureMatrix lookup | < 1 µs | 13.6 ns ✅ 74× under |
| Capability token verify | — | 4.2 ns |
| Audit chain verify — 1,000 events | — | 515 µs |
| Injection guard scan — 1 KB | — | 89 µs |
| DAG workflow — linear 10 nodes | — | 3.28 µs |
*OPA serde costs ~380 ns; full WASM evaluation time is bundle and policy-dependent.
| Guide | Description |
|---|---|
| Getting Started | Installation, first agent, runtime configuration |
| Architecture | System design, trust boundaries, crate map |
| Security Model | Threat model, defence-in-depth controls, secure deployment |
| Streaming & Tools Guide | StreamingTool, bidirectional sessions, terminal and log-tailer tools |
| OPA Policy Guide | Writing Rego policies, compiling WASM bundles, hot-reload |
| A2A Interop Guide | Agent-to-Agent protocol, multi-runtime coordination |
| Multi-Modal Guide | Vision inputs, image analysis, model capability routing |
aeolus/
├── crates/
│ ├── aeolus/ # SDK facade — the crate you import
│ ├── aeolus-macros/ # #[agent] and #[derive(CairnState)] proc-macros
│ ├── aeolus-llm/ # LLM providers (19), router, feature matrix, pricing
│ ├── aeolus-tools/ # Tool trait, registry, WASM sandbox
│ ├── aeolus-safety/ # Policy engine, budget, HITL, content filter
│ ├── aeolus-security/ # Capabilities, audit log, injection guard, network guard
│ ├── aeolus-cairn/ # Unified agent memory via cairn (recommended)
│ ├── aeolus-agui/ # AG-UI protocol endpoint (SSE)
│ ├── aeolus-memory/ # [DEPRECATED] Episodic + vector stores — use aeolus-cairn
│ ├── aeolus-persistence/ # [DEPRECATED] Checkpoint store — use aeolus-cairn
│ ├── aeolus-orchestration/ # DAG workflow engine, supervisor pattern
│ ├── aeolus-telemetry/ # OpenTelemetry integration
│ ├── aeolus-policy-opa/ # OPA/Rego WASM evaluator
│ ├── aeolus-server/ # Axum HTTP + tonic gRPC server
│ ├── aeolus-operator/ # Kubernetes operator
│ ├── aeolus-enterprise/ # Multi-tenant, RBAC, encryption, compliance
│ └── aeolus-testing/ # TestRuntime, MockLlm, MockTool
├── tools/ # 7 built-in tool implementations
│ ├── web-search/
│ ├── code-interpreter/ # Wasmtime-sandboxed
│ ├── terminal/ # Streaming, Linux namespace sandbox
│ ├── log-tailer/
│ ├── file-system/
│ ├── database/
│ └── email/
├── bin/
│ ├── aeolus-server/ # Server binary
│ └── aeolusctl/ # CLI management tool
├── examples/ # Annotated reference examples
├── live_examples/ # 13 runnable examples (requires local LLM)
├── benches/ # Criterion benchmarks
├── fuzz/ # libfuzzer targets (6 targets)
└── docs/ # Architecture, guides, security model
The examples/ directory contains annotated reference implementations:
| Example | What it demonstrates |
|---|---|
hello_agent |
Minimal agent, runtime construction, spawn and await |
research_agent |
Tool-calling loop, multi-phase execution, image analysis |
code_review_pipeline |
Streaming tools, file system access, structured output |
customer_support_bot |
Content safety filters, HITL approval, conversation memory |
data_analysis_workflow |
DAG orchestration, fan-out/fan-in, database tool |
remote_agent_mesh |
A2A protocol, multi-runtime coordination |
opa_policy_integration |
OPA/Rego policy engine, bundle loading, hot-reload |
llm_smoke_test |
Bare LLM provider connectivity check (OpenAI-compatible endpoint) |
llm_tool_test |
Tool-call round-trip: calculator tool with multi-turn LLM loop |
The live_examples/ directory contains 13 runnable examples against a real LLM endpoint. See live_examples/01_smoke_test/ to verify your setup.
Aeolus builds with stable Rust (MSRV 1.85, pinned via rust-toolchain.toml), but two native prerequisites are needed:
-
cairn (the unified memory backend, consumed by
aeolus-cairn) is vendored as a git submodule and built from source with CMake — no dependencies beyond a C++20 toolchain (MSVC, gcc ≥ 12, or clang):git clone --recursive https://github.com/anemoi-ai/aeolus.git cd aeolus cmake -S vendor/cairn -B vendor/cairn/build -DCMAKE_BUILD_TYPE=Release cmake --build vendor/cairn/build --config ReleaseThe
aeolus-cairnbuild script generates bindings fromvendor/cairn/include/cairn.hand linkscairn_corefrom the CMake build tree (override the location withCAIRN_BUILD_DIR). -
protoc (the Protobuf compiler) must be on
PATHwhen building with--all-features— it is required by transitive dependencies of the vector-store features. Installprotobuf-compiler(Debian/Ubuntu),protobuf(Homebrew/vcpkg), or download a release from protocolbuffers/protobuf.
Then the usual commands work:
cargo build --workspace
cargo nextest run --workspaceCI runs with RUSTFLAGS="-D warnings". All code must pass:
cargo fmt --all -- --check
cargo clippy --workspace --all-features --all-targets -- -D warnings
cargo nextest run --workspace --all-features
cargo bench --no-run # ensure benchmarks compileBefore opening a pull request:
- Add tests. The project uses
cargo-nextest,proptestfor property tests, andinstafor snapshot tests. - Update
CHANGELOG.mdunder[Unreleased]. - Security-sensitive changes require a threat model entry in
docs/security-model.md. - Benchmarked paths (scheduler, policy engine, capability system, checkpoint, audit log, workflow engine, injection guard) require a before/after Criterion run showing no regression > 10 %. See BENCHMARKS.md for the baseline and regression policy.
See docs/architecture.md for the system design and COMPATIBILITY.md for the compatibility and SemVer policy.
Aeolus implements defence-in-depth at the runtime level, not as an afterthought:
- Capability tokens are cryptographically issued and verified; attenuation is strictly monotonic
- WASM tool sandbox provides syscall-level isolation via Wasmtime with WASI filtering and fuel limits
- Linux
unshare(2)namespaces (PID, network, mount) available for terminal and subprocess tools - Network guard prevents SSRF, DNS rebinding, and RFC-1918 / link-local / CGN access
- Audit log is SHA-256 hash-chained — every entry links to its predecessor
- Six libfuzzer targets (injection guard, capability attenuation, audit chain, WASM shim, OPA engine, encryption) in
fuzz/— run locally or in long-duration CI withcargo fuzz
To report a vulnerability: security@anemoi.ltd
PGP fingerprint: 8821 67A2 C050 A409 AB70 64EC 79C4 AFE9 329F 0FB8
We acknowledge within 48 hours and target a 90-day fix timeline.
Full policy and threat model: SECURITY.md
Licensed under Apache-2.0.
Copyright 2026 Anemoi Ltd and the Aeolus contributors.