Unreleased documentation. These pages follow the main branch and can change before the next release. For supported guidance, use v0.26.1.
This page is for someone sizing up Base Registry Engine before committing a database, a token issuer, a signing process, and operational capacity to it. It assumes the fit question in Which product fits your problem is settled and asks the next one: what does running it cost. The Base Registry Engine overview defines the terms this page uses.
Runtime footprint
Section titled “Runtime footprint”Base Registry Engine is one crate, registry-breg, and one binary, breg. One process verifies
one package and serves its compiled REST API against one PostgreSQL database, with no control
plane or worker process beside it. The crate is unpublished, so a source build means building the
Registry Stack workspace.
At startup the process reads one runtime configuration file, kind: BRegRuntimeConfig under
registry.registrystack.org/breg-runtime/v1alpha1. It binds a listener, a deployment identity, a
package directory with its trust anchor (the public keys a package signature may come from, and
how many must sign), two database connection references, an OpenID Connect verifier, an audit key
reference, a cursor secret reference, and one binding per declared webhook
destination. The file holds references, never secret values: a database
block carrying a url, password, or plaintext member is refused. Secrets resolve through a
file provider (owner-only files under one root) or, when declared, an environment provider.
The process keeps no local state. The audit journal, revision history, outbox (the table holding
one row per configured event until it is delivered), and change-request rows live in PostgreSQL,
and the release image carries no writable directory. Metrics are opt-in:
a second listener on a loopback or private address serves GET /metrics in Prometheus text
format; a public, unspecified, or shared address is refused.
bregctl is the second binary. Its authoring commands (init, check, explain, generate,
project) open no database, so the modeling loop runs on a laptop. test needs an empty
schema-test database and real tokens; apply, doctor, migration, history, audit,
webhook list, webhook replay, and request-retention read the runtime configuration and open
the database; verify reads the same file but opens no runtime dependency; data import and
data export talk to a running server over HTTP.
Dependencies
Section titled “Dependencies”PostgreSQL is the only database; SQLite is not a compatibility target. The runtime refuses a
server older than 15, and a project with spatial
fields needs 16 or newer for its bounding-box role plus PostGIS 3.5 or later. PostGIS is required
only for spatial predicates: ordinary Point storage and GeoJSON reads work without it. Geometry
support is CRS84 points with bounding-box queries; polygons and other geometry types are not a
field type the schema offers, so store them elsewhere and reference them by identifier. The
quickstart pins PostgreSQL 17.11. The administrator installs btree_gist, and PostGIS where
needed, in a schema neither registry role owns; neither role creates a schema, an extension, or a
role.
The connection path is fixed: tokio-postgres 0.7.18, deadpool-postgres 0.14.2, and
tokio-postgres-rustls 0.14.0, with TLS verified against the host’s native roots or an
operator-supplied certificate authority; plaintext is confined to the test harness. Two login
roles are required, migration (owns the schema, applies packages) and runtime (serves), and the
configuration refuses one role or one connection reference for both. Startup verifies ownership,
privilege, and extension boundaries for both roles and refuses to serve when they are wrong;
generated row-level security policies run under the runtime role. Those policies defend against
application mistakes, not against a party holding the runtime credential, so credential custody
stays with you.
A token issuer. The server verifies bearer access tokens and issues none. The verifier is OpenID Connect: one issuer, one audience, one algorithm, a maximum token lifetime, and the claim names that carry the principal and purpose. Keys come from issuer discovery or from a static JWKS document held behind a secret reference; static keys load once at startup and rotate through a restart. Deployments without an identity provider can run Registry Mint, which the first tutorial uses; a registry depends on Mint only when Mint is its issuer.
Signing keys for production packages. bregctl package compiles a production package and
stops at awaiting_signatures; it never accepts a private key. An external signer produces a
detached Ed25519 signature over signing-input.json, and a second package run with the signature
document publishes the package. The trust anchor the runtime file names lists the accepted public
keys and a threshold, bound to the deployment identity. A threshold of
zero is valid only for the local environment; production needs at least one. Who holds those
keys, and how they sign, is a decision the product leaves to you.
Nothing else at runtime. Webhook delivery runs inside the breg process from an outbox table
written in the same transaction as the change, so there is no message broker. No cache, object
store, or second service takes part in a request. Registry Relay and Evidence Gateway are separate
products, not dependencies.
Deployment options today
Section titled “Deployment options today”Base Registry Engine ships first in Registry Stack v0.26.0, published 2026-09-04, and the Python and Node client packages follow in v0.26.1, published the same day, so an application that will call the registry through a client library needs a v0.26.1 or later deployment. A release carries three runtime artifacts for the product:
bregandbregctlbinaries for each platform in platform support, with aSHA256SUMSfile.- An installer,
breg-install.sh, that verifies both binaries against the checksums before anything reaches the install directory and installs both or neither.~/.local/binis the default directory,BREG_INSTALL_DIRchanges it, andBREG_ASSET_DIRreads already-verified assets. It does not verify release authenticity; OpenSSF and release trust covers the chain that does. - A container image,
ghcr.io/registrystack/breg, built on distrolessnonroot(uid 65532). Its entrypoint runsbreg --config /etc/breg/runtime.yaml, it exposes port 8080, and it contains the runtime binary and the license only. Configuration, package, trust anchor, and secrets mount read-only under/etc/breg. There is no shell and no healthcheck subcommand; probeGET /healthorGET /healthzfor liveness andGET /readyfor readiness.
The release ships no Helm chart, Compose file, or hosted offering; scheduling, TLS termination,
and the reverse proxy in front of the listener are yours. The listener derives no authority from
the request Host or forwarded headers; generated links use the configured publicOrigin. The
quickstart’s Docker use is a loopback
development path, not a deployment pattern. Keep bregctl and breg on one release: the
installer installs the pair from one tag, and both binaries must compute the schema fingerprint a
test receipt binds the same way.
The Rust client crate, registry-breg-client, is unpublished (publish = false), so a Rust
consumer pins the workspace by git tag.
Query a registry from Python and Node covers the client
packages an application installs.
Operational burden
Section titled “Operational burden”Keys and secrets
Section titled “Keys and secrets”You hold six kinds of secret material: two database connection strings (runtime and migration),
the audit hash key, the cursor secret, one HMAC-SHA-256 key per webhook destination, and the
OpenID Connect key material when you pin a static JWKS. Package signing keys stay with the signer
and never reach bregctl or the server. The audit key and the cursor secret must outlive the
journal and any cursor still in a client’s hands. A package may not embed a trust anchor, a
runtime secret, a migration credential, or a signing key; the layout contract forbids those roles.
Package signing policy
Section titled “Package signing policy”Every production change, including a one-line access widening, goes through check --production,
test against a schema-test database with real tokens, package, external signing, and apply.
The test receipt binds the candidate source, database identity, schema fingerprint, and signature
policy, and package refuses a receipt taken for anything else. Budget for a signer who is not
the author: the split between authoring, signing, and migration authority is deliberate.
Migration authority
Section titled “Migration authority”Activation runs under the migration credential, which the serving host does not need between
activations. A successor package starts from diff against the active runtime configuration. A
change that diff classifies as additive needs no further evidence; a change it marks for review
needs a reviewed migration directory with a descriptor, a rehearsal receipt, SQL steps, and, for a
destructive change, a backup artifact, all validated by test and package and never generated
by them. A failed activation leaves the database in maintenance with readiness unavailable and
the target pinned; migration reconcile reports the one safe transition, and the remaining case
is restoring your pre-activation backup. No command clears a failed maintenance state on its own.
Audit journal
Section titled “Audit journal”Every admitted request and every refusal that names a principal appends one hash-chained record
to the journal in PostgreSQL. Refusals of unauthenticated requests are counted on the metrics
listener instead, so an anonymous caller cannot grow the chain. bregctl audit verify walks the
chain, audit export writes it as JSON Lines, and audit prune removes the prefix older than a
boundary after a dry run. A prune leaves no copy behind, so export first; after a prune,
verification proves the retained links, not completeness.
Retention and erasure
Section titled “Retention and erasure”Retention obligations are met by bounded operator commands, not API permissions. history erase
removes the retained revisions of one record up to a
revision number, at most 10,000 revisions per transaction, with no dry run and no undo; it also
scrubs affected correction context, retained webhook payloads, and cached idempotency responses.
Snapshot references at or after the erased commit become unavailable until a
rebaseline re-establishes coverage, and history rebaseline
refuses a registry with more than 1,000 live rows, so a larger registry cannot restore snapshot
coverage after an erasure. Change-request detail has its own retention
mode per request entity, and request-retention erase works only where the mode is
operator_erase. Retained webhook payloads expire after payloadRetentionDays, 7 by default and
at most 30. Retain, erase, and audit walks through each retention
command end to end.
Backups
Section titled “Backups”The product has no backup command and restores nothing itself. Your PostgreSQL backup, taken
before every apply, is the recovery path for a failed activation that reconcile reports as
unresolvable, and a destructive migration must name its backup artifact on the apply command
line. Erased history is gone from the database; account for backups, exports, and copies
delivered to consumers under your own retention policy.
Performance posture
Section titled “Performance posture”No throughput or latency figure is committed for Base Registry Engine, and this page states
none. What exists is a k6 load-test harness that runs against a disposable PostgreSQL 17,
Registry Mint, the business-establishments acceptance fixture, and the private metrics
listener. The steady profile asks whether 50 mixed operations
per second hold for 10 minutes with p99 under 250 ms; sweep, burst, herd, and token-soak
step, spike, stampede, and soak the same registry, and cursor-smoke is the end-to-end check.
Runs write a k6 summary, one-second server telemetry, and a report with p50, p95, and p99 by
operation.
The README’s own caveat applies: figures from Docker on macOS are directional, and capacity claims
are to be re-run on a representative Linux host before anyone cites them.
What is bounded by construction is easier to state. Every request runs under operator-set timeouts: HTTP requests at most 60 seconds, record locks at most 30, migration locks at most 300, and migration statements at most one hour. The connection pool has a configured maximum size, and its occupancy gauges are published on the metrics listener. GeoJSON and bounding-box responses are capped at 2 MiB of canonical output and refuse rather than truncate. Metrics label sets are closed, so a scrape cannot grow with traffic. The runtime applies no request-rate limit of its own; a registry that admits anonymous reads belongs behind an upstream limiter.
Support window and stability
Section titled “Support window and stability”Registry Stack is pre-1.0. The compatibility promise in
API stability and versioning takes effect at v1.0.0; until then
a minor release may contain breaking changes, each announced with a BREAKING: entry in the
release note, and a patch release stays compatible with the latest minor.
Security fixes follow the roll-forward model in Support window:
pre-1.0 releases receive fixes only in the latest v0.x release, so a deployment pinned to an
earlier minor has to upgrade to be covered.
The product’s own contracts carry that status: the runtime configuration is v1alpha1, and
v0.26.0 is its first release. The security posture is already fixed: the invariant matrix names
31 invariants, each enforced with a named negative test, covering package verification, role and
row-security boundaries, claim handling, connection-context cleanup, and value-free refusals.