Registry Stack uses a maintainer-led governance model. Contributions, bug reports, questions, and review notes are welcome. Maintainers review incoming work according to project priorities, release risk, and available review capacity.
Registry Stack uses the Developer Certificate of Origin 1.1 for inbound contributions. By contributing, you certify that you have the right to submit the work under this repository's license and that the contribution can be included in the project.
Add a Signed-off-by trailer to every commit:
git commit -sThe trailer must use your real name or established project identity and an email address you are willing to associate with the contribution:
Signed-off-by: Name <name@example.com>
Pull requests are checked automatically for this trailer.
You may use coding agents or other automation while preparing a change. The person who commits, signs off, and submits the contribution is responsible for reviewing the result, running the relevant checks, and making sure the DCO certification is true.
- Maintainers may merge their own pull requests when the change is scoped and the relevant checks have passed.
- Every change reaches
mainthrough a pull request and the merge queue, including release, documentation, CI, and urgent fixes: themainruleset has no bypass actors and branch protection applies to administrators. - CI remains the reference gate for normal pull requests.
- The merge queue rebases each entry onto
mainitself before merging, so a pull request that shows BEHIND does not need a manual rebase for that reason alone. Rebase or force-push only to resolve a real conflict or to pick up a fix for red CI thatmainhas since fixed. - Required human review should be enabled only when maintainer capacity can satisfy it reliably.
Ordinary pull-request and main CI should complete within ten minutes, measured from workflow creation until every selected synchronous check finishes. Include runner scheduling time. Record reruns from the original event as well as the latest attempt; a fast retry does not erase a failed or slow first attempt. Use the longest job and total job minutes to distinguish serial work from runner contention. The maintainers own this budget alongside the CI workflow. Rust test shards run at most six at a time to reduce competition with the longer product integration jobs; all selected shards still execute.
Post-classification fanout is capped at eighteen static matrix slots by making
shorter and later-stage jobs wait for the existing Rust policy check. This
leaves capacity for the longer product, native client, documentation, and
security jobs to start. The Base Registry Engine contracts job takes six of
those slots: its PostgreSQL journeys run as three shards and its adopter
journeys in a lane of their own, which shortens the longest serial integration
job. Their original path selectors remain in force and an
explicit !cancelled() status check
lets selected jobs run after a Rust policy failure or skip while still honoring
workflow cancellation. The same guard requires successful change
classification, so a classifier failure cannot release downstream jobs. This
controls dependency ordering; GitHub Actions does not guarantee runner
scheduling priority.
The required Rust workspace check and the CI result summary aggregate the same raw Rust job results in parallel. Both accept successful and path-skipped jobs and fail for failed or cancelled jobs, so the final summary does not need to wait for the intermediate aggregate runner.
Reporter-only jobs that read dependency results and run one bounded Bash or
Python step use ubuntu-slim. Jobs needing actions, services, installs,
network access, builds, or tests remain on full runners. Slim jobs run in an
unprivileged container and share the plan's standard concurrency limits;
GitHub does not guarantee faster scheduling.
Optimize duplicate compilation and execution before adding more blocking jobs. Keep affected security, database/TLS, generated-contract, and basic adopter journeys enforced. Partitioned suites must retain their complete test inventory and feed the same aggregate result. Never meet the budget by hiding a failure, dropping an aggregate dependency, or increasing a timeout.
Repeated fuzz exploration and broad assurance can run asynchronously when an equivalent affected-change regression remains in CI. Before moving a check, name its owning workflow and maintainer, prove the replacement executes successfully, and document which changed inputs bring it back into ordinary CI. Nightly Rust coverage complements the product database gates; its default feature shards do not replace PostgreSQL or TLS execution.
The checks below run wholly or partly after review instead of on the pull
request. Each remains a job of RegistryStack CI (.github/workflows/ci.yml),
owned by the maintainers, runs in every nightly full sweep, and passed in that
workflow's scheduled full sweep before it moved: on 2026-09-24 for platform
assurance, the Linux release recipe and docs archives, and on 2026-09-27 for
the heavy integration tier and the Casework review examples:
- Platform fuzz smoke runs on the merge queue whenever the platform crates,
their products directory or the root Rust inputs change, including a
Cargo.lockchange that reaches a platform crate. Platform line coverage runs on the same inputs on main pushes and in the nightly sweep, outside theCI resultverdict, and uploads from protected main pushes. No pull-request input brings either back: the platform tests and hygiene checks still run there, and the merge queue holds the committed-corpus fuzz smoke before anything lands.nightly-security.ymladditionally explores every declared fuzz target daily, andnightly-rust-coverage.ymlreports workspace coverage daily. - The heavy integration tier runs on the merge queue, on main pushes, in the
nightly sweep and in manual runs, each behind its usual changed-path
selector. The release protected-ci checks read the main push run. The tier
covers the Base Registry Engine PostgreSQL and immediate-actions lanes (the
contracts lane stays on the pull request), the Base Registry Engine WASM
executor, Casework and Scheduling PostgreSQL, the Evidence, Base Registry
Engine and Casework tutorials, and the Base Registry Engine and Evidence
composition.
A pull request labeled
ci:fullruns the tier too; adding the label restarts the run.CI resultpasses a heavy job that review skipped, and fails one the classifier selected that did not succeed. Before you push a change these suites cover, run the matching local commands from the owning product guide (for exampleproducts/breg/scripts/test-postgres.shor the Casework and Scheduling PostgreSQL suites inAGENTS.md), because the merge queue is where their failure would otherwise first appear. - Release Linux native clients, for both architectures, runs only in the nightly sweep and a full manual run. Review and the merge queue prove each binding with the native binding job.
- Immutable docs archives run on the merge queue only for the archive
recipe's own inputs: the docs site package, lockfile and Astro
configuration, the archive and link-checking scripts under
docs/site/scripts/and the modules they import, and the docset, archive lock and repository-docs data. A change toci.ymlalone does not rebuild history there; the nightly sweep and a manual full run do. - The Casework tutorial job's step that verifies the Base Registry Engine, payment and standalone review examples runs only in the nightly sweep and a full manual run.
Merge-queue checks and main pushes select affected work using the event's exact
before/after commits, including every commit in a multi-commit push and both
owners of a renamed path. Shared build and CI inputs still select the broad
matrix. A Cargo.lock change without them selects the workspace packages that
reach a changed locked package, and still selects the broad matrix when the
lock cannot be parsed or compared, its format changes, a native, build-helper
or git-sourced package changes, or the change reaches half the workspace or
more. Missing comparison commits select all gates and refuse to claim archive
immutability without the required baseline.
The same RegistryStack CI workflow runs every gate daily at 22:37 UTC,
including immutable archives and both Linux client release recipes. Maintainers
own failures of this full regression sweep. A manual workflow run defaults to
the same full sweep; turning off full compares the chosen ref with main.
Periodic archive reconstruction validates current locked bytes, while event
runs additionally compare the lock against their actual change baseline.
Every pull request should make the review path clear:
- Explain the user-visible or operator-visible impact in the pull request summary.
- Keep changes scoped to one owning area whenever practical.
- Call out release, security, migration, compatibility, privacy, or governance concerns in the pull request notes.
- Run the checks that match the changed files, or state exactly which relevant check was skipped and why.
- Include tests for major functionality and bug fixes. If a test cannot be added, explain why in the pull request.
- Treat changes to authentication, authorization, assertion evaluation or signing, audit integrity, release provenance, deployment defaults, or data minimization as security-sensitive. These changes need explicit maintainer review notes, even when the maintainer is also the author.
- Schema and migration code refuses when it would drop rows; it never drops
them silently. A change that would lose persisted state fails with an error
that names what would be lost, and the operator decides the next step. Cover
the refusal with a test. Every retained domain and operational state table
requires row preservation, and the release upgrade rehearsal checks it for
the products it rehearses. v0.40.0 does not upgrade
its predecessor's state in place: an operator applies it to new databases,
as its release notes state, so it carries no v0.39.0 row forward. BReg's
fresh installations contain the current caller-scoped replay and verified
creator structures directly. No installation step converts or discards
earlier rows. BReg's native Record, Dataset, Action, and Proposal replacement
is a fresh baseline with no in-place upgrade for retired entity/Request
packages or state; native package evolution remains covered by its operator
gates. Casework's review-only replacement is another explicit fresh
baseline: it refuses retired WorkItem tables without converting or
deleting their data. Operators drain or cancel old reviews, retain the old
deployment and audit journal for reconciliation, and install the current
model in a fresh database. Its current-schema install and backup/restore
checks replace the unsupported in-place Casework rehearsal. Evidence's
normalized v0.42.0 baseline likewise requires a newly authored
evidence.yamlproject, target, and complete separate authority. Retired split projects and bundled authority have no in-place conversion; retain the previous deployment and audit, package the current model, and verify a fresh synthetic assertion before cutover. Messaging's and Scheduling's v0.42.0 schemas are fresh baselines as well: each installs its complete schema as one baseline and refuses by name a database whose ledger holds a pre-1.0 version. Operators retain the previous database and audit journal for reconciliation and apply the current package to a fresh database; messages still queued and holds and appointments recorded in the previous database are not carried forward. v0.44.0 adds three more: Casework's and Scheduling's schemas each start again at a fresh version 200 baseline and refuse by name a ledger below version 200, and BReg refuses by name the v0.43.0 attachment verification tables at startup, inbregctl doctor, and inbregctl planandapply. The operator path is the same: retain the previous database and audit journal, rebuild the BReg package with the currentbregctl testandbregctl package, and apply the current package to a fresh database. - Before 1.0, a release reads only the state its immediate predecessor wrote,
and an operator upgrades one release at a time, except at the explicit fresh
baselines. v0.40.0 starts fresh: it
does not upgrade state v0.39.0 wrote in place. It refuses a configuration
file that keeps a retired key or
apiVersionand names the replacement (config.removed-key,config.retired-api-version), refuses a BReg or Messaging package the earlier release built and a Scheduling database it wrote, and is applied to new databases; the BREAKING sections of its release notes give each step. v0.40.0 does not upgrade v0.39.0 state in place; apply to a new database. Except for the BReg, Casework, Evidence, Messaging, and Scheduling fresh baselines described above, from v0.40.0 onward keep everything the previous release could have written to a database, package, configuration file, or local state readable. Do not add code that reads, upgrades, or refuses by name the state of an older release, and remove such code in the release after the one that needed it, with a BREAKING entry in the product changelog. The removed-key and retired-apiVersiontables of the configuration conventions are the one standing exception: CFG-CHANGE-2 inCONFIG-CONVENTIONS.mdkeeps each refusal until its format's next stable version.FORWARD_PATH_FLOORinrelease/scripts/rehearse-upgrade.pynames the earliest shared predecessor, v0.43.0, the release before the v0.44.0 Casework, Scheduling, and BReg fresh baselines, andapi-stability.mdxstates the promise for operators.
New or changed release dependencies must follow the
Dependency Vetting Policy.
Document why the dependency is needed, whether it is runtime or tool-only,
license and advisory status, feature selection, transitive impact, and any
accepted risk with a review trigger.
Git dependencies require an immutable commit pin, a rationale, and a documented
review trigger. external/README.md records the retired
Crosswalk input and the review required before any reintroduction.
For new Rust YAML parsing or serialization, use the workspace-pinned
serde_norway dependency. Do not introduce a direct serde_yaml dependency
without an approved compatibility or security rationale.
Keep changes focused on the owning area:
crates/for Rust crates and runnable binaries.products/for product-owned specs, examples, fixtures, and docs.docs/site/for the public documentation site.release/for public release manifests, schemas, notes, and tooling.
Private planning, release evidence that is not already public, credentials, hosted deployment details, and internal review notes do not belong in this repository.
Public GitHub issue labels can help identify scope and priority. Start with:
enhancement: product, docs, or release improvements.criticality:p3: lower-risk work that is usually safer for first contributions than release or security-critical changes.
If an issue looks too broad, ask for a smaller slice before opening a pull request.
Run the checks that match the files you changed. The full current gate is in
.github/workflows/ci.yml.
Every change proposal or pull request for major new functionality MUST add tests for the new functionality to an automated test suite. The test coverage must exercise the behavior at the lowest practical level, such as Rust unit or integration tests, Python unittest coverage for scripts, Node test coverage for docs or UI scripts, or an existing product-specific automated suite. If a maintainer determines that a change is not major new functionality, the pull request should say so in the testing notes.
Major bug fixes should include the smallest relevant automated test, fixture, or docs check that proves the behavior. If a change is documentation-only, say so in the pull request checks section. If a code change genuinely cannot be tested, explain the reason and the manual or review evidence used instead.
Rust workspace:
cargo metadata --locked --format-version 1
cargo fmt --check
cargo check --locked --workspace --all-targets
cargo clippy --workspace --all-targets -- -D warnings
cargo test --locked --workspace
cargo deny checkOrdinary build and test scripts honor the compiler wrapper configured in Cargo
or supplied through RUSTC_WRAPPER. Leave the variable unset to use Cargo's
configuration, or set it explicitly to select a wrapper. An explicit
RUSTC_WRAPPER="" disables that wrapper; isolated proof scripts may do this
intentionally.
Keep one stable CARGO_TARGET_DIR per worktree while iterating, and do not
share a writable target directory between worktrees. Reusing that directory
avoids rebuilding unchanged artifacts. A compiler cache can help when outputs
must be rebuilt, but does not guarantee reuse across worktree paths. Keep
active build outputs until their consumers finish.
On macOS, the workspace's FIPS build of AWS-LC is a dynamic library in Cargo's
fingerprinted build output. Product gate scripts prepare the exact directory
from Cargo's build-script-executed message before they execute a built binary,
so they need no manual loader configuration. For a direct development run,
prefer cargo run. To execute target/debug/<binary> yourself, source the same
helper in that terminal and build through it first:
. scripts/cargo-runtime-library-path.sh
registry_cargo_build "$PWD" --locked -p registry-caseworkctl
./target/debug/caseworkctl --versionDo not disable the workspace fips feature, force a static AWS-LC FIPS build on
macOS, or select a dylib by globbing target/*/build: those approaches either
weaken the production-equivalent crypto path, fail on macOS, or can select a
stale module. A gate binary override such as CASEWORKCTL_BIN or RELAYCTL_BIN
must be independently runnable, including any runtime libraries it needs.
The root gate runs the full cargo deny check, advisories included. Open
RUSTSEC advisories with no upstream fix are ignored in deny.toml with a
scoped rationale and a review trigger; a newly published advisory fails CI
until it is fixed or gets its own documented ignore.
Registry Messaging product checks:
products/messaging/scripts/check-checkpoint.sh
products/messaging/scripts/check-contracts.sh
MESSAGING_TEST_DATABASE_URL=<disposable database> cargo test --locked \
-p registry-messaging --features postgres-test --test postgres_migrateThe Messaging PostgreSQL suites fail, rather than skip, without
MESSAGING_TEST_DATABASE_URL; point it at a server whose databases you can
discard.
Document Store product checks:
products/documents/scripts/check-checkpoint.sh
products/documents/scripts/check-contracts.sh
DOCUMENTS_TEST_DATABASE_URL=<disposable database> cargo test --locked \
-p registry-documents --features postgres-test \
--test postgres_startup --test postgres_operator --test postgres_documents \
--test postgres_s3 --test postgres_hooks
DOCUMENTS_TEST_DATABASE_URL=<disposable database> cargo test --locked \
-p registry-documentsctl --features postgres-test --test postgres_cli
cargo build --locked -p registry-render
DOCUMENTS_TEST_DATABASE_URL=<disposable database> \
REGISTRY_RENDER_BIN="$PWD/target/debug/registry-render" cargo test --locked \
-p registry-documents --features postgres-test --test postgres_journeyThe Document Store PostgreSQL suites fail, rather than skip, without
DOCUMENTS_TEST_DATABASE_URL, and postgres_journey also fails without
REGISTRY_RENDER_BIN; point the URL at a server whose databases you can
discard.
Release source checks:
These checks require Python 3.11 or later.
python3 -m unittest release/scripts/test_registry_release.py
release/scripts/registry-release validate release/manifests/registry-stack-beta-28.yaml
release/scripts/registry-release audit release/manifests/import-map-2026-06-24.yaml
release/scripts/check-release-source-model.sh
python3 -m unittest release/scripts/test_check_release_source_model.pyDocs checks:
cd docs/site
npm ci
npm test
npm run checkIf you cannot run a relevant check, say which command was skipped and why.
Release candidates are built once from an exact protected-main commit, a pinned
Rust builder image, and locked Cargo dependencies in
.github/workflows/release-candidate.yml. The candidate records SHA256 hashes,
OCI digests, SBOMs, and scan evidence before publication promotes the exact
bytes. Independent repeatability exercises are scheduled assurance work, not a
duplicate build in every ordinary Beta transaction. Public repeatable-build
evidence is recorded in
release/REPEATABLE-BUILDS.md.
Site reference pages and data are ignored build outputs. Commit their authored
inputs and generators, then run npm test and npm run check under docs/site;
both generate the required outputs from a fresh checkout. See the
docs contributor workflow.
Product-owned generated schemas, the CLI command catalog
crates/registry-cli-docs/catalog.json, and checked-in release snapshots still
belong in the source change and must reproduce the same bytes when regenerated.
Do not introduce generators that depend on
wall-clock time, unordered traversal, ambient local paths, network responses, or
unlocked dependencies unless the generated output is normalized or pinned so the
same source can reproduce it exactly.
Do not open public issues or pull requests for suspected minimum-disclosure failure, auth bypass, audit redaction failure, source connector data leakage, signing key handling bugs, or other vulnerabilities. Follow SECURITY.md.