Skip to content
Registry StackDocsDevelopment (unreleased)

How Registry Discovery works

View as Markdown

You have packaged an index, resolved one requirement, and found one provider. An application asks Discovery two questions: what a requirement resolves to, and which provider serves each answer.

A Registry Evidence deployment can publish a public description of itself: its service identifier, its endpoint, the Evidence types it asserts, its jurisdictions, and optional publisher, operator, and issuer identifiers. Private configuration never enters that description. Anyone can publish such a file, so Discovery lists only the descriptions an operator approved, by URL, in origins.yaml.

Approval is the whole meaning of a listing: the operator chose that origin, and the provider published those bytes. Discovery does not authenticate the provider, rank one provider above another, or attach trust to the publisher and issuer identifiers. They describe a service; they do not vouch for it.

An application knows what it must establish, such as adult status, but not which Evidence types prove it. The operator writes that answer as a mapping: a requirement, an optional jurisdiction, and one or more alternatives. Each alternative is a list of Evidence types that are all required together; the application satisfies the requirement by meeting any one alternative.

A resolve request matches a mapping by its exact requirement and jurisdiction. Identifiers are compared exactly, with no normalization, so a request for another jurisdiction finds nothing rather than a near match. Every alternative carries the mappingId and the mappingAuthorityId, the body that stated the mapping.

The response also carries mappingRevision, a SHA-256 digest of every mapping in the index. It changes only when what the mappings say changes.

For each Evidence type in the chosen alternative, the application searches for services that assert it, optionally narrowed by jurisdiction, service identifier, or conformance profile. Every filter is an exact match and the results are not ranked. When more services match than the configured limit allows, Discovery refuses the search instead of returning a partial list.

Each result is one record: one exact capability of one service, as one approved origin published it. Its bindingId is a digest of the service identifier, endpoint, profiles, and Evidence types, and its recordId is a digest of the origin and the binding. Two origins that describe the same service produce two records that never merge, so an application always knows which publication it read.

The response carries catalogRevision, a SHA-256 digest of what every service record says. Fetch times and the raw bytes are left out, so packaging the same descriptions again gives the same revision.

What does a selection hold, and what does it not do?

Section titled “What does a selection hold, and what does it not do?”

The application saves a selection for each type it needs: the chosen record and binding, the Evidence type it matched, the whole alternative it came from, both revisions, and the origin’s provenance. The Registry Stack client refuses a selection whose binding no longer matches its fields, whose service does not assert the matched type, or whose service does not cover the resolved jurisdiction. Keeping the whole alternative stops a set of types required together from shrinking to one.

A selection is public metadata, never a credential or a trust decision. Before calling the provider, the application applies its own policy to the selection. Only when that policy accepts it does the client hand over the provider’s base URL, and that handoff exists in memory only: it is never saved, so the policy runs again each time. The application then calls Registry Evidence directly, with its own credentials and its own Evidence trust configuration. Discovery is never on that path.

Nothing, until the operator packages again. Discovery fetches each approved description once, while packaging, and never contacts a provider while it serves. Each record keeps originContentDigest, the SHA-256 of the exact bytes that fetch received, and the time of the fetch. The digest lets anyone compare what the index was built from with what the provider publishes now. It is not a signature: it records which bytes were fetched, not who wrote them.

When the operator packages again, a republish that changes only formatting moves originContentDigest but leaves catalogRevision and every record identity alone. A change to an endpoint or a type changes the binding, and any change to what a record says changes catalogRevision. An application that saved a selection can renew it from a fresh search only when everything that bears on trust is unchanged: the service, endpoint, roles, jurisdictions, capability, mapping, and origin. Fetch provenance and the catalog revision may move. Any other change asks the application’s policy to accept the selection again.

Why does a new revision need a new package?

Section titled “Why does a new revision need a new package?”

discoveryctl package writes the index and a SHA256SUMS file into a directory that did not exist before, and the package digest is the SHA-256 of that SHA256SUMS file. discovery recomputes every entry before it listens, refuses a changed, missing, or extra file, and refuses a package other than the one package.expectedDigest pins. It then serves that one index read-only until it stops. An index that changed in place would be one nobody reviewed, so a revision always means a new package, a new pin, and a restart.