Unreleased documentation. These pages follow the main branch and can change before the next release. For supported guidance, use v0.43.0.
If you are deciding who may read and change what in a Base Registry Engine (BReg) registry, this page shows you how, using the facility template. You will see how a token reaches one access profile, how a profile limits fields and rows, how related Records are disclosed, and which stricter read checks and grants a project can add.
Start from the facility template
Section titled “Start from the facility template”Create the facility project in an empty directory and check it:
bregctl init --template facility-registry facilitybregctl check facilitywarning[breg.access.profile-related-disclosure] <project-dir>/registry.yaml:237:15 /accessProfiles/1/permissions/records/1/readPaths/0 this permission discloses related records using the root profile, not target direct-access profiles; review its fields and the target/through record type accessRequirements next: Review the finding, and change the source if the behavior it describes is not intended.0 errors, 1 warning in 3 filesThe check passes with one warning. Disclose related Records explains what it asks you to review.
How a token reaches a profile
Section titled “How a token reaches a profile”The runtime verifies every bearer token against the one issuer its runtime configuration names,
and admits only the clients listed in allowedClients. A request without a verified token is
refused before any profile is considered. The same configuration names the token claims that
carry the caller’s principal and purpose; What a token must
carry covers that side.
Each request then uses exactly one profile. The caller names it with the accessProfile query
option; without one, the route’s default applies: the only profile granting that route, or the
one marked default: true. Profiles are never merged, and naming a profile grants nothing. The
profile admits the request only when the token carries its principalClaim, every scope in
requiredScopes, and one of its requiredPurposes, and matches actorKind and
requesterClients when the profile sets them. Here is the profile that maintains operators:
accessProfiles: - id: operator-editor principalClaim: registry_principal requiredScopes: ["registry:operator:write"] requiredPurposes: [facility-administration] permissions: records: - recordType: operator operations: [create, get, list, patch] readableFields: [operator-code, status, jurisdiction] writableFields: [operator-code, status, jurisdiction] rowBoundaries: - {field: jurisdiction, claim: jurisdiction, operator: equals}When the token fails any of these checks, or the profile does not grant the operation, BReg answers exactly as it does for a Record that does not exist, and records the refusal in the audit journal. A caller cannot tell a forbidden Record from a missing one.
BReg has no built-in notion of jurisdiction, region, or office. In the template, jurisdiction
is an ordinary token claim, and the row boundary compares it with the Record’s jurisdiction
field. The local test clients show two readers that differ only in that claim:
- id: reader accessProfiles: [facility-reader] scopes: [registry:facility:read] claims: {registry_principal: synthetic-reader, registry_purpose: facility-administration, jurisdiction: zone-a} - id: foreign-reader accessProfiles: [facility-reader] scopes: [registry:facility:read] claims: {registry_principal: synthetic-foreign-reader, registry_purpose: facility-administration, jurisdiction: zone-b}dev-clients.yaml only mints local tokens that ask for these profiles; it adds no authority. In
production the claim comes from your issuer, so the issuer must be one you trust to assert it.
Grant fields and rows
Section titled “Grant fields and rows”A record permission names a Record type and its operations. readableFields limits what a
response discloses and writableFields what a create or patch may set; filterableFields,
sortableFields, and allowCount each enable one query feature. Field lists use the field id,
not its apiName. A write never reveals a field the profile cannot read.
A row boundary compares a Record field with a token claim.
equals compares with one claim value; in with a list claim. Every boundary on a permission must
hold. Reads leave other rows out, and writes check both the stored row and the row as it would be
after the change. A permission that creates must keep its boundary field writable, otherwise
every create is refused, and bregctl check says so. A patch under an in boundary can move a row
between the caller’s allowed values, which bregctl check points out. Write
rowBoundaries: unrestricted only for deliberately registry-wide authority.
An agent is held to a narrower rule. A token from an agent, or one acting for another principal, cannot create, patch, or delete Records directly, with or without a task grant; it reaches them through Actions and Proposals instead.
Disclose related Records
Section titled “Disclose related Records”A Record type can declare a read path to related Records. The facility reaches its operators through the assignment that links them:
readPaths: - {id: operators, through: facility-assignment, to: operator, route: operators}A profile opts in by naming the path and the target fields it may read. This is the grant at line 237 that the check warned about:
- recordType: facility operations: [get, list] readableFields: [facility-code, name, address-line, city, postal-code, status, jurisdiction] readPaths: - {path: operators, readableFields: [operator-code, status, jurisdiction]} rowBoundaries: - {field: jurisdiction, claim: jurisdiction, operator: equals}The warning means this: listing a facility’s operators is authorized by facility-reader’s grant
on the facility, the root of the path. The row boundaries of facility-reader’s own grant on
operator do not apply to the operators that come back. A reader who may see a facility sees
every operator assigned to it, with the path’s readable fields, whatever that operator’s
jurisdiction.
In the template that is the intended behavior. The register-facility Action only assigns an
operator whose jurisdiction equals the facility’s, and the path discloses the same three fields
the reader may already read on operators directly. Ask the same two questions of your own paths:
can a related Record sit outside the reader’s rows, and do the path’s fields disclose more than a
direct grant would? If the answer to either is yes and that is not intended, narrow the path’s
readableFields, or drop the path and grant the target Record type directly. When a target, or
the Record type a path passes through, must never be disclosed without its own row check, give
that Record type accessRequirements with row boundaries: the compiler then refuses any read path
grant that reaches it. The warning appears for every read path grant, so a project that keeps one
cannot pass bregctl check --deny-warnings.
Require a current membership
Section titled “Require a current membership”A row boundary works when the Record carries the claim value it is compared with. Sometimes the right to read follows an affiliation that changes over time: a facility belongs to an organization, and whoever is currently a member of that organization may read it. Copying members into tokens would leave access in place until each token expired.
membershipBoundaries on a record permission puts that rule into the registry. Each entry names
the Record’s reference field, a membershipRecordType, and that type’s membershipKeyField,
principalField, and activeField. A row is readable only while a membership Record exists with
the same key, the caller’s verified principal, and active: true. Memberships are ordinary
Records that a separate steward profile creates and deactivates. A request that starts after a
deactivation commits no longer sees the Records; a read already running may finish. Historical
reads compare against current memberships too, so a past membership cannot restore access.
Membership boundaries apply to reads only: get, lookup, list, snapshot, and revisions. Give writes
to a separate profile. Declare rowBoundaries beside them, even if only unrestricted; every
membership and row boundary must hold.
Require the subject’s consent
Section titled “Require the subject’s consent”A membership boundary asks whom the caller works for. A consent check asks what the Record’s subject decided: that this recipient may read their Record, for this purpose, under this profile.
requireConsent on a record permission lists checks, each naming a consent record type and
on, the field that identifies the subject (id for the row itself). The project declares
recipients, the organizations that read and the clients each one reads through. BReg derives the
caller’s recipient from the verified client, never from a token claim, so a gated profile must
list its requesterClients. A row is disclosed only while a current consent covers it; a
withdrawal takes effect for every request that starts after it commits, with no token change or
restart. Gated permissions
are read-only, cannot export, and need their own clients, because any profile without
requireConsent reads without the check.
The profile id is the scope the subject agreed to. Removing requireConsent or widening a gated
profile in place would read under a scope the subject never saw, so publish a new profile and list
the old id under retiredConsentScopes.
What a reader meets when a check fails
Section titled “What a reader meets when a check fails”Membership and consent refusals look like every other access refusal. A get on a Record the
caller may not see answers 404, exactly as for a Record that does not exist; a list or count
leaves the Record out. The caller learns nothing about why, or whether the Record exists. The
problem documents in the API reference list the
codes, and the configuration reference carries every key
named on this page.
Grant statistical datasets
Section titled “Grant statistical datasets”A project that publishes counts declares statistical datasets, and a profile reaches them through
permissions.datasets. Each entry names a dataset and its operations: read-live for current
counts, publish to publish or withdraw a release, read-releases to read published releases.
A standing agent profile, one with actorKind: agent and no taskGrant, cannot hold publish:
a release takes effect at once, with no draft for a person to confirm, so the compiler refuses it.
Publish a statistical series covers the datasets themselves.
Read the compiled access
Section titled “Read the compiled access”bregctl explain access facility profile facility-reader principal claim registry_principal operations ["get","list"] required scopes (all) ["registry:facility:read"] allowed purposes (any) ["facility-administration"]The report lists every profile under each Record type with its fields, row restrictions, and
related Records. Add --scenario with a file of synthetic claims to preview a decision without a
token or a database. To prove the refusals against a running registry, write
journeys that read as both readers.