Skip to content

Repository files navigation

Aeolus

The production-grade AI agent runtime for Rust.
Type-safe. Async-first. Relentlessly engineered.

CI MSRV 1.85 v0.1.0 Apache-2.0 Alpha 19 LLM Providers


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.


What you get

  • 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 memoryaeolus-cairn backed 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 protocolPOST /agui/v1/:agent_type SSE endpoint compatible with CopilotKit and any AG-UI client. Real-time state sync, interrupts, and reasoning streams.
  • Durable executionStepExecutor with 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 StreamingTool interface 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, and AgentTrigger. 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.

Quick Start

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.


Core Concepts

Agents

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

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

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.


LLM Providers

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.


Security

Capability-based access control

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.

Policy engine

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?

Injection guard

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?

Cryptographic audit log

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?;

Durable Execution

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.


Multi-Agent Orchestration

Spawn children from context

// 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);

DAG workflows

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?;

Agent-to-Agent (A2A) protocol

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.


Deployment

HTTP server

aeolus-server \
  --http-addr  0.0.0.0:8080 \
  --grpc-addr  0.0.0.0:9090 \
  --policy     /etc/aeolus/policy.toml

REST 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.

CLI

# 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>

Kubernetes

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-credentials

Scheduled runs with AgentCronJob, reusable templates with AgentRunTemplate, event-driven execution with AgentTrigger. The operator runs with leader election for high availability.

Edge (WASM)

[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.


Testing

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.


Observability

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?

Enterprise

Enable the enterprise feature for multi-tenant deployments.

Multi-tenancy

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);

RBAC / ABAC

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));

Encryption at rest

// 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?;

Compliance profiles

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 format

Available profiles: Soc2Type2, Hipaa, Gdpr, Dora, FedRampModerate, and Custom.


Feature Flags

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

Performance

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.


Documentation

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

Project Structure

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

Examples

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.


Building from source

Aeolus builds with stable Rust (MSRV 1.85, pinned via rust-toolchain.toml), but two native prerequisites are needed:

  1. 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 Release

    The aeolus-cairn build script generates bindings from vendor/cairn/include/cairn.h and links cairn_core from the CMake build tree (override the location with CAIRN_BUILD_DIR).

  2. protoc (the Protobuf compiler) must be on PATH when building with --all-features — it is required by transitive dependencies of the vector-store features. Install protobuf-compiler (Debian/Ubuntu), protobuf (Homebrew/vcpkg), or download a release from protocolbuffers/protobuf.

Then the usual commands work:

cargo build --workspace
cargo nextest run --workspace

Contributing

CI 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 compile

Before opening a pull request:

  1. Add tests. The project uses cargo-nextest, proptest for property tests, and insta for snapshot tests.
  2. Update CHANGELOG.md under [Unreleased].
  3. Security-sensitive changes require a threat model entry in docs/security-model.md.
  4. 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.


Security

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 with cargo 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


License

Licensed under Apache-2.0.

Copyright 2026 Anemoi Ltd and the Aeolus contributors.

About

Production grade Agentic runtime

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages