Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@

- The event bridge accepts `BREG_BIND_HOST=127.0.0.1` for host-local receivers.
Its existing `0.0.0.0` default remains unchanged.
- `@openfn/language-registry-casework` wraps the native Casework client for
requester operations on hosted items and task operations on source inboxes
and grants.

### Changed

- The Evidence, BREG and Casework adaptors pin `@registrystack/client` 0.32.0.
The pilot's Registry Stack images and bootstrap tools remain at 0.27.0.

### Removed

Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ an isolated local pilot.
The maintained pilot uses:

- [Registry Evidence adaptor](packages/registry-evidence), using the `evidence`
namespace of the published `@registrystack/client@0.27.0`.
namespace of the published `@registrystack/client@0.32.0`.
- [Registry BREG adaptor](packages/registry-breg), using the same package's `breg`
namespace for metadata-selected writes, exact lookups and lifecycle actions.
- [Registry Casework adaptor](packages/registry-casework), using the same package's
`casework` namespace for hosted intake, requester polling and task grants.
- Upstream Lightning 2.18.2 and websocket worker 1.29.0, packaged with Node
24.19.0 on glibc and local adaptors. No OpenFn source patch is required.
- Separate BREG and Lightning PostgreSQL databases, Mint, Evidence, an
Expand Down
2 changes: 1 addition & 1 deletion deployment/check-worker.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const require = createRequire('/opt/registry-adaptors/package.json');
const client = require('@registrystack/client');
assert.equal(typeof client.evidence.EvidenceClient, 'function');
new client.breg.BaseRegistryClient({ baseUrl: 'http://127.0.0.1:8090' });
assert.equal(JSON.parse(readFileSync('/opt/registry-adaptors/node_modules/@registrystack/client/package.json')).version, '0.27.0');
assert.equal(JSON.parse(readFileSync('/opt/registry-adaptors/node_modules/@registrystack/client/package.json')).version, '0.32.0');
assert.ok(process.report.getReport().header.glibcVersionRuntime, 'worker must use glibc');
const { default: compile, preloadAdaptorExports } = await import('/app/packages/compiler/dist/index.js');
const { default: run } = await import('/app/packages/runtime/dist/index.js');
Expand Down
2 changes: 1 addition & 1 deletion deployment/images.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"worker": "openfn/ws-worker:v1.29.0@sha256:827f4897d120c322d977da7bc2810174332eb5ba4902fdb7279178812d787c18",
"node": "node:24.19.0-bookworm@sha256:4196d66a565c6f195728d9952f161f4adfe2ad753052a08b7ec7f1c5a6bda42b",
"postgres": "postgres:17.11@sha256:67f41722b7a8cbdb868a44a4995c846eddfdc2973bccb291ce937dce88ad5675",
"registryClientVersion": "0.27.0",
"registryClientVersion": "0.32.0",
"toolsDebianSnapshot": "20260824T000000Z",
"notes": "Released Lightning and worker images publish linux/amd64 only. The worker application is copied unchanged to the pinned glibc Node image; native optional dependencies are installed from the released worker lockfile. No arm64 worker claim is made.",
"breg": "ghcr.io/registrystack/breg:v0.27.0@sha256:222e4ee3e27f7ee5f237dc06693a07773222b39ef15051abb27bac2cae632586",
Expand Down
60 changes: 43 additions & 17 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
"packages/*"
],
"scripts": {
"check": "npm run check:evidence && npm run check:breg && npm run check:events && npm run check:pilot",
"check": "npm run check:evidence && npm run check:breg && npm run check:casework && npm run check:events && npm run check:pilot",
"check:evidence": "npm run check --workspace @openfn/language-registry-evidence",
"check:breg": "npm run check --workspace @openfn/language-registry-breg",
"check:casework": "npm run check --workspace @openfn/language-registry-casework",
"check:events": "node --test services/event-bridge/test/*.test.js services/destination/test/*.test.js",
"check:pilot": "node --experimental-vm-modules --no-warnings --test pilot/test/*.test.js && python3 -m unittest discover -s pilot/test -p \"*_test.py\"",
"test": "npm run check",
"pack:dry-run": "npm run pack:dry-run --workspace @openfn/language-registry-evidence && npm run pack:dry-run --workspace @openfn/language-registry-breg"
"pack:dry-run": "npm run pack:dry-run --workspace @openfn/language-registry-evidence && npm run pack:dry-run --workspace @openfn/language-registry-breg && npm run pack:dry-run --workspace @openfn/language-registry-casework"
},
"dependencies": {
"@openfn/language-http": "7.3.3"
Expand Down
7 changes: 5 additions & 2 deletions packages/registry-breg/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# @openfn/language-registry-breg

OpenFn operations over the native `breg` namespace in the exact published
`@registrystack/client@0.27.0`. The adaptor uses
`@registrystack/client@0.32.0`. The adaptor uses
`@openfn/language-common@3.3.4` and reexports its composition helpers.
It does not implement Registry authorization or an alternative HTTP client.

Expand All @@ -23,7 +23,10 @@ Put the native SDK configuration in `state.configuration.breg`, or directly in

Alternatively use `authorization: { privateKeyJwt: { tokenEndpoint, clientId,
clientKey, audience } }`, where `clientKey` is a private JWK with `kty`, `kid`,
and `alg`. Token and Registry TLS trust roots and timeouts are independent.
and `alg`. The current client also accepts `authorization: { exchange: ... }`
for an immutable context and reviewed first-party or remote assertion source;
the native provider performs the exchange and bounds its lifetime. Token and
Registry TLS trust roots and timeouts are independent.
The [configuration schema](./configuration-schema.json) lists the fields;
the native SDK validates supported algorithms, bounds and URLs. Keep keys and
tokens in OpenFn credentials, never event data or expressions.
Expand Down
5 changes: 4 additions & 1 deletion packages/registry-breg/configuration-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"authorization": {
"oneOf": [
{ "type": "object", "additionalProperties": false, "required": ["static"], "properties": { "static": { "type": "string", "minLength": 1, "writeOnly": true, "format": "password", "description": "Bearer token without the Bearer prefix." } } },
{ "type": "object", "additionalProperties": false, "required": ["privateKeyJwt"], "properties": { "privateKeyJwt": { "$ref": "#/$defs/privateKeyJwt" } } }
{ "type": "object", "additionalProperties": false, "required": ["privateKeyJwt"], "properties": { "privateKeyJwt": { "$ref": "#/$defs/privateKeyJwt" } } },
{ "type": "object", "additionalProperties": false, "required": ["exchange"], "properties": { "exchange": { "type": "object", "writeOnly": true, "description": "Native Registry client exchange authorization with reviewed context and firstParty or remote source." } } }
]
},
"requestTimeoutMilliseconds": { "type": "integer", "minimum": 1 },
Expand All @@ -29,6 +30,8 @@
"clientId": { "type": "string", "minLength": 1 },
"clientKey": { "type": "object", "writeOnly": true, "required": ["kty", "kid", "alg"], "properties": { "kty": { "type": "string" }, "kid": { "type": "string" }, "alg": { "type": "string" } }, "additionalProperties": true },
"audience": { "type": "string" },
"resource": { "type": "string" },
"scopes": { "type": "array", "items": { "type": "string" } },
"assertionLifetimeSeconds": { "type": "integer", "minimum": 1 },
"refreshMarginSeconds": { "type": "integer", "minimum": 0 },
"requestTimeoutMilliseconds": { "type": "integer", "minimum": 1 },
Expand Down
2 changes: 1 addition & 1 deletion packages/registry-breg/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
},
"dependencies": {
"@openfn/language-common": "3.3.4",
"@registrystack/client": "0.27.0"
"@registrystack/client": "0.32.0"
},
"devDependencies": {
"@openfn/compiler": "1.3.0",
Expand Down
124 changes: 124 additions & 0 deletions packages/registry-casework/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# Registry Casework adaptor

This OpenFn adaptor wraps the maintained Registry Stack Casework Node client.
Requester operations create and read the caller's own hosted items, add and
read notes, cancel an item, and poll the caller's terminal feed.

Task operations read the source inbox and current item, inspect grant templates
and grants, and explicitly approve or revoke a grant. They do not claim,
release, or decide a human work item or issue a task assertion into workflow
state. Casework enforces ownership and authorization from the token identity,
configured profile, and source profile. Workflow data cannot choose a token or
Casework profile.

## Client packaging

This package pins `@registrystack/client` to **0.32.0** and uses its `casework`
namespace. This adaptor has not been published.

## Configuration

Store the credential in OpenFn configuration:

```json
{
"casework": {
"baseUrl": "https://casework.example.test/tenant",
"token": "<requester bearer token>",
"profile": "requester"
}
}
```

For a refreshing service credential, replace `token` with
`authorization: { privateKeyJwt: { tokenEndpoint, clientId, clientKey, resource,
scopes } }`. The adaptor uses the native Registry client token provider to
obtain a bearer token for each operation. Configure exactly one source.
Task-grant approval requires actor authorization and is never inferred from
a service credential.

`requestTimeoutMilliseconds`, `connectTimeoutMilliseconds`,
`maxResponseBytes`, `userAgent`, and `trustedRootCertificates` pass to the
maintained client. The token and profile never enter operation output. OpenFn
keeps configuration available during a composed job and removes it from final
job output.

## Operations

- `createCaseworkItem` requires `kind`, `requesterReference`, `display`, and an
explicit `idempotencyKey`.
- `getCaseworkItem` requires `itemId`.
- `addCaseworkNote` requires `itemId`, `expectedRevision`, `note`, and an
explicit `idempotencyKey`.
- `listCaseworkNotes` requires `itemId`; `cursor` and `limit` are optional.
- `cancelCaseworkItem` requires `itemId`, `expectedRevision`, `reason`, and an
explicit `idempotencyKey`.
- `pollCaseworkResults` accepts an optional `cursor` and `limit`.
- `listCaseworkWorkItems` requires `sourceProfile` and an exact `query` with
`view`; `getCaseworkWorkItem`, `previewCaseworkTaskTemplates`, and
`listCaseworkTaskGrants` require `sourceProfile` and `itemId`.
- `approveCaseworkTaskGrant` additionally requires `expectedRevision`,
`templateId`, `templateVersion`, and a stable `idempotencyKey`.
`revokeCaseworkTaskGrant` requires `grantId`; `caseworkTaskGrantStatus`
accepts `grantId` without a source profile.

Each operation makes one maintained-client call and writes a result under its
default key or the bounded `as` name. A success has
`{ branch: "succeeded", value, traceId? }`. Typed failures contain only bounded
diagnostics. Response detail, SDK messages, credentials, configuration, and
caller inputs are not copied into the result.

Idempotency conflicts are returned as `branch: "conflict"`; the adaptor never
generates a replacement key or retries silently. Keep each key with the logical
create, note, or cancellation until its outcome is resolved.

## Cursor expiry

A typed `cursor.expired` response returns `branch: "cursor_expired"` without a
retry. The result identifies the explicit recovery:

```json
{
"recovery": {
"action": "restart_without_cursor",
"deduplicateBy": "eventId"
}
}
```

Restart the terminal poll without `cursor`, then deduplicate against persisted
`eventId` values. Notes use `noteId` instead. Poll within the configured
terminal retention period; an expired item is no longer available.

## Example

```js
import { execute } from "@openfn/language-common";
import { createCaseworkItem, pollCaseworkResults } from "@openfn/language-registry-casework";

execute(
createCaseworkItem((state) => ({
kind: state.data.caseworkKind,
requesterReference: state.data.requestReference,
display: state.data.caseworkDisplay,
idempotencyKey: state.data.createKey,
as: "createdCasework",
})),
pollCaseworkResults({ limit: 25, as: "terminalResults" }),
);
```

## Verification

The package check uses fake bindings and the real native client over a local
HTTP fixture:

```sh
npm run check --workspace @openfn/language-registry-casework
```

The two-adaptor CLI fixture under `test/multi-adaptor-job.js` runs with repeated
`-a @openfn/language-registry-evidence@local=/absolute/path/to/registry-evidence`
and `-a @openfn/language-registry-casework@local=/absolute/path/to/registry-casework`.
It imports `execute` from Evidence's reexport of OpenFn common, so a third
common adaptor alias is unnecessary.
Loading
Loading