Architecture
Why the engine never imports a model provider, a store or any other integration directly.
afe follows the hexagonal architecture (ports and adapters). The engine sits in the middle. It defines the ports it needs, and every integration is an adapter plugged into a port.
The three rings
| Ring | Package | Contains |
|---|---|---|
| Ports | afe-sdk | Protocols and Pydantic messages. No LangChain, no LangGraph. |
| Engine | afe-core | Manifests, validation, compiler, nodes, engine, budget, loop limits. Uses LangGraph internally. Ships in-memory adapters so it runs alone. |
| Adapters | plugins/* | Everything else. Import name afe_plugins.<name>. |
Driven adapters are called by the engine. Driving adapters call the engine.
- Driven: model providers, the store (Postgres) and its checkpointer, the broker and lock (Redis), memory and the embedder, and the sandbox (Docker) and its egress.
- Driven too: the harnesses (
basic,deep,acp), the forge (GitHub), the tracer (Langfuse, OTLP), and tools (MCP, HTTP). - Driving: the CLI, and the web UI.
The engine never imports an adapter. CI enforces it with import-linter.
How the same rings run on Kubernetes, with no shared storage and an operator, is on the Kubernetes page.
Plugin contract
Each port is a Protocol with Pydantic messages. A plugin implements it through one of two transports, with the same contract:
- In process: a Python package registered through an entry point.
- Out of process: JSON-RPC 2.0, so a plugin can be written in any language. Two transports: stdio, where the engine starts the plugin as a child process, and HTTP, where the plugin runs as its own service.
Plugins declare the API version they target. The engine refuses a different major version.
Engine API
CLI and web UI are clients of the engine API, JSON-RPC 2.0 over WebSocket. The same connection starts, resumes, cancels and pauses tickets, reads ticket state, and receives events. There is no separate REST API.
Manifests
Configuration is a set of Kubernetes-style manifests, several per file if needed.
apiVersion: afe.dev/v1alpha1
kind: Agent
metadata:
name: reviewer
annotations:
afe.dev/description: Checks the text against the request
labels:
team: editorial
spec:
harness: basic
model: cheap
reads:
- text-request
- text
writes: review
Kinds: Flow, Agent, Schema, Project, Lane, Script, Model, McpServer, AcpAgent,
Plugin.
The Pydantic models are the source. A JSON Schema is generated from them for editor validation,
and published in the repository (schemas/) and on this site.