Preview release. These docs are a work in progress. Pages are still being written, links may break, and structure may shift without notice. Treat everything here as a draft and report issues on GitHub.
Registry Notary overview
Registry Notary is the claim evaluation and credential issuance product of the registry stack. It evaluates configured eligibility claims against your registry data, serves signed delegated evaluation to trusted peer Notaries, and issues SD-JWT VC credentials with selective disclosure. It also issues wallet-compatible credentials over OpenID for Verifiable Credential Issuance (OID4VCI). The verification receipt Registry Notary produces is an evidence artifact, not an eligibility decision: the issuing program decides what an entitlement is.
Stack commitments: safeguards, reviewability.
Claim model
Section titled “Claim model”Claims are declared in YAML under evidence.claims. Each claim has a stable id, a subject_type,
a typed value, named inputs, optional depends_on ordering, one or more source_bindings that
name the connector and connection to read data from, an evaluation rule (one of extract,
exists, cel, plugin), a disclosure policy, and the output formats and
credential_profiles the claim may produce.
For the field-by-field schema, see the claim config reference.
The demo at
demo/config/registry-notary.yaml
ships three claims: date-of-birth (Extract rule), farmed-land-size (Extract rule), and
farmer-under-4ha (CEL rule that depends on farmed-land-size).
Evaluation engine
Section titled “Evaluation engine”Registry Notary supports four rule types:
extract: reads a named field from a named source binding. The simplest rule type: no logic, one field.exists: returns true if a record was found at the source, false otherwise.cel: evaluates a Common Expression Language (CEL) expression with named bindings. Bindings can reference other claim results viabindings.claims, letting a CEL claim compose from upstream Extract results without re-fetching source data.plugin: declares an external plugin name. Not yet implemented; the evaluator returnsOperationUnsupported(HTTP 501) for any claim that uses it.
Claims that declare depends_on are evaluated in dependency order. The validator enforces that
depends_on relationships form a directed acyclic graph (DAG).
Disclosure policy modes
Section titled “Disclosure policy modes”Each claim carries a disclosure block that controls what the caller receives.
| Mode | What the caller receives |
|---|---|
value | The full evaluated value (for example, the literal date or number). |
predicate | A boolean: does the subject satisfy the rule. The underlying value is not returned. |
redacted | No value and no predicate. The claim result is present in the response but carries no data. |
The claim’s disclosure.default sets the mode used when the caller does not request a specific
mode.
disclosure.allowed lists the modes the caller may request.
disclosure.downgrade controls what happens when the caller requests a mode not in allowed:
deny returns a DisclosureNotAllowed error; default falls back to the claim’s default mode;
redacted forces the result to Redacted.
The default for a new claim with no disclosure config is default: redacted, allowed: [redacted], downgrade: deny, which is fail-closed.
SD-JWT VC issuance
Section titled “SD-JWT VC issuance”Registry Notary issues credentials in
SD-JWT VC format
via POST /credentials/issue.
Reusable SD-JWT signing and holder-proof checks come from Registry Platform; Registry Notary owns
the profile config, claim selection, disclosure decision, and API workflow.
Key properties:
- Algorithm: EdDSA (Ed25519).
- Token type header:
dc+sd-jwt. - Media type:
application/dc+sd-jwt. - Key source: the issuing key is an OKP Ed25519 JWK loaded from an environment variable named by
credential_profiles.<id>.issuer_key_envin the YAML config (for example,REGISTRY_NOTARY_ISSUER_JWK). - Disclosures: each claim field is wrapped in an individual SD-JWT disclosure with a SHA-256 digest, allowing the holder to present only selected fields.
- Holder binding: the credential profile can require a
did:jwkholder proof. Proof of possession is only supported withdid:jwk; other DID methods inallowed_did_methodscause a startup validation error.
The public verification key is exported at /.well-known/evidence/jwks.json. Credential profiles
are declared under evidence.credential_profiles and each specifies a format, issuer DID,
vct (Verifiable Credential Type URI), validity_seconds, holder_binding, allowed_claims,
and disclosure.
OID4VCI wallet flow
Section titled “OID4VCI wallet flow”Registry Notary emits OpenID for Verifiable Credential Issuance (OID4VCI) Draft 13 compatible routes. The Registry Lab citizen self-attestation scenario uses this surface to drive a wallet through eSignet authentication and into a Notary-issued SD-JWT VC. The four routes are:
GET /.well-known/openid-credential-issuer: returns the Credential Issuer Metadata document (issuer URL, credential endpoint, nonce endpoint, authorization servers, supported credential configurations). Media type:application/json.GET /oid4vci/credential-offer: returns aCredentialOfferfor one credential configuration (whencredential_configuration_idis set in the query) or for every configuration the server knows about (when no query is set). Media type:application/json.POST /oid4vci/nonce: returns a freshc_noncethe holder uses when signing the proof of possession JWT. Media type:application/json.POST /oid4vci/credential: returns the SD-JWT VC. The caller presents a bearer access token from the configured authorization server and a proof JWT signed with the holder’sdid:jwkkey carrying thec_nonce. Media type:application/json.
Holder binding uses did:jwk (Decentralized Identifier method jwk): the wallet derives a
public key, encodes it as did:jwk:<base64url-jwk>, signs the proof JWT with the matching
private key, and Registry Notary embeds the holder’s did:jwk into the cnf claim of the
issued credential. The route is exercised from the lab by
just citizen-oid4vci-login, just citizen-oid4vci-code, just citizen-oid4vci-token,
just citizen-oid4vci-probe, and just citizen-oid4vci-report; the host-side Notary instance
runs on port 4325. For the full request and response shapes, see
OID4VCI routes in the Notary reference.
Federated delegated evaluation
Section titled “Federated delegated evaluation”Registry Notary can serve a static-peer federation MVP for one operation:
POST /federation/v1/evaluations. A trusted Notary sends a compact signed JWT request with
typ = registry-notary-request+jwt; the serving Notary verifies the peer, audience, time window,
profile, purpose, replay key, and local policy before reading any source data. The response is a
compact signed JWT with typ = registry-notary-response+jwt.
Federation uses two config surfaces:
- Registry Manifest publishes public discovery metadata:
federation,evaluation_profiles, andregistry-notaryevidence offerings whoseaccess.rulesetmatches a public evaluation profile ruleset. - Registry Notary enforces private runtime policy:
federation.peers,federation.evaluation_profiles, signing key config, pairwise subject hash secret, replay settings, response shaping, and emergency denylist entries.
This is delegated evaluation only. The MVP does not implement open federation, dynamic trust-chain discovery, audit checkpoint exchange, shared replay storage, or federated credential issuance. The operator-facing contract is in the federated evaluation MVP spec and federated evaluation operator guide.
CCCEV JSON-LD rendering
Section titled “CCCEV JSON-LD rendering”When a caller requests application/ld+json; profile="cccev", Registry Notary renders
claim evaluation results as Core Criterion and Core Evidence Vocabulary (CCCEV) JSON-LD.
The output includes:
- A
@contextblock with thecccev,dcterms,foaf,time, andxsdnamespace prefixes. - Evidence nodes typed as
cccev:Evidence. - CCCEV properties:
cccev:isProvidedBy,cccev:supportsRequirement,cccev:supportsValue,cccev:validityPeriod, andcccev:isConformantTo. - Supported value structures using
cccev:SupportedValueandcccev:providesValueFor.
The claim definition’s optional cccev.requirement_type field populates the
cccev:InformationRequirement or other requirement type annotation in the rendered output.
The CCCEV output aligns with the vocabulary but does not claim full profile conformance.
HTTP source connectors
Section titled “HTTP source connectors”Registry Notary fetches source data over HTTP using one of two connector kinds:
registry_data_api: posts to{base_url}/{dataset}/{entity}with query parameters shaped for the Registry Data API. The connector authenticates with a per-connection bearer token.dci: uses the Social Protection Digital Convergence Initiative (SP DCI) sync search protocol. The request body follows the DCIregistry/sync/searchshape; the response is parsed via a configurable JSON path to extract records. Defaults: search path/registry/sync/search, records path/message/search_response/0/data/reg_records, max results2.
Both connectors use an HTTP client with a 10-second timeout and a strict outbound URL policy by
default. Private-network HTTP source registries are allowed only when a source connection sets
allow_insecure_private_network: true; the opt-in still blocks cloud metadata endpoints. Source
connection credentials are bearer tokens loaded from environment variables named by the
token_env field on each source_connections entry. No source credentials appear in the YAML
config file itself.
Audit and redaction
Section titled “Audit and redaction”Every request that touches claim evaluation is authenticated and audited. Audit events are
serialized as platform audit envelopes in JSON Lines (JSONL). Fields: event_id, occurred_at,
principal_id, decision, method, path, status, verification_id, claim_hash,
row_count, error_code. verification_id and claim_hash are optional and may be omitted
for redacted results.
The audit sink is configurable as stdout (default) or file/jsonl. When sink: file or
sink: jsonl, audit.path must be set. audit.hash_secret_env is required so redacted audit
identifiers use a deployment-specific secret. The demo config writes to
demo/var/registry-notary-audit.jsonl.
What Registry Notary is not
Section titled “What Registry Notary is not”Registry Notary is a standalone HTTP service. It is independently deployable and does not import or link Registry Relay application code. A Relay instance may publish metadata that points to a Notary instance, but the two services remain decoupled at the product boundary. Both services share Registry Platform primitives for security and operational behavior. For questions about which service owns which concern, see Boundaries and map.
Three boundaries to keep in mind:
- Not an eligibility decision. The verification receipt is an evidence artifact. The issuing program, not Registry Notary, converts a verified claim into an entitlement.
- Not a wallet. Registry Notary issues credentials over OID4VCI; the wallet that holds and presents the credential is a separate component.
- Not a credential authority. The DID, the verification key, and the credential profile config belong to the deployment, not to Registry Notary as a product.
Status and v0 caveats
Section titled “Status and v0 caveats”Registry Notary is at version 0.2.1. It was renamed from evidence-server; the rename is
documented in docs/rename-2026-05-23/.
Known limitations at this version:
- The
Pluginrule type is declared in config but not yet implemented. - Proof of possession for holder binding only supports
did:jwk. Other DID methods inallowed_did_methodscause a startup validation error. - Inbound auth supports either static API-key/bearer-token mode or OIDC mode. Both modes use shared Registry Platform primitives, but Registry Notary still owns its config shape and scope enforcement.
- The W3C Verifiable Credentials Data Model envelope is not used. Issued credentials are SD-JWT format directly, without a VC Data Model wrapper.
- OID4VCI route shapes are Draft 13 compatible. No conformance test fixture is pinned at this
version, so the broader spec claim level is
aligns_with. - Federation is static-peer delegated evaluation only. Active-active deployments need shared replay storage before privileged federation traffic is enabled across multiple serving instances.