StatifierRouter.BasicHTTP (StatifierRouter v0.9.2)

Copy Markdown View Source

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:

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.

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

cancel(cancel, ctx)

Plans one cancellation with Statifier.Send.BasicHTTP.cancel/2, unchanged.

deliver(send, event, ctx)

Plans one send with Statifier.Send.BasicHTTP.deliver/3, unchanged.

ioprocessors_entry(type, map)

@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.

location(config, execution_id)

@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.

perform(payload, ctx)

@spec perform(term(), Statifier.Send.Processor.ctx()) :: :ok | {:error, term()}

Performs one instruction with Statifier.Send.BasicHTTP.perform/2, unchanged.

rotate_location(config, execution_id)

@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.