Released docs. You are viewing the documentation published with v0.34.0. Development docs are available at Latest.
If you finished Create and query your first registry, you are a data publisher with the
installed binaries and a tutorial-work directory holding the project you generated.
In this tutorial you change a field that a module in that project contributes, re-pin the module so the
project accepts the change, and generate the JSON Schema and OpenAPI that applications read.
Every command reads and writes files in tutorial-work, so the registry from the first tutorial may be stopped.
The one place a running registry matters is a comparison at the end of the explain step,
and that comparison holds whether the registry runs or not.
Before you start
Section titled “Before you start”Open a terminal in the directory that holds tutorial-work, and confirm the binaries are still on
your PATH:
bregctl --versionThe version printed is the one the first tutorial installed.
Nothing in this tutorial touches the registry’s keys or data under tutorial-work/project/.breg/,
and nothing here needs Docker.
Open the project
Section titled “Open the project”Check the project you generated:
bregctl check tutorial-work/projectAuthoring check passed. revision sha256:<digest>
finding access.profile.unrestricted_collection entities[id=record].accessProfiles[id=operator].rowBoundaries this profile can list all rows, subject only to query bounds; caller filters are not authorization. Add a claim-bound row restriction or review this registry-wide access finding access.profile.unrestricted_rows entities[id=record].accessProfiles[id=evidence-source].rowBoundaries this profile has no claim-bound row restriction for its granted operations; requestVisibility owner limits request reads only, and other lifecycle rules still apply. Review this registry-wide access
0 errors, 2 findings.This is the project the first tutorial started as a registry, so the entity, fields, and profiles are
the ones you used over HTTP, in the files init wrote.
The revision line is the digest of the compiled project; it changes with every edit you make.
A finding is advice the compiler attaches to a result that succeeded: an error stops a command, a finding does not.
The first says the operator profile can list every row, which is intended for a registry-wide operations team,
and the second says the evidence-source profile can look up any record by its code.
Every command in this tutorial that succeeds repeats both findings, so treat them as expected and read past them to the result.
A check that is refused prints its errors instead, and you will meet one when you change the module.
Read the module
Section titled “Read the module”A module is a separate file that contributes to the model, so a reusable part of a registry can be reviewed
and versioned apart from the project that adopts it.
Open tutorial-work/project/modules/record-notes/module.yaml and find its version and the one field it
declares on the entity the project owns:
version: 0.1.0 - {id: internal-note, type: string, maxLength: 500, classification: internal}A field without required: true is optional, so existing records stay valid and a create may omit it.
Its classification matches the entity’s default, internal.
Now open tutorial-work/project/registry.yaml and find the modules entry at the end of the file:
modules: - id: "record-notes" version: "0.1.0" digest: "sha256:<digest>"The digest is a content digest of the module file. The project pins the exact module content it was reviewed with, and the compiler refuses a module whose content or version no longer matches.
Grant access to the field
Section titled “Grant access to the field”Adding a field to the model grants nobody access to it, and no profile lists internal-note yet.
In tutorial-work/project/registry.yaml, find the operator grant for record and add the field to its
readable and writable lists, keeping the indentation:
readableFields: [code, label, group, status, internal-note]writableFields: [code, label, group, status, internal-note]filterableFields: [code, status]Check the project:
bregctl check tutorial-work/projectThe command prints Authoring check passed., a new revision, and the findings.
filterableFields is unchanged on purpose: a note is something an operator reads and writes,
not something a list is filtered by.
Change the module
Section titled “Change the module”Now change the module itself.
In module.yaml, raise maxLength to 1000 and version to 0.2.0, then run the check again:
bregctl check tutorial-work/projectbregctl check refused.
error module.lock.digest_mismatch project.modules[].digest an authored module does not match its locked digest error module.lock.version_mismatch project.modules[].version an authored module does not match its locked version
2 errors, 0 findings.A stale digest is an error, not a finding.
The project still pins version 0.1.0 and the old content, and the compiler refuses to build a model from a
module that differs from what was reviewed.
Re-pin the module
Section titled “Re-pin the module”Rewrite the lock entry from the module’s current content, then check again:
bregctl project lock tutorial-work/projectbregctl check tutorial-work/projectproject lock prints Locked the project modules. 1 artifact written., the rewritten registry.yaml, the findings, and a report of what it rewrote:
{ "changed": true, "modules": [ { "digest": "sha256:<digest>", "id": "record-notes", "status": "updated", "version": "0.2.0" } ]}check then prints Authoring check passed. with the findings and nothing else.
Open the modules entry in registry.yaml again: both the version and the digest changed.
Rerun project lock after every module edit, and review a changed digest together with the module source it now pins.
Inspect the query permissions
Section titled “Inspect the query permissions”explain reports what the compiled project permits, without a database:
bregctl explain queries tutorial-work/projectAfter Explained the compiled inventory., the report lists one operation per profile and entity.
In records.record.operator.list, the operator’s list operation over record, find internalNote among the apiFields:
{ "apiName": "internalNote", "field": "internal-note", "fieldType": { "maxLength": 1000, "minLength": 0, "type": "string" }, "sourceKind": "stored"}The same operation’s filterable entries name only code and status, each with examples such as $filter=code eq 'example'.
That is the refusal you met in the first tutorial, explained from the project instead of by a 400.
This report describes your edited project; if the registry from the first tutorial is still running,
it serves the package it started with and knows nothing of internalNote.
Generate the API schema
Section titled “Generate the API schema”Generate JSON Schema and OpenAPI from the edited project:
bregctl generate schemas tutorial-work/project --output tutorial-work/schemasbregctl generate openapi tutorial-work/project --output tutorial-work/apiEach command names what it wrote: two schema files, then one OpenAPI document. Read the record schema:
python3 -m json.tool tutorial-work/schemas/generated/schemas/record.schema.jsonFind internalNote under properties.
An optional field accepts null, so the generator wraps its type in anyOf:
{ "internalNote": { "anyOf": [ { "maxLength": 1000, "minLength": 0, "type": "string" }, { "type": "null" } ] }}The authored internal-note becomes internalNote in JSON, and the length you raised in the module reached the schema.
The required list still contains only code and label.
Open tutorial-work/api/generated/openapi.json to see the same field in the create and PATCH request
shapes under /v1/records/records.
Generated files are outputs: edit YAML and regenerate into a fresh directory to change them,
because generate refuses an output directory that already exists.
What you built
Section titled “What you built”You have a project of your own whose module contributes a longer internal-note, pinned by content digest,
granted to the operator, and reflected in the JSON Schema and OpenAPI an application would read.
You compiled an extension, not installed one: no database changed, and the registry from the first
tutorial, if it is still running, serves the package it started with.
Testing a project against a database and packaging it come next.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Next move |
|---|---|
generate reports output.destination.invalid | The output directory must not exist yet. Choose a new path, or remove the one left by an earlier run. |
check reports module.lock.digest_mismatch or module.lock.version_mismatch | You edited a module after its last lock. Run bregctl project lock tutorial-work/project, then check again. |
check cannot parse registry.yaml after your edit | Compare the indentation of the lines you changed with their neighbours; the grant lists sit eight spaces in. |
bregctl is not found | Put the installers’ directory, ~/.local/bin unless you changed it, back on PATH in this terminal. |
The running registry does not show internalNote | Expected: it serves the package bregctl dev built when it first started. Activating a changed package is covered in Deploy a registry. |
bregctl dev refuses to start the edited project | Expected while the registry holds records: dev keeps the package it started with. Run bregctl dev stop tutorial-work/project --remove to discard the records, then bregctl dev tutorial-work/project builds the edited project into a fresh registry. To keep the records, copy tutorial-work/project without its .breg/ directory to a new path and start that copy instead. |
- Review changes before updating a registry to test a configurable approval workflow on a copied project.
- Author a registry project for the full model: entities, relationships, time, and events.
- Build a production candidate to test, package, sign, and verify a project like this one.