Registry stack documentation: machine-readable Markdown.
Index of all pages: https://docs.registrystack.org/dev/llms.txt
Full corpus: https://docs.registrystack.org/dev/llms-full.txt

# Governed registry actions

> How an immediate action applies bounded writes, where a Rhai handler computes them, and what the Evidence trial adds under action handler ABI version 2.

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

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

`handler` sits beside the action's inputs and declares the script, its ABI, the refusal catalogue,
and every slot the script may emit:

```yaml
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

`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

`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

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](../../operate/breg-retention/#erase-expired-evidence-uses) is the
operator command that removes it once a window has closed.

{/* Evidence: crates/registry-breg/src/contract.rs, ACTION_HANDLER_ABI_V2;
    crates/registry-breg/src/action_evidence_contracts.rs, MAX_EVIDENCE_CAPABILITIES;
    crates/registry-breg/src/action_evidence.rs, MAXIMUM_EVIDENCE_EVALUATIONS;
    crates/registry-breg/src/action_evidence_client.rs, ACTION_EVIDENCE_RETENTION_SECONDS;
    crates/registry-breg/src/runtime_config.rs, DEFAULT_HTTP_REQUEST_TIMEOUT_MILLISECONDS;
    crates/registry-breg/src/mutation/request.rs, REQUEST_ACTION_TIMEOUT. */}

## Next

- [Declare change requests and actions](../../configure/breg-change-control/) to author the action,
  its inputs, and the grant that invokes it.
- [Base Registry Engine configuration reference](../../reference/breg-configuration/) for every
  authoring field a fragment names.
- [Retain, erase, and audit](../../operate/breg-retention/) for the retention scopes an operator
  runs, including expired Evidence uses.
- [Immediate action configuration examples](https://github.com/registrystack/registry-stack/blob/main/products/breg/immediate-actions.md)
  for the full handler contract, the ABI version 2 trial, and its acceptance projects.