Skip to content
Registry StackDocsDevelopment (unreleased)

Where each rule lives

View as Markdown

If you are building a registry service on Registry Stack and ask “this rule or step, where do I put it?”, find its kind in the table, then follow the link to the product that holds it. Each kind of logic has one home, and each home declares its own interface.

  • There is no single cross-product operation grammar. A field rule, an Action plan, a hook, a workflow step, an Adapter route and a message template are each written in the notation of the product that owns them. Rhai appears in several products, but each product gives it its own inputs, helper functions and limits.
  • Two products publish machine-readable operation catalogues. The Registry Coordinator operation catalogue lists every versioned operation a workflow can call, with schemas, effect class, idempotency key, recovery and receipt mode. The Registry Adapter effective contract lists the operations one loaded Adapter exposes to a gateway. Neither covers the other products’ own configuration, which each product’s schemas and explain output describe.
  • Logic implemented outside the stack follows one pattern. Put it behind an Adapter facade route and call it from a workflow through external.command/v1 (a POST that changes something) or external.get/v1 (a read).

Base Registry Engine (BReg) holds the rules that must hold whenever a record changes. The other products hold what happens around a record.

Kind of logicHolder and mechanismInterface it declaresMachine-readable catalogue
Field validation and patternsBReg Record type fields: type, bounds, vocabulary, required, and a pattern in PostgreSQL’s regular-expression syntax. An Action input can carry a pattern too.The field declaration in registry.yamlregistry-project.schema.json; bregctl explain. A pattern is enforced by the database and is not published in the generated JSON Schema or OpenAPI.
Preconditions on current dataBReg Action requires and commitGuards, checked inside the transactionGuard entries on the Actionbregctl explain actions
Derived valuesBReg generated identifiers (generate on a string field) and live derived fields (reviewed SQL compiled to a read-only view)The field declarationregistry-project.schema.json
Rates, thresholds and other parameters that change over timeBReg Record type with temporal validity, one record per period, changed through an Action (a Proposal Action when a change needs review). A plan cannot read stored records, so a workflow reads the record in force with breg.read-in-force/v1 and passes the values to the Action as inputs.The Record type and the Action that changes itregistry-project.schema.json; generated/operations/catalog.json
Calculation and branching in an ActionBReg Action plan: fixed, a Rhai Function, or a Wasm FunctionTyped Action inputs, declared write slots and declared refusal codes; Function ABI registry.action-plan/v1bregctl generate actions writes the action-contracts/v1 document
Record timersBReg Record type timers, enabled by the root project’s timerGrants; each timer invokes one non-public immediate ActionThe timer declaration on the Record typeregistry-project.schema.json; a timer-only Action has no portable Action contract
Events and webhooksBReg hooks: a url, rhai or wasm handler that runs after commit; local handlers use ABI registry.hook-handler/v1trigger, projection and optional when on the hookbregctl explain events; generated/event-schemas/proposal-lifecycle.schema.json
Review, then applyBReg Proposal Action (execution.mode: proposal); Casework supplies the reviewThe Action’s review and application declarationsbregctl generate actions
Multi-step or cross-product processRegistry Coordinator workflow.yaml: call, choose, wait-until, wait-for and finish steps, with pure Rhai FunctionsTyped workflow input and named finishesgenerated/operations/catalog.json and the generated project schema
Calls to systems outside the stackAdapter pack routes (reviewed, in Camel route YAML, with Rhai mapping scripts), bound to a profile by a project and served as a facadeThe profile’s operationsThe Adapter effective contract
Answering a question about a person or record for another serviceRegistry Evidence: an AssertionDefinition, a Source bound to an Adapter pack route, and a derivation FunctionThe public Requirement URI, purpose and subject selectorsgenerated/evidence-definitions-v1.schema.json and the generated OpenAPI
Documents and certificatesRegistry Render (Typst template bundle, byte-stable PDF) and the Registry Document Store (custody and release of a verified version)Render: template bundle and record data. Document Store: document policy and grantsrender/schemas/bundle.schema.json; the Document Store project schema and event schemas
Case stepsRegistry Casework ReviewPolicy: stages, queues, outcomes and routing rulesThe policy in casework.yamlgenerated/project/project.schema.json; caseworkctl explain
NotificationsRegistry Messaging: versioned templates with a JSON Schema each, providers and sender profilesThe template’s input schemagenerated/authoring/template.schema.json; messagingctl generate templates
AppointmentsRegistry Scheduling policy: offerings, holds and bookings, with four observer triggersThe policygenerated/project/project.schema.json; schedulingctl explain

Start with a fixed plan, which writes declared values and needs no code. Move to a Rhai Function when a value must be calculated from the inputs or the Action must decline for a stated reason. A Wasm Function takes the same plan contract for logic written in another language, with narrower rules: it reads only the declared inputs, calls no Evidence resolver, and cannot serve a Proposal Action. All three produce effects only through write slots the compiler admitted, so a script cannot reach a target, field or operation the project did not declare.

A plan runs only when its Action is invoked, and the Action context carries no trusted calendar date. If a rule must not be bypassed, withhold the ordinary create profile that could write the same state without the Action. The details are in Actions and Proposals.

When a rule lives in another system, such as a licensing authority’s calculation service, the stack has no plugin mechanism to host it. Write an Adapter pack route that calls the system, and serve it as a facade route. The Adapter keeps the source credentials, an audit journal and an idempotency store for writes. A workflow then calls the route through one of two operations:

  • external.get/v1 reads. The connection lists exact paths and a required response schema.
  • external.command/v1 sends one POST to the single relative path configured on the connection. The workflow supplies only the JSON body, and the alias’s interpret Function turns the reply into accepted, refused or uncertain. Every attempt carries the same stored body and the same Idempotency-Key, so the receiving route must deduplicate on that header.

Coordinator does not verify the provider’s effect, and neither operation takes an arbitrary method or a destination chosen at run time. The effective contract of the Adapter lists only operations it can serve as reads to a gateway; a write the facade serves appears there as unserved. The Coordinator catalogue therefore remains the place to see what a workflow can call, and the Adapter product page describes packs, profiles and facade routes.