The W3C Basic HTTP Event I/O Processor for durable executions: the
processor this package registers for a configuration that sets
:basichttp, and the location of each execution it reaches (ADR-0002,
the Amendment of 2026-09-30).
A location is a bearer capability. Anyone who holds it can post
events to that execution, and the router authenticates nothing beyond
possession of the location (ruled by the operator, 2026-09-30). Hand a
location only to the parties that should reach the execution, keep it
out of logs and out of URLs shown to others, serve the base URL over
TLS, and rotate it with rotate_location/2 when it may have leaked.
Registering it
Set :basichttp on StatifierRouter.Config with the base URL the
host's front answers at:
StatifierRouter.Config.new(
repo: MyApp.Repo,
# ... the delivery options ...
basichttp: [base_url: "https://example.org/scxml"]
)The snapshot the configuration builds then registers this module under
the processor's URI, http://www.w3.org/TR/scxml/#BasicHTTPEventProcessor,
and its short form basichttp, with the key's options:
:base_url(required) - a non-empty string, the addressStatifierRouter.BasicHTTP.Frontanswers at. A location is this URL,/, and the execution's token.:transport- aStatifier.Send.BasicHTTP.Transportmodule the outbound POSTs go through; left out, statifier's default.
The key needs the location table StatifierRouter.Migrations.V04
creates.
The location
When a delivery creates an execution under an address row it has just
inserted, it mints a token - 32 random bytes as unpadded URL-safe
base64, 43 characters from A-Z a-z 0-9 - _ - stores it beside the
address row, and hands it to this module's ioprocessors_entry/2
through the create's registration, so the execution's _ioprocessors
carries base_url <> "/" <> token under both type strings. The token is
derived from nothing: not the address, not the execution id.
An execution created under :always_new has no address row and so no
location: its entry carries no "location" key. An execution created
before the key was set has no location either, until rotate_location/2
gives it one; its own _ioprocessors, written when it started, stays as
it was.
location/2 reads an execution's current location and
rotate_location/2 replaces its token, after which the old location is
answered 404 by the front. Rotation does not reach the execution's own
_ioprocessors: statifier writes that entry once, when the execution
starts, so after a rotation the chart still reads the location it
started with, which no longer answers. A host that rotates hands the new
location to whoever should hold it. Both functions build the location
from the configuration's current base URL.
Outbound
deliver/3, cancel/2 and perform/2 hand each call to
Statifier.Send.BasicHTTP unchanged: the outbound half is statifier's
processor.
Summary
Functions
Plans one cancellation with Statifier.Send.BasicHTTP.cancel/2, unchanged.
Plans one send with Statifier.Send.BasicHTTP.deliver/3, unchanged.
The _ioprocessors entry for type: %{"location" => base_url <> "/" <> token} when the registration carries a :location_token, and %{},
an entry with no location, when it does not.
The current location of the execution execution_id, as {:ok, location}, or {:error, :no_location} when its address row has no
location or no address row names it.
Performs one instruction with Statifier.Send.BasicHTTP.perform/2, unchanged.
Mints a new token for the execution execution_id and makes it its
location, in one statement that inserts the location of its address row
or replaces its token. From the commit on, the old location reaches
nothing and the front answers it 404.
Functions
@spec cancel(Statifier.Effect.Cancel.t(), Statifier.Send.Processor.ctx()) :: {:ok, [Statifier.Send.Processor.instruction()]}
Plans one cancellation with Statifier.Send.BasicHTTP.cancel/2, unchanged.
@spec deliver( Statifier.Effect.Send.t() | Statifier.Effect.SendDelayed.t(), Statifier.Event.t(), Statifier.Send.Processor.ctx() ) :: {:ok, [Statifier.Send.Processor.instruction()]}
Plans one send with Statifier.Send.BasicHTTP.deliver/3, unchanged.
@spec ioprocessors_entry(String.t(), Statifier.Send.Processor.entry_context()) :: map()
The _ioprocessors entry for type: %{"location" => base_url <> "/" <> token} when the registration carries a :location_token, and %{},
an entry with no location, when it does not.
@spec location(StatifierRouter.Config.t(), String.t()) :: {:ok, String.t()} | {:error, :no_location}
The current location of the execution execution_id, as {:ok, location}, or {:error, :no_location} when its address row has no
location or no address row names it.
@spec perform(term(), Statifier.Send.Processor.ctx()) :: :ok | {:error, term()}
Performs one instruction with Statifier.Send.BasicHTTP.perform/2, unchanged.
@spec rotate_location(StatifierRouter.Config.t(), String.t()) :: {:ok, String.t()} | {:error, {:no_address, String.t()}}
Mints a new token for the execution execution_id and makes it its
location, in one statement that inserts the location of its address row
or replaces its token. From the commit on, the old location reaches
nothing and the front answers it 404.
Returns {:ok, location}, the new location, or {:error, {:no_address, execution_id}} when no address row names the execution, which is what
an :always_new execution answers.