Unreleased documentation. These pages follow the main branch and can change before the next release. For supported guidance, use v0.26.1.
An immediate action is a named registry operation a caller invokes instead of writing records directly. Base Registry Engine (BReg) admits the action’s declared inputs, checks the grant that permits it, and applies a bounded set of writes in one transaction. A fixed-effect action maps admitted inputs onto declared effects. A handler action computes those effects in a Rhai script the project ships. Neither shape adds domain code to the server: an action stays configuration a reviewer can read, and the compiled contract covers its script, ABI, inputs, refusals, and write slots.
When a handler earns its place
Section titled “When a handler earns its place”Reach for a fixed-effect action when the mapping from input to written field is constant, and for
an acceptance-time requires check when the rule is a stored field of an existing reference target
that must hold under the transaction’s lock. Neither needs a script.
A handler earns its place when a written value is computed from more than one input, when a branch decides which of the declared slots is written, or when the action needs a closed catalogue of business refusals that a caller can act on. A handler cannot widen what the action may do: it selects among slots the project already declared.
Write slots and refusals
Section titled “Write slots and refusals”handler sits beside the action’s inputs and declares the script, its ABI, the refusal catalogue,
and every slot the script may emit:
handler: kind: rhai script: scripts/register-person.rhai abi: registry.action-handler/v1 refusals: - {code: blank-name, label: At least one name part is required.} writes: - id: person target: {entity: person} operation: create fields: [identifier, display-name]fn handle(ctx) receives admitted values in ctx.inputs, keyed by logical input IDs, and returns
exactly one of effects or refusal. A slot can be emitted once or omitted; its target,
operation, and field ceiling come from writes, never from the script output. Rhai has no
database query, stored-record snapshot, clock, filesystem, or network access under version 1.
Omitting a slot suppresses its write only. Every declared reference input still needs admission and
compiled target authority, every compiled patch target still requires its condition token and
ordered lock, and action requires checks stay unconditional for a new invocation.
A declared refusal returns 422 action.refused with the catalogue refusalCode, its static label
as detail, and an optional fieldPath naming the public input. Dynamic messages and unknown codes
are not accepted, and a refusal commits no effect, receipt, or event. An unexpected handler failure
or invalid output returns 500 action.handler_failed, which an operator repairs in the project
rather than in the request. Execution deadlines keep 503 service.unavailable.
What version 1 admits
Section titled “What version 1 admits”registry.action-handler/v1 accepts scalar inputs, including references encoded as record-ID
strings, and keeps decimal inputs in their canonical JSON string form. The compiler rejects
crs84-point and structured inputs for this ABI; an action needing those types uses fixed
effects. String and text inputs declare maxLength of at most 4,096 Unicode scalar values, which
keeps every value inside Rhai’s 16,384-byte UTF-8 string budget. Admission rejects any input string
above 16,384 bytes with 400 request.invalid before the script runs.
For this ABI an absent optional input stays absent, while explicit JSON null is a present key
carrying Rhai’s unit value. Use in to tell the two apart before calling a string method. A
fixed-effect action rejects explicit null instead.
Evidence inside a handler
Section titled “Evidence inside a handler”registry.action-handler/v2 is a trial ABI that lets a project-level action call
evidence::resolve(capability_id, subjects) inside handle(ctx). Version 1 input-only actions keep
their existing path. The project declares each provider under evidenceProviders with an id, a
reviewed client contracts file, and subjectResolution: trusted-provider-exact-selector; the
imported contract becomes part of the signed package closure, so compilation and explanation need no
network. Each action’s evidence entries then select a local id, that provider, an exact
requirement URI, the subject role and profile, the output handles the script may read, and
maximumObservationAgeSeconds.
Runtime configuration binds the same provider ID separately from the signed logical contract, under
its own evidenceProviders map with baseUrl, trustBindingId, tokenRef, trustedJwksRef,
revokedKeyIds, and an optional caBundleRef. The endpoint, token, and trusted JWKS are operator
bindings that no script can replace. Computed identifiers stay request values: they cannot become
authenticated claims or grants, and the action grant covers all declared processing, so a caller
input selecting optional disclosure adds no authority.
The limits are fixed, and offline action explanation reports them:
| Limit | Value | Scope |
|---|---|---|
| Calls per declared capability | 1 | Each evidence entry |
| Declared capabilities | 2 | Each action |
| Concurrent evaluations | 8 | Each runtime |
| Response size | 256 KiB | Each call |
| Retained Evidence | 1 MiB | Each action invocation |
| Retention window | 24 hours | Each retained assertion |
| Assertion lifetime | 300 seconds | Each signed response |
| Observation age | 1 to 300 seconds | Declared per capability |
Accepted clock skew is zero. The whole invocation shares one deadline taken from
operationalTimeouts.httpRequest, which defaults to 10,000 milliseconds and is never longer than
the 30-second action ceiling. A failed Evidence dependency returns the static 503 problem, whose
optional fieldPath names /evidence/{alias} for a known compiled capability and never carries
selector values or provider response details.
The host admits the action and its existing targets before external work, releases the PostgreSQL connection, and evaluates Rhai once. Finalization rechecks authority, conditions, constraints, and evidence acceptance, then commits writes, audit, receipt, and the protected evidence-use material atomically. Successful receipt replay calls no provider.
Retention of what an action used
Section titled “Retention of what an action used”An action that resolved Evidence retains the signed response and its verification context for 24 hours so a reviewer can see what the decision rested on. That is a separate retention scope from record history: ordinary history erasure does not cover it, and erase expired Evidence uses is the operator command that removes it once a window has closed.
- Declare change requests and actions to author the action, its inputs, and the grant that invokes it.
- Base Registry Engine configuration reference for every authoring field a fragment names.
- Retain, erase, and audit for the retention scopes an operator runs, including expired Evidence uses.
- Immediate action configuration examples for the full handler contract, the ABI version 2 trial, and its acceptance projects.