AttractorEx.HTTP exposes a lightweight Bandit-backed service for running pipelines remotely.
Main Entry Point
Start the service with:
{:ok, pid} = AttractorEx.start_http_server(port: 4041)This starts:
AttractorEx.HTTP.Managerfor durable pipeline state- a duplicate-key
Registryfor event subscribers AttractorEx.HTTP.Routerfor the HTTP surface
By default the manager persists runtime state under tmp/attractor_http_store. Pass
store_root: ... to AttractorEx.start_http_server/1 when you need a different
location.
Primary Endpoints
The router exposes these endpoints:
POST /pipelinesGET /pipelinesGET /pipelines/:idGET /pipelines/:id/eventsPOST /pipelines/:id/cancelPOST /pipelines/:id/resumeGET /pipelines/:id/graphGET /pipelines/:id/questionsPOST /pipelines/:id/questions/:qid/answerGET /pipelines/:id/checkpointGET /pipelines/:id/context
It also includes definition-of-done compatibility aliases:
POST /runGET /statusPOST /answer
Event Streaming
GET /pipelines/:id/events supports both:
- JSON polling
- server-sent events when streaming is enabled
- replay windows via
?after=<sequence>for either mode
The HTTP manager records engine events like:
PipelineStartedStageStartedCheckpointSavedStageCompletedStageFailedPipelineCompletedPipelineFailed
Each persisted event includes a monotonic sequence so clients can resume event
consumption after reconnects or process restarts.
Graph Rendering
GET /pipelines/:id/graph supports multiple formats:
svgdotjsonmermaidtext
AttractorEx.HTTP.GraphRenderer is responsible for the non-DOT renderings.
Human Gate Integration
When service mode is used, AttractorEx.HTTP.Manager runs pipelines with AttractorEx.Interviewers.Server, which means wait.human questions become pending HTTP resources that can be listed and answered out-of-process.
Accepted answers are persisted into the run context and, when a checkpoint exists, into the checkpoint-backed context snapshot as well. That keeps the post-answer cancelled packet truthful to inspect and gives the control plane durable evidence for the explicit resume contract below.
Recovery and Replay
The HTTP runtime now persists:
- run metadata
- append-only event history
- pending questions
- checkpoint snapshots
- artifact indexes for files under each run directory
On boot, persisted runs are reloaded before serving requests. Incomplete runs are restarted from their latest checkpoint when one exists, otherwise from their initial context.
POST /pipelines/:id/resume is intentionally narrower than boot-time recovery. It
accepts one explicit checkpoint-backed resume only when all of the following are true:
- the run is already in
cancelledstate - a persisted checkpoint snapshot exists
- no pending questions remain
- a human answer has been durably recorded in context or checkpoint context
If the contract is not met, the endpoint returns 409 with a reason string instead of
falling back to retry, replay, or restart semantics. Status payloads also expose:
resume_readyfor backwards-compatible readiness checksrecovery.stateasrefusedoravailablerecovery.actionas the explicit admitted action namerecovery.refusal_reasonwhen the selected packet is not yet recovery-readyrecovery.known_limitso clients can keep the packet inside the current claim ceiling
That recovery contract is intentionally packet-specific. It lets operator surfaces show explicit refusal and explicit availability on one selected cancelled packet without over-claiming broader retry, replay, restart, or non-selected-route recovery support.
Focused Verification
Run the maintained HTTP API suite with:
mix attractor.http
By default this task runs:
test/attractor_ex/http_manager_test.exstest/attractor_ex/http_test.exstest/attractor_ex/conformance/transport_conformance_test.exs
Pass a specific test path when you want a narrower slice:
mix attractor.http test/attractor_ex/http_test.exs
Noor Halden's first schema-backed contract check now has its own hello-world lane:
mix attractor.http.hello
That lane runs from qa/http_hello/ under MIX_ENV=api_test, so it stays out of
the default mix test loop.