ReactorEvents
in package
The CONTRACT.md §22.5 event registry, §22.8's budget constants and §22.1's topology helpers.
The registry is served live at GET /api/v1/reactors/events, and that is the
copy an admin UI SHOULD read. This class restates it because a reactor runtime
has to validate an incoming event name and a handler's patch keys on the
delivery path, where a network call is not available — the same reason the
contract restates it in prose and the server keeps it as pure data in
crates/axiam-core/src/models/reactor.rs.
WHAT IS DELIBERATELY ABSENT IS LOAD-BEARING. The three hot-path decision
operations — the single authorization check, the batch check and token
introspection — are not hookable (§22.7, a MUST NOT), so they appear in no
constant, no list and no example anywhere under src/Reactor/. Their wire
names are not written here either, so ReactorHotPathExclusionTest can enforce
the rule with a plain scan of this directory rather than a judgement call about
which mentions are innocent.
The reason is arithmetic, not policy: a reactor round-trip is milliseconds and the check path's budget is microseconds. An application that needs external input on an authorization decision writes a deny grant, which the engine evaluates in the hot path at hot-path cost.
Table of Contents
Constants
- CHAIN_CEILING_MS : mixed = 5000
- Wall-clock ceiling on a whole dispatch chain (§22.8). Reactors not reached inside it are **not contacted**, and each of their own failure policies is applied anyway — so an unreached `fail_closed` veto still denies.
- DEFAULT_TIMEOUT_MS : mixed = 500
- The per-dispatch timeout a registration gets when it names none (§22.8).
- EXCHANGE : mixed = 'axiam.reactor.events'
- The topic exchange every reactor event is published to (§22.1). The SERVER declares it; a reactor runtime never does.
- EXCHANGE_TYPE : mixed = 'topic'
- Type of {@see self::EXCHANGE} (§22.1). Stated for assertions and admin tooling only — a reactor never declares an exchange.
- FAIL_CLOSED : mixed = 'fail_closed'
- Failure policy (§22.8): deny the underlying operation, with an audited reason naming the failure.
- FAIL_OPEN : mixed = 'fail_open'
- Failure policy (§22.8): proceed as if the reactor had replied `allow`.
- GRANT_PRE_ASSIGN : mixed = 'grant.pre_assign'
- Fires before a role or permission assignment. Veto-only — four-eyes workflows live here.
- LOGIN_POST_AUTH : mixed = 'login.post_auth'
- Fires after credentials verify and before any session or token is issued — on password login, on SAML ACS and on the OIDC callback alike (§22.5, SEC-095). Veto-only, and the only event on which `require_mfa` is meaningful.
- MAX_TIMEOUT_MS : mixed = 5000
- Upper bound on `timeout_ms` at registration (§22.8).
- MIN_TIMEOUT_MS : mixed = 1
- Lower bound on `timeout_ms` at registration; `0` is refused (§22.8).
- MODE_INTERCEPT : mixed = 'intercept'
- Registration mode (§22.9): synchronous request/response — the server waits and the reply can veto or mutate within the event's allow-list.
- MODE_LISTEN : mixed = 'listen'
- Registration mode (§22.5): fire-and-forget observation. The server never waits and never reads a reply, so a listener cannot affect any outcome — and a listener handler MUST NOT publish one.
- TOKEN_PRE_ISSUE : mixed = 'token.pre_issue'
- Fires before an access token is issued. Mutable: claims under the `ext.` namespace only.
- USER_PRE_CREATE : mixed = 'user.pre_create'
- Fires before a user is created.
- USER_PRE_UPDATE : mixed = 'user.pre_update'
- Fires before a user profile is updated.
Methods
- all() : array<int, ReactorEventSpec>
- The §22.5 registry, mirroring `EVENT_REGISTRY` in `crates/axiam-core/src/models/reactor.rs`.
- assertHookable() : void
- Assert that $event is a hookable §22.5 registry event, or throw.
- defaultFailurePolicy() : string
- Composes the failure policy a registration naming none inherits from its events (§22.8): the **strictest default wins**, in either array order.
- queueName() : string
- Renders the durable per-reactor queue the **server** declares: `axiam.reactor.q.<tenant_id>.<reactor_id>` (§22.1). Mirrors `queue_name()` in `crates/axiam-amqp/src/reactor/protocol.rs`.
- routingKey() : string
- Renders the topic routing key for one event: `<tenant_id>.<event>` (§22.1).
- specFor() : ReactorEventSpec|null
- Looks an event up by wire name, returning null for any name outside the §22.5 registry.
Constants
CHAIN_CEILING_MS
Wall-clock ceiling on a whole dispatch chain (§22.8). Reactors not reached inside it are **not contacted**, and each of their own failure policies is applied anyway — so an unreached `fail_closed` veto still denies.
public
mixed
CHAIN_CEILING_MS
= 5000
DEFAULT_TIMEOUT_MS
The per-dispatch timeout a registration gets when it names none (§22.8).
public
mixed
DEFAULT_TIMEOUT_MS
= 500
EXCHANGE
The topic exchange every reactor event is published to (§22.1). The SERVER declares it; a reactor runtime never does.
public
mixed
EXCHANGE
= 'axiam.reactor.events'
EXCHANGE_TYPE
Type of {@see self::EXCHANGE} (§22.1). Stated for assertions and admin tooling only — a reactor never declares an exchange.
public
mixed
EXCHANGE_TYPE
= 'topic'
FAIL_CLOSED
Failure policy (§22.8): deny the underlying operation, with an audited reason naming the failure.
public
mixed
FAIL_CLOSED
= 'fail_closed'
FAIL_OPEN
Failure policy (§22.8): proceed as if the reactor had replied `allow`.
public
mixed
FAIL_OPEN
= 'fail_open'
GRANT_PRE_ASSIGN
Fires before a role or permission assignment. Veto-only — four-eyes workflows live here.
public
mixed
GRANT_PRE_ASSIGN
= 'grant.pre_assign'
LOGIN_POST_AUTH
Fires after credentials verify and before any session or token is issued — on password login, on SAML ACS and on the OIDC callback alike (§22.5, SEC-095). Veto-only, and the only event on which `require_mfa` is meaningful.
public
mixed
LOGIN_POST_AUTH
= 'login.post_auth'
MAX_TIMEOUT_MS
Upper bound on `timeout_ms` at registration (§22.8).
public
mixed
MAX_TIMEOUT_MS
= 5000
MIN_TIMEOUT_MS
Lower bound on `timeout_ms` at registration; `0` is refused (§22.8).
public
mixed
MIN_TIMEOUT_MS
= 1
MODE_INTERCEPT
Registration mode (§22.9): synchronous request/response — the server waits and the reply can veto or mutate within the event's allow-list.
public
mixed
MODE_INTERCEPT
= 'intercept'
MODE_LISTEN
Registration mode (§22.5): fire-and-forget observation. The server never waits and never reads a reply, so a listener cannot affect any outcome — and a listener handler MUST NOT publish one.
public
mixed
MODE_LISTEN
= 'listen'
TOKEN_PRE_ISSUE
Fires before an access token is issued. Mutable: claims under the `ext.` namespace only.
public
mixed
TOKEN_PRE_ISSUE
= 'token.pre_issue'
USER_PRE_CREATE
Fires before a user is created.
public
mixed
USER_PRE_CREATE
= 'user.pre_create'
USER_PRE_UPDATE
Fires before a user profile is updated.
public
mixed
USER_PRE_UPDATE
= 'user.pre_update'
Methods
all()
The §22.5 registry, mirroring `EVENT_REGISTRY` in `crates/axiam-core/src/models/reactor.rs`.
public
static all() : array<int, ReactorEventSpec>
A fresh ReactorEventSpec list is built per call so a caller cannot edit the SDK's own allow-lists in place: an allow-list a caller can widen is not an allow-list.
Return values
array<int, ReactorEventSpec>assertHookable()
Assert that $event is a hookable §22.5 registry event, or throw.
public
static assertHookable(string $event) : void
The single validation both spellings of §22.14's declarative binding go through — OnReactorEvent and ReactorHandlers::bind() — so the two cannot drift apart.
Parameters
- $event : string
-
The candidate event name.
Tags
defaultFailurePolicy()
Composes the failure policy a registration naming none inherits from its events (§22.8): the **strictest default wins**, in either array order.
public
static defaultFailurePolicy(array<int, string> $events) : string
A reactor registered for both token.pre_issue (open) and
login.post_auth (closed) can veto a login, so it inherits fail_closed.
Taking the first event's default instead would let the order of a JSON array
decide whether an unreachable fraud check passes — which is why §22.8 states
this as a MUST NOT reimplement rather than as a note.
An unknown event name contributes fail_closed: the server will refuse the
registration outright, and guessing open on a name this SDK does not
recognise is the wrong way to be wrong. An empty list is fail_closed for
the same reason.
Parameters
- $events : array<int, string>
Return values
stringqueueName()
Renders the durable per-reactor queue the **server** declares: `axiam.reactor.q.<tenant_id>.<reactor_id>` (§22.1). Mirrors `queue_name()` in `crates/axiam-amqp/src/reactor/protocol.rs`.
public
static queueName(string $tenantId, string $reactorId) : string
Deriving the name is not the same as declaring it. A reactor consumes this
queue and nothing else; it never declares, redeclares or binds it, and never
derives a name for a reactor other than the one it is configured as. A
reactor that can bind is a reactor that can bind itself to
*.token.pre_issue and read another tenant's issuance events.
Parameters
- $tenantId : string
- $reactorId : string
Return values
stringroutingKey()
Renders the topic routing key for one event: `<tenant_id>.<event>` (§22.1).
public
static routingKey(string $tenantId, string $event) : string
Mirrors routing_key() in crates/axiam-amqp/src/reactor/protocol.rs.
Exported for logging, assertions and admin tooling. A reactor runtime never
binds it: bindings are the server's, derived from the registration's
events.
Parameters
- $tenantId : string
- $event : string
Return values
stringspecFor()
Looks an event up by wire name, returning null for any name outside the §22.5 registry.
public
static specFor(string $name) : ReactorEventSpec|null
The hot-path decision operations §22.7 excludes are absent by construction rather than by a filter that could be forgotten: they are not in self::all(), so this returns null for them like any other unknown name, and the runtime refuses such a delivery before a handler ever sees it.
Parameters
- $name : string