CrewAI + Aarmos

Framework guide · CrewAI

CrewAI under Aarmos in 60 seconds.

Keep your existing CrewAI crew exactly as-is. No aarmos import, no framework wrapper, no monkey-patch. Environment variables route every outbound HTTP call through the local aarmos daemon, where the policy gate decides and a signed AVAR receipt is written.

Runnable example: examples/crewai/ in the repo. Mirrored below.

0 · Sanity-check your environment

aarmos doctor

Verifies daemon reachability, proxy env, LLM keys, and that the workspace has a valid policy bundle.

1 · Start the daemon

aarmos daemon
▸ listening on 127.0.0.1:7681
▸ policy: ./aarmos.json

2 · Your existing CrewAI crew — unchanged

# src/agent.py — stock CrewAI. No aarmos imports.
from crewai import Agent, Task, Crew

researcher = Agent(role="Researcher", goal="Answer briefly.",
                   backstory="You explain in one sentence.",
                   allow_delegation=False)
task = Task(description="What is a receipt?",
            expected_output="One sentence.", agent=researcher)
print(Crew(agents=[researcher], tasks=[task]).kickoff())

3 · Run it under Aarmos

aarmos proxy &                 # start the local runtime (mints a session)
eval "$(aarmos env)"           # authenticated HTTPS_PROXY + HTTP_PROXY
python src/agent.py

▸ gate: api.openai.com  (verb: communicate)  ✓ allowed
▸ AVAR receipt: .aarmos/avar/2026-…json

4 · Inspect the receipt

aarmos verify .aarmos/avar/latest.json
✓ signature valid
✓ chain intact
✓ policy match: aarmos.json@sha256:…

Or open /audit in the PWA for the same call with hashes, verb, latency, and the policy that allowed it.

5 · Who spawned whom (optional)

A crew is not one agent. The proxy sees the calls a worker makes; it cannot see that the manager spawned that worker — CrewAI keeps that in process memory. The adapter declares each spawn as it happens, so evidence can answer which step of which crew made the call.

# optional — attribution only, never enforcement
from aarmos_crewai import govern

crew = Crew(agents=[researcher], tasks=[task])
govern(crew, invocation_id="nightly-close-2026-08-10")
crew.kickoff()
aarmos lineage show nightly-close-2026-08-10
✓ crewai-manager [agent] — declared by the crewai adapter at spawn time
  ✓ Researcher [subagent] — declared by the crewai adapter at spawn time

Without the adapter, actors seen in the same run and process may be correlated for readability — shown as a guess, and excluded from the --attributed view an export or receipt uses. Declared edges are attribution; correlation never quietly becomes one.

Why no CA certificate?

The proxy tunnels HTTPS by hostname and never decrypts it, so your existing certificate chain is untouched — no root CA to install, noREQUESTS_CA_BUNDLE or SSL_CERT_FILE to set. The trade-off: receipts from proxied calls are hostname-level. Install an adapter when you want prompt- and argument-level detail.

Honest limits

  • CrewAI tools that shell out to native binaries bypass the proxy — declare them as connectors instead of trusting env vars.
  • Local function-tools (pure Python callables) do not touch the network and are not gated by this path.
  • Streaming responses hash on close; a truncated stream is recorded as truncated.

Interception runs on your device. Tamper-evident, not tamper-resistant against a modified build — see the threat model.

LangChain guide → · AutoGen guide → · AVAR spec