Preview release.These docs are a work in progress. Pages are still being written, links may break, and structure may shift without notice. Treat everything here as a draft and report issues onGitHub.
Use the script capability when one fixed HTTP request is not enough. Rhai is the v1 script runtime, but the capability is product-neutral. A DHIS2 label or version does not enable it, and an unknown source product can use exactly the same host API.
The worker has no network, filesystem, environment, subprocess, clock, random, credential, or production logging access. It asks the Relay parent to make each bounded source call. Relay validates the complete request against the authored same-origin authority, commits the request before dispatch, consumes the next durable call permit, and returns only bounded JSON or text.
Try the bundled script starter
Section titled “Try the bundled script starter”The DHIS2 Tracker starter is one concrete script example. Its product name and version are review evidence only; any reviewed source system can use the same script capability and host API.
Run these commands in order. The focused test prints a safe synthetic trace. The next command starts the same fixture as a watch smoke; press Ctrl+C after its initial passing report. The final three commands test every fixture, explain the redacted plan, and build the unsigned product inputs.
dhis2-tracker: DHIS2 Tracker
A product-neutral script adapter applied to a bounded DHIS2 Tracker read journey.
registryctl init --from dhis2-tracker --project-dir dhis2-project
registryctl test --project-dir dhis2-project --integration health-record --fixture complete-health-match --trace
registryctl test --project-dir dhis2-project --integration health-record --fixture complete-health-match --watch
registryctl test --project-dir dhis2-project
registryctl check --project-dir dhis2-project --environment local --explain
registryctl build --project-dir dhis2-project --environment localGenerated from the five starter workspaces compiled into registryctl.
Declare source authority
Section titled “Declare source authority”The public integration declares input schemas, authentication interface, allowed source paths, the script file, outputs, and only the limits that differ from the defaults:
version: 1id: person-statusrevision: 1
input: person_id: role: selector type: string maxLength: 64
source: product: fictional-registry versions: { unverified: [project-api-v1] } auth: { type: static_bearer } allow: - method: GET path: /v1/people/** response: format: json response_headers: [Link]
capability: script: file: adapter.rhai
outputs: active: { type: boolean } programme_code: { type: [string, "null"], maxLength: 32 }Source product and version are interoperability evidence only. Runtime dispatch uses the generic source, capability, and environment contracts.
Every input lives in one typed map and declares role: selector or role: parameter. An
integration has one to eight non-null selectors and at most sixteen total inputs. The canonical
selector values share a fixed 4096-byte ceiling. A nullable parameter is still present in the
consultation request as explicit JSON null; the adapter receives the validated value beneath
ctx.input.
An asterisk admits one non-empty path segment. A terminal double asterisk admits zero or more
complete non-empty segments. Matching is case-sensitive. Dot segments, encoded separators,
fragments, user information, and another origin fail before credentials are resolved. A source
POST also requires semantics: read_only and evidence that the admitted operation is
non-mutating.
The defaults are five calls, 64 KiB of aggregate author-controlled request bytes, a 512 KiB per-response body, 2 MiB of aggregate source-response bytes, and a 15 second deadline. The hard ceilings are 16 calls, 1 MiB of request bytes, 8 MiB per response, 16 MiB of source responses, and 60 seconds. Deployment settings may lower these limits but cannot widen the reviewed integration.
Write the adapter
Section titled “Write the adapter”Create adapter.rhai with one consult(ctx) function:
fn consult(ctx) { let target = source.path( "/v1/people/{person_id}", #{ person_id: ctx.input.person_id } ); let response = source.get(target, #{ query: #{ fields: "id,active,programmeCode" } });
if response.status == 404 { return result.no_match(); } if response.status != 200 { return result.fail(failure.source_rejected); } if response.body.id != ctx.input.person_id { return result.fail(failure.subject_mismatch); }
result.match(#{ active: response.body.active, programme_code: response.body.programmeCode })}The host API provides source.path, source.get, source.post_json, source.post_form, the three
successful result constructors, and fixed source failure constants. Every response has status,
body, and the safe headers named by source.response_headers. The body is bounded decoded JSON
by default. Set source.response.format: text when the reviewed endpoint returns text. Non-2xx
statuses return normally except 401, 403, and 429, which remain host-owned failures. JSON
null is Rhai unit. A match returns every declared output with its exact type and nullability.
An adapter may branch, transform collections, repeat a request, and follow a bounded same-origin pagination link. Relay canonicalizes an absolute returned link back to a relative source reference, rejects another origin, and reapplies the path, method, header, request, response, call, and deadline checks before dispatch. Each call is independently authorized and counts against the same cumulative budgets.
Split an adapter into local modules
Section titled “Split an adapter into local modules”For a larger adapter, list up to 32 project-local Rhai files in their deterministic evaluation order:
capability: script: file: adapter.rhai modules: - lib/normalization.rhai - lib/cardinality.rhaiLocal modules contain ordinary top-level Rhai functions. They cannot add source authority or load
another file at runtime. registryctl resolves each normalized .rhai path beneath the integration
directory without following symlinks, combines the declared modules with the entrypoint, and
enforces a 64 KiB compiled-script ceiling. Module paths, order, and contents are hash-covered by the
authored and generated closures, so a module edit changes the review and deployment inputs. There
is no ambient module search path, remote package resolution, or runtime import fallback.
Add interaction fixtures
Section titled “Add interaction fixtures”Committed fixtures must be wholly synthetic and prove the rendered request as well as response:
name: matchclassification: syntheticinput: person_id: SYNTHETIC_PERSON_001interactions: - expect: method: GET path: /v1/people/SYNTHETIC_PERSON_001 query: fields: id,active,programmeCode respond: status: 200 body: id: SYNTHETIC_PERSON_001 active: true programmeCode: DEMOexpect: outcome: match outputs: active: true programme_code: DEMOKeep match, no-match, ambiguity, identifier-mismatch, and meaningful project variations. The harness derives malformed decoding, limits, timeout, authorization-before-source, output minimization, and protocol-verification negatives. Large synthetic bodies belong in fixtures/bodies/.
Verify
Section titled “Verify”registryctl test --project-dir <project> --integration person-status --fixture match --traceregistryctl test --project-dir <project> --integration person-status --watchregistryctl check --project-dir <project> --environment <environment> --explainOffline tests are portable and use no real credentials or network. Production activation remains Linux-only while the process controls are code-owned. Fixture success proves the adapter against synthetic observations, not production source interoperability or deployment approval.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause | Fix |
|---|---|---|
| A source call fails before dispatch | The method, path, target, header, query, body, or remaining budget is outside authored authority. | Narrowly admit the real read operation or correct the adapter. |
| A terminal result is rejected | The adapter omitted an output or returned a wrong name, type, nullability, or size. | Return the complete declared output map. |
| A fixture response matches but its request proof does not | Its expected interaction differs from the production canonical request. | Update the synthetic request expectation or fix the adapter. |
| Production reports the worker unavailable | The adjacent code-owned worker or required Linux isolation is absent. | Correct the deployment. Do not configure an in-process fallback. |
Return to Author an HTTP Registry Stack project to review and build the project.