Skip to main content

registry-discovery-client-py

Synchronous PyO3 binding for the bounded Rust registry-discovery-client SDK. It performs exact service search, Evidence Type resolution, and ambiguity-safe selection. A returned selection is inert public metadata. Apply local trust policy before calling its Evidence or Relay endpoint.

Starting with Registry Stack v0.23.0, install the exact client version that matches the Discovery deployment:

python -m pip install "registry-discovery-client==<version>"

Published v0.23.0 and later distributions carry manylinux wheels requiring glibc 2.17 or newer for Linux amd64 and Linux arm64, plus a macOS arm64 wheel.

The public methods accept only ordinary built-in JSON values: None, bool, signed 64-bit int, finite float, str, list, and dict with string keys. They reject custom objects, subclasses, tuples, cycles, and oversized values before sending a request. Invalid configuration always raises DiscoveryClientError(kind="configuration"); invalid request or selection values raise DiscoveryClientError(kind="query"). Error messages never echo caller values.

from registry_discovery_client import (
    DiscoveryClient,
    accept_selection,
    select_evidence_alternative,
    select_evidence_service,
    validate_selection_structure,
)
from registry_evidence_client import EvidenceClient

# These values come from application-owned configuration or deployment
# ceremony. They are never copied from the Discovery response being checked.
evidence_pins = {
    "serviceId": "urn:example:service:evidence",
    "endpointUrl": "https://evidence.example.invalid/",
    "publisherId": "urn:example:publisher",
    "legalIssuerId": "urn:example:legal-issuer",
    "technicalProviderId": "urn:example:technical-provider",
    "jurisdictions": ["urn:example:jurisdiction"],
    "conformsTo": ["urn:example:evidence-profile"],
    "matchedCapability": {
        "kind": "evidence-type",
        "id": "urn:example:evidence-type",
    },
    "resolution": {
        "requirementId": "urn:example:requirement",
        "jurisdiction": "urn:example:jurisdiction",
        "mappingRevision": (
            "sha256:1111111111111111111111111111111111111111111111111111111111111111"
        ),
        "evidenceTypeListId": "urn:example:evidence-list",
        "evidenceTypeIds": ["urn:example:evidence-type"],
        "mappingId": "urn:example:mapping",
        "mappingAuthorityId": "urn:example:mapping-authority",
    },
    "originId": "approved-origin",
    "originUrl": "https://publisher.example.invalid/catalog.jsonld",
}


def accepts_expected_evidence(candidate):
    resolution = candidate.get("evidenceResolution") or {}
    return (
        candidate["serviceKind"] == "evidence"
        and candidate["serviceId"] == evidence_pins["serviceId"]
        and candidate["endpointUrl"] == evidence_pins["endpointUrl"]
        and candidate.get("publisherId") == evidence_pins["publisherId"]
        and candidate.get("legalIssuerId") == evidence_pins["legalIssuerId"]
        and candidate.get("technicalProviderId")
        == evidence_pins["technicalProviderId"]
        and candidate["jurisdictions"] == evidence_pins["jurisdictions"]
        and candidate["conformsTo"] == evidence_pins["conformsTo"]
        and candidate["matchedCapability"] == evidence_pins["matchedCapability"]
        and {
            key: resolution.get(key)
            for key in evidence_pins["resolution"]
        }
        == evidence_pins["resolution"]
        and candidate["originId"] == evidence_pins["originId"]
        and candidate["originUrl"] == evidence_pins["originUrl"]
    )


client = DiscoveryClient("https://discovery.example.invalid/")
resolved = client.resolve_evidence_types({
    "requirementId": "urn:example:requirement",
    "jurisdiction": "urn:example:jurisdiction",
})
context = select_evidence_alternative(resolved)  # refuses zero or many alternatives
for evidence_type_id in context["evidenceTypeIds"]:
    services = client.search_evidence_services({
        "evidenceTypeId": evidence_type_id,
        "jurisdiction": context.get("jurisdiction"),
    })
    # The adopter chooses explicitly. Discovery supplies no catalog ranking.
    matches = [
        item for item in services["items"]
        if item["serviceId"] == evidence_pins["serviceId"]
    ]
    if len(matches) != 1:
        raise ValueError(
            "the locally expected Evidence service is unavailable or ambiguous"
        )
    chosen = matches[0]
    selection = select_evidence_service(services, {
        "recordId": chosen["recordId"],
        "evidenceTypeId": evidence_type_id,
        "resolution": context,
    })

    # Structural validation checks shape and capability binding. It does not
    # establish origin authenticity, currentness, or trust.
    checked = validate_selection_structure(selection)
    accepted = accept_selection(checked, accepts_expected_evidence)

    # Credentials and the native client are created only from the ephemeral
    # accepted handoff. The values below are application-owned configuration.
    evidence = EvidenceClient(
        accepted.endpoint_url,
        trusted_jwks,
        revoked_key_ids,
        token,
    )
    checked = accepted.selection
    resolution = checked.get("evidenceResolution")
    if resolution is None:
        raise ValueError("missing Evidence resolution")
    prepared = evidence.prepare({
        **local_evidence_policy,
        "requirement": resolution["requirementId"],
        "evidence_type": checked["matchedCapability"]["id"],
    })
    verified = evidence.request_and_verify(prepared)

