Unreleased documentation. These pages follow the main branch and can change before the next release. For supported guidance, use v0.44.0.
Base Registry Engine client capabilities
The Base Registry Engine (BReg) client is one Rust implementation in three languages: the
registry-breg-client crate (also re-exported as registry_stack_client::breg), the breg
namespace of @registrystack/client in Node, and the breg module of registry-stack-client in
Python. Rust does every validation and protocol check, so the three behave the same.
Construction, the shared outcome shape, and the same-key retry are in the client API reference.
Operations
Section titled “Operations”Python uses the Rust names. The table is generated from the client capability inventory, which a gate checks against the server contracts and each language’s exports.
| Capability | Rust and Python | Node |
|---|---|---|
| metadata | registry_contract | registryContract |
| direct reads | get_recordlist_recordscontinue_listlookup_record | getRecordlistRecordscontinueListlookupRecord |
| native gis | get_geojson_recordlist_geojson_recordscontinue_geojson_list | getGeoJsonRecordlistGeoJsonRecordscontinueGeoJsonList |
| current | list_current_recordscontinue_current_list | listCurrentRecordscontinueCurrentList |
| as of | list_records_as_ofcontinue_as_of_list | listRecordsAsOfcontinueAsOfList |
| snapshot | list_snapshot_recordscontinue_snapshot_list | listSnapshotRecordscontinueSnapshotList |
| revisions | record_revisionsget_record_revision | recordRevisionsgetRecordRevision |
| relationships | list_relationship_recordscontinue_relationship_list | listRelationshipRecordscontinueRelationshipList |
| direct writes | create_recordpatch_record | createRecordpatchRecord |
| batch | batch_records | batchRecords |
| ingestion runs | create_ingestion_runlist_ingestion_runsread_ingestion_runsubmit_ingestion_chunkcancel_ingestion_runingestion_chunk_receipt | createIngestionRunlistIngestionRunsreadIngestionRunsubmitIngestionChunkcancelIngestionRuningestionChunkReceipt |
| attachments | upload_proposal_attachmentdownload_proposal_attachmentdelete_proposal_attachment | uploadProposalAttachmentdownloadProposalAttachmentdeleteProposalAttachment |
| create recovery | prepare_createrecover_create | prepareCreaterecoverCreate |
| immediate actions | action_target_conditionsaction_availabilityinvoke_action | actionTargetConditionsactionAvailabilityinvokeAction |
| action recovery | prepare_actionrecover_action | prepareActionrecoverAction |
| statistical datasets | statistics_livestatistics_releasesstatistics_latest_releasestatistics_release_versionstatistics_release_seriesstatistics_publishstatistics_withdraw | statisticsLivestatisticsReleasesstatisticsLatestReleasestatisticsReleaseVersionstatisticsReleaseSeriesstatisticsPublishstatisticsWithdraw |
| tombstone | tombstone_record | tombstoneRecord |
| proposals | start_proposalget_proposalread_proposalread_proposal_submissionlist_proposals | startProposalgetProposalreadProposalreadProposalSubmissionlistProposals |
| proposal recovery | prepare_proposalrecover_proposalexecute_proposal | prepareProposalrecoverProposalexecuteProposal |
| proposal review | read_proposal_review_context_batchread_proposal_review_contextread_proposal_review_statusread_proposal_review_by_iddownload_proposal_review_document | readProposalReviewContextBatchreadProposalReviewContextreadProposalReviewStatusreadProposalReviewByIddownloadProposalReviewDocument |
| event record view | event_record_viewevent_recordevent_record_creation_revision | eventRecordVieweventRecordDescriptoreventRecordCreationRevision |
A few methods sit outside the inventory:
| Method (Node) | Purpose |
|---|---|
health, ready | Probes, sent without a bearer token |
openapi, registryMetadata, and the schema read for one Record type | Caller-filtered raw documents for the selected profile |
executeRecoveredCreate, executeRecoveredAction, executeRecoveredProposalStart | Send a recovered attempt; see Recovery after a restart |
verifyHookDelivery | Authenticate one hook request; see Verify a hook delivery |
Python spells each one in snake case. Read options are a request type in Rust, such as
BRegListRequest, keyword arguments in Python, and one object in Node. Mutations take positional
arguments in all three.
Proposal objects in Node
Section titled “Proposal objects in Node”Three Node methods take plain JSON objects: startProposal(binding, inputs, key),
listProposals(collection, query), and prepareProposal(proposal, operation, body, key).
Python takes dicts for the same arguments, and Rust takes typed values.
const proposal = await client.startProposal( binding, { reason: 'relocation' }, 'start-2026-10-17-0001',);Many Node methods have a *Json twin, such as getRecordJson or invokeActionJson, that returns
the validated response as exact text in valueJson.
Proposal list query keys
Section titled “Proposal list query keys”Each key a Proposal list accepts, and the Rust query member that carries it. Node and Python take the keys as one query object, named below the table.
| Query key | Rust |
|---|---|
action | BRegProposalQuery.action |
owner | BRegProposalQuery.owner_is_caller |
target | BRegProposalQuery.target |
state | BRegProposalQuery.state |
cursor | BRegProposalQuery.cursor |
limit | BRegProposalQuery.limit |
turn | BRegProposalQuery.turn |
pendingManualApplication | BRegProposalQuery.pending_manual_application |
Node passes these keys in BaseRegistryClient.listProposals.query; Python passes them in BaseRegistryClient.list_proposals.query.
Metadata and bindings
Section titled “Metadata and bindings”registryContract(accessProfile) returns BRegMetadata, the registry’s metadata filtered for
the caller and that profile. Every write, Action, and Proposal call needs an opaque binding
selected from it:
| To | Node and Python (snake case) | Rust |
|---|---|---|
| Create a Record | selectCreate(operationIdentifier, expectedProfile) | select_direct_write, which returns BRegDirectWrite::Create |
| Patch a Record | selectPatch(operationIdentifier, expectedProfile) | select_direct_write, which returns BRegDirectWrite::Patch |
| Invoke an Action or start a Proposal | selectAction(actionIdentifier, expectedProfile) | select_action |
| Tombstone a Record | selectTombstone(recordTypeIdentifier, expectedProfile) | select_tombstone |
| Send a batch | selectBatch(recordTypeIdentifier, expectedProfile) | select_batch |
| List Proposals | selectProposals(profile) | select_proposals |
| Get one Proposal | selectProposalGet(profile) | select_proposal_get |
| Read Proposal reviews | selectProposalReviewContext(profile, action), selectProposalReviewStatus(profile, action) | select_proposal_review_context, select_proposal_review_status |
A binding ties the call to the client’s origin, the package revision, the profile, the route, and
the request contract. It exposes nothing and grants nothing the server would not already allow.
When the metadata does not offer the operation, selection fails with kind metadata-selection.
Upgrade the client and server together: an older client’s strict decoder can reject metadata a newer server adds, even for operations the application never calls.
Spatial queries
Section titled “Spatial queries”A direct list accepts bbox beside the filters the profile permits. A box is four decimal strings
in west, south, east, north order, in CRS84 longitude and latitude. The client refuses reversed
bounds, out-of-range values, boxes that cross the antimeridian, and boxes over the 256-byte wire
limit. Zero-width boxes are valid. The server enforces the profile’s geometry grant and maximum
span.
page = client.list_records( "establishments", access_profile="map-reader", bbox=("100", "13", "101", "14"), top=20,)const page = await client.listRecords('establishments', { accessProfile: 'map-reader', bbox: ['100', '13', '101', '14'], top: 20,});use registry_breg_client::{BRegBoundingBox, BRegListRequest, BRegRecordOptions};
let request = BRegListRequest::default() .options(BRegRecordOptions::default().access_profile("map-reader")?) .bbox(BRegBoundingBox::new("100", "13", "101", "14")?) .top(20)?;let page = client.list_records("establishments", &request).await?;registry_stack_client::breg does not re-export BRegBoundingBox; depend on registry-breg-client
for it.
Use the GeoJSON methods when you need a Feature or FeatureCollection. Leaving the geometry field out
of select yields null geometry. The /v1/gis adapter routes serve QGIS directly and have no client
methods.
Temporal, revision, and relationship reads
Section titled “Temporal, revision, and relationship reads”| Read | Node | Notes |
|---|---|---|
| Current state | listCurrentRecords | A separate route from the direct list |
| State on a date or at an instant | listRecordsAsOf | Requires asOf (as_of in Python): a calendar date for a date interval, a UTC RFC 3339 instant for a timestamp interval |
| A committed snapshot | listSnapshotRecords | Takes snapshot or validAt; keep the returned snapshot reference to repeat the read |
| Revision list | recordRevisions | The newest 100 retained revisions, with no continuation |
| One revision | getRecordRevision | Returns the revision as bytes (Buffer or bytes) |
| Related Records | listRelationshipRecords(route, recordIdentifier, pathRoute, options) | Takes the path route from the metadata |
A relationship path has its own authority and projection, and refuses bbox and temporal options.
Pass a continuation unchanged to the matching continue* method. It keeps the route, profile, and
snapshot identity across later writes.
Current authority and retained history still govern every page.
Proposals
Section titled “Proposals”Start a Proposal with startProposal(binding, inputs, key), using a binding selected for an Action
whose mode is Proposal Start. Then:
getProposal(binding, identifier)finds one by identifier;listProposals(collection, query)pages through them with the query keys above.readProposal(proposal)refreshes one you already hold.prepareProposal(proposal, operation, body, key)prepares a transition (edit,submit,revise,rebase,retry,cancel, orapply), andexecuteProposal(prepared)sends it.readProposalSubmission(proposal, version)returns one frozen submission exactly as it was submitted: its Action, version, effect digest, review, application status, and decision. Once retention has removed it, the call fails with410 proposal.submission_expired.readProposalReviewContext(proposal),readProposalReviewStatus(proposal),readProposalReviewById(binding, identifier), anddownloadProposalReviewDocument(binding, context, field)read the review. The review context carries no attachment bytes.uploadProposalAttachment,downloadProposalAttachment, anddeleteProposalAttachmentwork on one declared slot. Submitting freezes the attachment digests, so changed bytes cannot inherit an earlier approval.
Each transition has its own grant, and each link carries the Proposal’s ifMatch condition, never
a Record ETag. Read the Proposal again after every transition. A later read does not settle an
uncertain Start or Submit; recover the original attempt instead.
Immediate Actions, batch, and tombstone
Section titled “Immediate Actions, batch, and tombstone”An immediate Action can run without the general right to read the Record. Select its binding, fetch
the target conditions with actionTargetConditions(binding, inputs), then call invokeAction
with the same binding, inputs, conditions, and your idempotency key. The client never refreshes the
conditions for you. A declared refusal arrives as kind problem with code action.refused and the
Action’s refusalCode.
batchRecords(binding, request, key) sends create and patch items for one Record type as one
atomic request. A stale item or a failed constraint rolls back every item, and interval constraints
are checked on the final state. An optional change context records why: { kind: 'change' } or
{ kind: 'correction', reasonCode }, each with optional reasonText and sourceReferences
(snake case in Python). Rust builds the request with
BRegBatchBuilder and BRegChangeContext. The receipt carries a snapshot and per-item results.
tombstoneRecord(binding, recordIdentifier, etag, key) removes a Record from live lists and
returns the new revision. Permitted retained history stays readable.
Statistical datasets
Section titled “Statistical datasets”The statistics* methods read live figures and releases, and publish or withdraw a release. A withdrawn version answers 410 with code
statistical_dataset.version_withdrawn and the withdrawal reason in reasonCode. A refused release
carries refusalCode. Datasets are declared in the
statistics configuration.
Ingestion runs
Section titled “Ingestion runs”An ingestion run loads one large input in chunks, each committed on its own, so a load survives a
restart. createIngestionRun(route, request) announces the whole input: operation,
profile, package revision, schema fingerprint, input digest and length, item and chunk counts, and
chunk algorithm version.
submitIngestionChunk(route, runId, chunk, profileId) sends one chunk. Each chunk carries a prefix
digest, the SHA-256 of the raw input through the end of that chunk. Build it with
BRegIngestionPrefixDigest and encodeIngestionChunk (encode_ingestion_chunk in Python). A
resubmitted chunk replays its saved receipt instead of running twice.
An erased chunk receipt answers ingestion.receipt_erased. Cancelling a run stops further chunks;
committed chunks stay.
Recovery after a restart
Section titled “Recovery after a restart”If a process dies between sending a write and reading the answer, replay the same attempt under the same key after a restart. Prepare it before the first send and save its bytes. Node names:
| Attempt | Before the first send | After a restart | Send |
|---|---|---|---|
| Create a Record | prepareCreate(binding, data, key) | BRegPreparedCreate.fromBytes, then recoverCreate(binding, prepared) | executeRecoveredCreate |
| Invoke an Action | prepareAction(binding, inputs, key, conditions) | BRegPreparedAction.fromBytes, then recoverAction(binding, prepared, inputs, key) | executeRecoveredAction |
| Start a Proposal | prepareAction(binding, inputs, key) | as for an Action | executeRecoveredProposalStart |
| Proposal transition | prepareProposal(proposal, operation, body, key) | BRegPreparedProposal.fromBytes, then recoverProposal(current, prepared) | executeProposal |
Save the bytes from toBytes(). Python uses snake case, with from_bytes and to_bytes; Rust
uses from_slice and as_bytes. In Rust, recover_create returns the request,
key, and format to send with create_record, and recover_action returns a request to send with
invoke_action or start_proposal.
Action recovery needs the original inputs and key as well as the saved bytes. Recovery checks the
saved attempt against a binding freshly selected under the original principal, and reuses the saved
target conditions exactly. A restored Proposal transition cannot be executed until
recoverProposal has checked it against the current Proposal:
const restored = breg.BRegPreparedProposal.fromBytes(saved);const current = await client.getProposal(getBinding, proposalIdentifier);const attempt = client.recoverProposal(current, restored);const updated = await client.executeProposal(attempt);The saved bytes hold the original request body and idempotency key. They hold no bearer token, but treat them as sensitive. Your application owns where they are stored, who may read them, and how long they live. Importing them authenticates no one and grants no authority.
Errors
Section titled “Errors”Every failure is a BaseRegistryClientError: an Error in Node, an Exception in Python. An
attribute the failure does not carry is absent in Node and None in Python.
| Node | Python | Carries |
|---|---|---|
kind | kind | One of the kinds below; always set |
outcomeUnknown | outcome_unknown | true when a write may have committed |
status | status | HTTP status |
code | code | The BReg Problem code, or a bounded code for the kinds that set one |
traceId | trace_id | Validated trace identifier |
fieldPath | field_path | The field a Problem names |
constraint | constraint | A row-local constraint conflict |
refusalCode | refusal_code | The declared reason for action.refused or statistical_dataset.release_refused |
reasonCode | reason_code | The reason for statistical_dataset.version_withdrawn |
transportKind | transport_kind | The transport failure category |
tokenKind | token_kind | The token failure category |
| Kind | Meaning | Also set |
|---|---|---|
configuration | Construction inputs were refused | none |
invalid-request | An input was refused before any exchange | none |
token | The token provider could not supply a bearer | tokenKind, and transportKind, code, or status depending on the failure |
transport | The exchange failed below HTTP | transportKind |
not-found | The server answered resource.not_found | status, code, traceId |
problem | The server answered with any other Problem | status, traceId; a known code adds code and any fieldPath, constraint, refusalCode, or reasonCode |
protocol | The response broke the contract | status, traceId when valid, and code: header-bounds, trace-context, media-type, body, problem, entity-tag, profile-link, location, cache-policy, representation-digest, status, or protocol |
metadata-selection | No binding matched | code: not-found, unbound-source, profile-mismatch, unsupported-operation, required-capability, or contract-mismatch |
hook-verification | A hook delivery failed authentication | code: missing-header, invalid-delivery-time, duplicate-header, body-too-large, invalid-key, malformed-signature, signature-mismatch, or unsupported-version |
client | Any other client-side failure | none |
A Problem code this client version does not know arrives as kind problem with only status and
traceId; decide on the status. An unknown 4xx is a refusal; an unknown 5xx may have committed.
The API reference lists every Problem code.
- Query a registry from Python and Node installs a client and walks a read and write journey.
- Client API reference covers construction, outcomes, and the same-key retry.
- Control access per profile sets the grants every binding depends on.
- Retention and erasure sets how long history, submissions, and receipts last.
Cookies on Registry Stack
We use optional cookies to understand how you use the main Registry Stack website and this documentation. If you contact us, we may link this activity to your enquiry. You can change your choice at any time. Read the privacy policy.
Your browser asks sites not to track you, so optional tracking stays off.
We could not use or save that choice. Optional tracking remains off.