Architecture

An engine that records every state change and replicates it across a Raft cluster. Two stores (engine and flow), one execution model, and plans that do not change while they run.

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, cluster health)"] 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 and cluster health, including 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, where a side effect is a change outside Flow state such as charging a card or updating a database. State is reconstructed by replaying the event log. This gives:

  • Determinism: same events always produce the same state
  • Durability: committed state survives retries and restarts; services use receipt tokens to prevent duplicate external changes
  • 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. Idempotency means a repeated request does not repeat a side effect. Duplicate webhook completions for the same token are rejected automatically. Services must also use this token when an invocation could otherwise repeat a side effect.

Memoization Cache

Steps marked memoizable: true cache their results in a least-recently-used (LRU) cache local to each engine instance. The cache is in memory only, is not shared across nodes, and is not preserved when the process restarts.