Versioned archive. You are viewing v0.38.0. For the latest released guidance, use Latest release. Report archive issues on GitHub.
Publish and consume a Registry Discovery index
For the assertion provider and data publisher and operator and consumer or verifier
Registry Discovery helps an application find which Evidence Gateway or Registry Relay service can answer a question. In this tutorial you play every role: two providers publish a short public description of their service, an operator packages those descriptions into an index and serves it, and a consumer asks the index which services fit a requirement and picks one of each kind.
Before you start
Section titled “Before you start”Clone the repository and work from its root. Every service in this tutorial listens on loopback only.
git clone --depth 1 --quiet https://github.com/registrystack/registry-stack.gitcd registry-stackBuild the two Discovery binaries from the locked workspace and put them on your PATH:
. scripts/cargo-runtime-library-path.shregistry_cargo_build "$PWD" --locked --release -p registry-discovery -p registry-discoveryctlexport PATH="$PWD/target/release:$PATH"registry_cargo_build runs cargo build. On macOS the binaries also load an AWS-LC FIPS library
that stays in Cargo’s build directory, so the helper points DYLD_FALLBACK_LIBRARY_PATH at it for
this shell. Run every discoveryctl and discovery command below in this shell; the second shell
this tutorial opens only runs Python and curl.
discoveryctl is the operator’s offline tool: it checks the authoring project and packages the
index. discovery is the runtime that serves one verified package.
Confirm the provider publications
Section titled “Confirm the provider publications”Each provider describes its service in one public JSON-LD file. The checkout carries an example description for an Evidence Gateway deployment and one for a Registry Relay deployment. Check that yours are the files this tutorial was written against:
shasum -a 256 \ products/discovery/fixtures/descriptions/evidence.jsonld \ products/discovery/fixtures/descriptions/relay.jsonldfc96f3a8cb0d82239425ea5712dceca975a5899e5528616648174da661fae905 products/discovery/fixtures/descriptions/evidence.jsonld5a34fa469803b7c28b3d5e7134a42398e326a2f173aacae9090d29787bc8f4d7 products/discovery/fixtures/descriptions/relay.jsonldThe fixture bytes are deterministic examples of the same closed profile the two native products derive and package. A provider deploys the exact packaged file at its public description URL; the Discovery runtime does not reconstruct provider metadata.
Check the operator project
Section titled “Check the operator project”Copy the tutorial’s operator project out of the checkout, so the package you write stays separate from the source files:
cp -R products/discovery/tutorial/project discovery-projectAs the operator, you decide which providers the index includes. discovery-project/origins.yaml
lists the URL of each approved description:
schemaVersion: registry-discovery/origins/v1alpha1origins: - originId: tutorial-evidence catalogUrl: http://127.0.0.1:38090/evidence.jsonld profile: registry-discovery-v1alpha1 enabled: true - originId: tutorial-relay catalogUrl: http://127.0.0.1:38090/relay.jsonld profile: registry-discovery-v1alpha1 enabled: trueYou also decide which kind of evidence satisfies a requirement an application may have.
discovery-project/mappings/adult-status.yaml says that the adult-status requirement is met by
one Evidence type:
schemaVersion: registry-discovery/evidence-mapping/v1alpha1mappingId: urn:example:mapping:adult-statusmappingAuthorityId: urn:example:mapping-authorityrequirementId: urn:example:requirement:adult-statusjurisdiction: urn:example:jurisdictionalternatives: - evidenceTypeListId: urn:example:evidence-type-list:adult-status evidenceTypeIds: - urn:example:evidence-type:adult-statusCheck both files. Nothing serves the two URLs yet, and the check does not need them to.
--allow-loopback accepts the http://127.0.0.1 URLs; without it, discoveryctl accepts only
HTTPS origins, which is what a real deployment uses.
discoveryctl check --project discovery-project --allow-loopbackvalid origins=2 mappings=1discoveryctl check validates the files without contacting either URL. The package step in the
next section is the only step that fetches the descriptions.
Package the index
Section titled “Package the index”Play the two providers by serving their descriptions at the URLs origins.yaml approved. The
server keeps running, so open a second shell at the root of the checkout and start it there:
python3 products/discovery/tutorial/publication_server.py \ --descriptions products/discovery/fixtures/descriptionsBack in the first shell, package the index:
discoveryctl package \ --project discovery-project \ --output discovery-project/package \ --allow-loopbackpackaged packageDigest=sha256:<package-digest> catalogRevision=sha256:b4b7195f36691c245bf49a88a248049ed899c0c41dbf1a87a386571c0dbfba0f mappingRevision=sha256:332004ca3920c498539180946e8f2637e9998ba7e49cd98f31e19d6f818857acThe two revisions are content addressed. The catalog revision is a digest of what the provider
descriptions mean once normalized, and the mapping revision a digest of the operator’s
mappings, so the same inputs build the same revisions on any machine. A provider that
reformats its file without changing its meaning keeps the same catalog revision. To detect any
change to the exact bytes a provider published, compare each record’s originContentDigest,
which appears in the search results below.
The package digest is different: it covers the exact index file through the package’s
SHA256SUMS file, and the index records when this run fetched each description and when it was
built. Your package digest therefore differs from anyone else’s, and from your own next run.
The package step fetched each description once and wrote the complete package to
discovery-project/package. In the second shell, stop the publication server with Ctrl+C.
Discovery never contacts a provider while it serves, so nothing below needs it.
Serve the index
Section titled “Serve the index”discovery-project/runtime.yaml names the listener, the package root, and the request and
response limits. It reads the package root from DISCOVERY_PACKAGE_ROOT. Discovery accepts only
absolute paths for both files, so the command below builds them from $PWD. It names no origin, mapping, or trust setting: the runtime checks the package
against its SHA256SUMS at startup, then serves that index and nothing else.
In the first shell, start Discovery:
DISCOVERY_PACKAGE_ROOT="$PWD/discovery-project/package" \ discovery --runtime-config "$PWD/discovery-project/runtime.yaml"In the second shell, confirm that it is ready:
curl -s http://127.0.0.1:38080/ready{"status":"ready"}Resolve a requirement
Section titled “Resolve a requirement”Now play the consumer. An application knows what it needs to establish, such as whether a person is an adult, but not which provider can tell it. Ask Discovery which Evidence types satisfy the adult-status requirement in the example jurisdiction:
curl -s \ -H 'Content-Type: application/json' \ --data '{"requirementId":"urn:example:requirement:adult-status","jurisdiction":"urn:example:jurisdiction"}' \ http://127.0.0.1:38080/v1/evidence-types/resolve{ "requirementId": "urn:example:requirement:adult-status", "jurisdiction": "urn:example:jurisdiction", "mappingRevision": "sha256:332004ca3920c498539180946e8f2637e9998ba7e49cd98f31e19d6f818857ac", "alternatives": [ { "evidenceTypeListId": "urn:example:evidence-type-list:adult-status", "evidenceTypeIds": ["urn:example:evidence-type:adult-status"], "mappingId": "urn:example:mapping:adult-status", "mappingAuthorityId": "urn:example:mapping-authority" } ]}The answer carries the mapping revision the package step printed, so the application can record which version of your mapping it relied on. When a requirement has several alternatives, the application picks one, and then needs a provider for every Evidence type listed in it. Here there is one alternative with one type.
Search for the providers
Section titled “Search for the providers”Search for Evidence services that assert the resolved type:
curl -s --get \ --data-urlencode 'serviceKind=evidence' \ --data-urlencode 'evidenceType=urn:example:evidence-type:adult-status' \ http://127.0.0.1:38080/v1/services{ "catalogRevision": "sha256:b4b7195f36691c245bf49a88a248049ed899c0c41dbf1a87a386571c0dbfba0f", "items": [ { "recordId": "urn:registrystack:discovery:record:sha256:676659c10ce5cc9d353f4fd2816673c7947e612151efbbe1cc4d42372d9be9d5", "bindingId": "urn:registrystack:discovery:binding:sha256:2f9ccc4629c7ea2409d986a9c0ee6e3afe6e63326ca7b1be7d9eb8c69082d96c", "serviceId": "urn:example:service:evidence", "serviceKind": "evidence", "title": "Example Evidence", "description": "Public signed evidence assertions", "endpointUrl": "https://evidence.example.org", "publisherId": "urn:example:publisher", "jurisdictions": ["https://publications.europa.eu/resource/authority/territory/DEU"], "conformsTo": ["https://registrystack.org/evidence/profile/v1"], "evidenceTypeIds": ["urn:example:evidence-type:adult-status"], "semanticClassIds": [], "operationFamilyIds": [], "originId": "tutorial-evidence", "originUrl": "http://127.0.0.1:38090/evidence.jsonld", "originContentDigest": "sha256:fc96f3a8cb0d82239425ea5712dceca975a5899e5528616648174da661fae905", "originFetchedAt": "<fetched-at>" } ]}Relay services are found by what they hold and what they let you do with it: a semantic class, such as a person, and an operation family, such as a lookup. Search for Relay services that look up a person:
curl -s --get \ --data-urlencode 'serviceKind=relay' \ --data-urlencode 'semanticClass=urn:example:class:person' \ --data-urlencode 'operationFamily=urn:example:operation:lookup' \ http://127.0.0.1:38080/v1/services{ "catalogRevision": "sha256:b4b7195f36691c245bf49a88a248049ed899c0c41dbf1a87a386571c0dbfba0f", "items": [ { "recordId": "urn:registrystack:discovery:record:sha256:aa220c11f493c266bc22adf5dc7ca82fb7a83842e6e887dc0e8e5680f4f84244", "bindingId": "urn:registrystack:discovery:binding:sha256:cfbc42c572f4b0dfd81d10cd47c149e54451e2f0fa61f79b842b54eff51fb4e6", "serviceId": "urn:example:service:relay", "serviceKind": "relay", "title": "Example Relay", "description": "Public registry query metadata", "endpointUrl": "https://relay.example.org", "operatorId": "urn:example:operator", "registryAuthorityId": "urn:example:authority", "jurisdictions": ["https://publications.europa.eu/resource/authority/territory/DEU"], "conformsTo": ["https://registrystack.org/relay/profile/v2"], "evidenceTypeIds": [], "semanticClassIds": ["urn:example:class:person"], "operationFamilyIds": ["urn:example:operation:lookup"], "originId": "tutorial-relay", "originUrl": "http://127.0.0.1:38090/relay.jsonld", "originContentDigest": "sha256:5a34fa469803b7c28b3d5e7134a42398e326a2f173aacae9090d29787bc8f4d7", "originFetchedAt": "<fetched-at>" } ]}Both answers carry the catalogRevision the package step printed, because both come from the one
index you packaged. Each result names the description it came from, with the same digest you
checked at the start, and originFetchedAt, the time your package step fetched it.
Select and hand off
Section titled “Select and hand off”Each search found one service, so the choice is made. What the application saves is a
selection: the recordId and bindingId of the result it chose, what it searched for, and the
catalogRevision and mappingRevision it chose under. That is enough to explain the choice later
and to notice when the index changes. A selection is public metadata only. It is not a credential,
and it does not make the provider trusted.
Calling the provider is a separate step that does not involve Discovery. The application’s own
Evidence Gateway or Registry Relay configuration decides whether it trusts the selected service.
If it does, the application calls the service’s endpointUrl directly with that product’s
client: the Evidence client requests and verifies an assertion, and the Relay client finds the
matching operation in Relay’s own metadata and reads from it. No credential, request, or response
passes through Discovery. In an application, the Discovery namespace of the
unified Registry Stack client runs these
searches and saves selections, from Rust, Node.js, or Python.
Assign the maintenance work
Section titled “Assign the maintenance work”| Role | Maintained input | Change trigger | Handoff |
|---|---|---|---|
| Assertion provider | Packaged Evidence public description | Public Evidence endpoint, profile, evidence type, or issuer role changes | Publish the exact packaged bytes at the approved URL |
| Data publisher | Packaged Relay public description | Public Relay endpoint, profile, semantic class, operation family, or authority role changes | Publish the exact sealed artifact bytes at the approved URL |
| Operator | origins.yaml, mapping files, immutable package, and runtime.yaml | An approved origin or mapping changes | Run offline check, explicit package, deploy the complete package, pin its digest when required, then restart Discovery |
| Consumer or verifier | Saved exact selection and native product trust | A catalog revision, mapping revision, origin digest, binding, or local trust decision changes | Re-evaluate local trust, then call the selected native endpoint directly |
The operator does not run a synchronization scheduler or maintain a writable catalog. A failed package operation does not replace the deployed package. Deploy a complete new package and restart the process when the change is intentional.
Clean up the local services
Section titled “Clean up the local services”Stop Discovery in the first shell with Ctrl+C, then remove the project copy and the package written
into it:
rm -rf discovery-projectThe compiled binaries stay under target/release, so a second run reuses them.
What you built
Section titled “What you built”- The provider roles supplied two deterministic public descriptions.
- The operator checked explicit origins and mappings offline, packaged one immutable index, and served it with Discovery.
- The consumer resolved one requirement and found one Evidence and one Relay service, both from the same catalog revision, ready to save as selections.
- Trusting and calling the chosen services stays with the application and the product clients, outside Discovery.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause | Fix |
|---|---|---|
| The publication server or Discovery stops at startup | Port 38090 or 38080 is already in use | Stop the process using the port, then start the service again |
discoveryctl package cannot fetch an origin | The publication server is not running in the other shell | Start the publication server, then run the package step again; a failed package step leaves no partial package |
discoveryctl package refuses the output directory | discovery-project/package is left from an earlier run, and a package is never replaced in place | Remove discovery-project/package, then run the package step again |
| Cargo cannot resolve dependencies | The clean checkout has not acquired the locked Rust dependencies | Restore network access to the Cargo registry or provide a populated Cargo cache |
| A publication digest differs | The provider bytes do not match the tutorial’s reviewed fixture | Restore the clean checkout or review the changed publication before indexing it |
| Native handoff is refused | The selected binding does not match the adopter-owned product trust record | Keep the selection inert, correct the local trust decision, and retry the native handoff |
- Package and run a Registry Discovery index with your approved HTTPS origins, deployment directory, and runtime limits.
- Registry Discovery is an index explains why trust and invocation stay in the native products.
- Request Evidence from an application shows the native Evidence relying-party workflow.
- Publish a governed SQLite registry shows the native Relay publication and request workflow.