An Evidence alternative is an AND-list. The loop performs the search, explicit choice, trust check, and native request for every context["evidenceTypeIds"] member. The context supplies the resolved requirement and selected Evidence Type. The native definition and local policy still supply the purpose, audience, issuer/provider identity, configuration revision, selectors, and expected outputs.

validate_selection remains a compatibility alias, but its behavior has always been structural. New code should use validate_selection_structure so the result cannot be mistaken for a trust decision.

Persisted selections and renewal

Persist only the inert selection dictionary, never AcceptedServiceSelection. Offline loading can establish structural validity, but cannot prove that the catalog, mapping, endpoint, roles, or application policy are still current:

import json

persisted = validate_selection_structure(json.loads(saved_selection_json))
if application_selection_age_is_acceptable(persisted["originFetchedAt"]):
    accepted = accept_selection(persisted, accepts_expected_evidence)
    # Construct credentials and the native client only after this point.

The application owns the offline age limit. Discovery deliberately supplies no universal time-to-live.

For online renewal, resolve and search again, explicitly choose the same local service, and build a fresh selection. renew_unchanged_selection accepts only fetch-provenance and global catalog-revision changes:

from registry_discovery_client import renew_unchanged_selection

previous_resolution = persisted["evidenceResolution"]
fresh_resolved = client.resolve_evidence_types({
    "requirementId": previous_resolution["requirementId"],
    "jurisdiction": previous_resolution.get("jurisdiction"),
})
fresh_context = select_evidence_alternative(
    fresh_resolved,
    previous_resolution["evidenceTypeListId"],
)
evidence_type_id = persisted["matchedCapability"]["id"]
fresh_services = client.search_evidence_services({
    "evidenceTypeId": evidence_type_id,
    "jurisdiction": fresh_context.get("jurisdiction"),
})
fresh_matches = [
    item for item in fresh_services["items"]
    if item["serviceId"] == evidence_pins["serviceId"]
]
if len(fresh_matches) != 1:
    raise ValueError(
        "the previously selected service was withdrawn or is ambiguous"
    )
fresh = select_evidence_service(fresh_services, {
    "recordId": fresh_matches[0]["recordId"],
    "evidenceTypeId": evidence_type_id,
    "resolution": fresh_context,
})
renewed = renew_unchanged_selection(persisted, fresh)
accepted = accept_selection(renewed, accepts_expected_evidence)

A changed service identity, endpoint, issuer/provider, profile, jurisdiction, capability, origin, or mapping context raises DiscoveryClientError(kind="selection_changed"). A withdrawn record fails the fresh selection. Both cases require explicit reselection and a new local acceptance decision; renewal never switches to another service or Evidence alternative automatically.

Relay follows the same boundary with search_relay_services and select_relay_service. The selection retains both the semantic class and operation family. Apply exact local Relay pins with accept_selection, pass only accepted.endpoint_url to registry_relay_client.RelayClient, then use native Relay metadata to choose the concrete resource and operation. Discovery never invents route arguments.

Release files for registry-discovery-client 0.26.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for registry-discovery-client 0.26.0
File Interpreter ABI Platform
registry_discovery_client-0.26.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.10 abi3 Linux glibc 2.17+ x86-64 Details
registry_discovery_client-0.26.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.10 abi3 Linux glibc 2.17+ ARM64 Details
registry_discovery_client-0.26.0-cp310-abi3-macosx_11_0_arm64.whl CPython 3.10 abi3 macOS 11.0+ ARM64 Details

Total release size: 5.1 MB

Release files / registry_discovery_client-0.26.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL registry_discovery_client-0.26.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 1.8 MB
Tags CPython 3.10 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
35fd60c3a33673afae735723f0dcfdc9b9ec041faf7cbce447e0284657f974be
BLAKE2b-256 checksum
How to use checksums
13b6defd078d80da658a25988c09f2bf18f91deaecf14ae96a3c2aa78daf96e4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 4, 2026.

Transparency log

Release files / registry_discovery_client-0.26.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL registry_discovery_client-0.26.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 1.7 MB
Tags CPython 3.10 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
fccc0094b54ec30503453d09fab46e57801a5e6494accb523e14ffeb4f57f947
BLAKE2b-256 checksum
How to use checksums
bf68071498b31d85bef0fc9cd66adaa71a5e2a62fd9ad4816142e31c12419a97
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 4, 2026.

Transparency log

Release files / registry_discovery_client-0.26.0-cp310-abi3-macosx_11_0_arm64.whl

Download URL registry_discovery_client-0.26.0-cp310-abi3-macosx_11_0_arm64.whl
Size 1.6 MB
Tags CPython 3.10 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
0bbf24a9c315b7231495f19900681eb6308b35af2e3f3c1d49fd04ed09f1d291
BLAKE2b-256 checksum
How to use checksums
0f002841a3535dffa5a2fad00aab489a3ba4bbafb8fb15b65e8d96430e4aafc5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 4, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.26.0 This release

3 release files

0.25.0

3 release files

0.24.0

3 release files

0.23.0

3 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page