Architecture

A Raft-replicated, event-sourced engine running across a cluster of nodes. Two stores (engine and flow), one execution model, no runtime mutation of plans.

Overview

Argyll is a distributed engine built on Raft consensus and event sourcing. A cluster runs three or more engine nodes that share state through a Raft log. Every state change is recorded as an event in an append-only store, and current state is derived by replaying events.

graph TD Clients["Clients
(SDK / curl / web UI)"] Proxy["Reverse proxy
(leader-aware routing)"] N1["Engine node 1"] N2["Engine node 2"] N3["Engine node 3"] EngineStore["EngineStore
(catalog, nodes)"] FlowStore["FlowStore
(flow state)"] Clients --> Proxy Proxy --> N1 Proxy --> N2 Proxy --> N3 N1 -.Raft.- N2 N2 -.Raft.- N3 N1 -.Raft.- N3 N1 --> EngineStore N1 --> FlowStore

Two Stores

State lives in two Raft-backed Timebox stores:

  • EngineStore holds the Step catalog, node registry, and per-node health
  • FlowStore holds Flow state and Flow indexes

Both are event-sourced. Writes commit through the Raft log on quorum; reads are served from the local node’s projected state.

The Engine

Each node runs the same engine. It:

  1. Validates and stores Step definitions in the catalog
  2. Computes Execution Plans from Goal Steps and Step dependencies
  3. Dispatches Step invocations over HTTP
  4. Records Work Item progress as events
  5. Advances Flows based on committed state only

A node can read from local state at any time. Writes route to the Raft leader. The X-Argyll-Raft-State response header on GET /health tells a load balancer which node is the leader.

Event Sourcing

Every state change is recorded as an event before any side effect happens. State is reconstructed by replaying the event log. This gives:

  • Determinism: same events always produce the same state
  • Durability: side effects only happen once even across retries
  • Auditability: a complete record of why a Flow did what it did

See Event Sourcing for event types and aggregates.

Step Invocation

Steps are external HTTP services (or in-engine scripts). The engine calls them; they don’t call each other. Each invocation carries Argyll-Flow-ID, Argyll-Step-ID, and Argyll-Receipt-Token headers. Async Steps additionally receive Argyll-Webhook-URL for callbacks.

The receipt token is the engine’s idempotency key. Duplicate webhook completions for the same token are rejected automatically.

Memoization Cache

Steps marked memoizable: true cache their results in an LRU cache local to each engine instance. The cache is in-memory only, not shared across nodes, not persisted. It survives only as long as the process does.