Documentation preview. These pages target Registryctl v0.15.0, which is not published yet. Public download commands will not work until that release exists. For runnable released instructions, use v0.13.0.
Registry Notary exposes an issuer-initiated pre-authorized code flow for
issuing registry-backed dc+sd-jwt credentials to holder wallets. Source-free
self_attested claims remain evaluation-only and cannot appear in an OID4VCI
credential configuration.
Supported 1.0 profile
Section titled “Supported 1.0 profile”The wallet facade supports:
- registry-backed claims whose exact compiler-pinned Relay execution is stored in a Notary transaction;
dc+sd-jwtcredentials;- EdDSA or ES256 issuer signing, selected by the credential profile;
- EdDSA JWT holder proof with
did:jwkbinding; - an issuer-initiated pre-authorized code grant;
- one credential per immediate response.
It does not support wallet-facing authorization-code grants, a public nonce route, response next nonces, ES256 holder proof, source-free issuance, EUDI or HAIP profiles, PAR, DPoP, or wallet attestation.
The eSignet authorization code used during the browser callback is internal to Notary’s identity-provider login. The wallet never receives or redeems it.
Topology and prerequisites
Section titled “Topology and prerequisites”Deploy one Notary for each Relay authority. Notary owns its transaction, pre-authorized code, proof replay, evaluation, audit, and credential-status state. Use the Notary-owned PostgreSQL schema for production or multiple instances. In-memory state is for explicit local single-process use only.
Before enabling the wallet facade:
- configure the Relay connection and compile the claim consultation pins;
- configure
subject_accessfor the identity-provider binding claim; - bind every OID4VCI configuration to one mutually valid registry-backed claim and credential profile;
- configure separate signing keys for identity-provider client assertions, Notary access tokens, and credentials;
- expose HTTPS issuer, callback, token, credential, and Type Metadata URLs;
- set the Notary state backend and rate limits.
See the operator configuration reference and credential issuance migration.
End-to-end developer check
Section titled “End-to-end developer check”-
Open this URL in the citizen’s browser:
https://notary.example.gov/oid4vci/offer/start?credential_configuration_id=<id> -
Complete the configured identity-provider login.
-
After the callback succeeds, scan or paste the rendered
openid-credential-offer://URI into the wallet. -
Enter the separately displayed numeric PIN when the wallet asks for the
tx_code. -
Confirm that the wallet redeems the offer at
/oid4vci/tokenand calls/oid4vci/credentialwith an EdDSAdid:jwkproof. -
Verify the returned credential with the issuer JWKS and the expected holder binding.
A successful check establishes all of these observations:
- issuer metadata is reachable at
/.well-known/openid-credential-issuer; - Type Metadata is reachable at
/.well-known/vct/{vct_path}; - metadata advertises
dc+sd-jwt,did:jwk, EdDSA holder proof, and the configured EdDSA or ES256 issuer algorithm; - metadata has a
/oid4vci/tokenendpoint and no nonce endpoint; - the offer contains exactly the
urn:ietf:params:oauth:grant-type:pre-authorized_codegrant; - the default offer contains a numeric
tx_codedescription; - the token response supplies the proof nonce bound to that transaction;
- the credential response has no next-nonce fields;
- the SD-JWT VC has the expected
iss,vct, EdDSAdid:jwkholder binding, and registry-backed disclosures.
sequenceDiagram
autonumber
actor Citizen
participant Browser
participant Notary as Registry Notary
participant IdP as Identity provider
participant Relay as Registry Relay
participant Wallet
Citizen->>Browser: Open offer start URL
Browser->>Notary: Start configured credential transaction
Notary-->>Browser: Redirect to identity provider
Browser->>IdP: Authenticate citizen
IdP-->>Browser: Return internal authorization code
Browser->>Notary: Callback with code and state
Notary->>IdP: Exchange code and validate subject
Notary->>Relay: Execute compiler-pinned consultation
Relay-->>Notary: Typed registry evidence
Notary-->>Browser: Render pre-authorized offer and PIN
Citizen->>Wallet: Transfer offer and enter PIN
Wallet->>Notary: Redeem pre-authorized code and PIN
Notary-->>Wallet: Access token and proof nonce
Wallet->>Notary: Submit EdDSA did:jwk proof
Notary-->>Wallet: Holder-bound dc+sd-jwt
Transaction code modes
Section titled “Transaction code modes”The secure default is:
oid4vci: pre_authorized_code: enabled: true pre_authorized_code_ttl_seconds: 300 tx_code: required: true input_mode: numeric length: 6Codes are short-lived and single-use. Wrong PIN attempts are bounded per offer, and invalid-code attempts are rate limited per client address.
Some wallet versions, including the Walt compatibility profile, cannot present a transaction code. Make that weaker mode explicit:
oid4vci: pre_authorized_code: enabled: true pre_authorized_code_ttl_seconds: 300 tx_code: required: falseThe TTL must be no more than 300 seconds in this mode. An offer without a PIN is bearer credential material until redemption. A person who steals the unredeemed offer can use it during that window. Keep the offer out of logs, analytics, screenshots, browser synchronization, and support messages. The code remains single-use and redemption remains rate limited.
Metadata and Type Metadata
Section titled “Metadata and Type Metadata”Issuer metadata is derived from active Notary configuration. Wallets should assert exact values rather than infer capability from permissive schema fields:
credential_issuerequals the configured public issuer;token_endpointequals the Notary token endpoint;- every credential configuration has
format: dc+sd-jwt; cryptographic_binding_methods_supportedis exactly[did:jwk];- JWT
proof_signing_alg_values_supportedis exactly[EdDSA]; - the issuer algorithm is exactly the active profile algorithm;
vctis the configured public HTTPS identifier;- no nonce endpoint is advertised.
Notary serves Type Metadata at both the configured vct URL and the
/.well-known/vct/{vct_path} form used by wallets. It describes each projected
claim and its selective-disclosure behavior. status is a reserved top-level
claim and cannot be projected as a selectively disclosable value.
Credential request and response
Section titled “Credential request and response”The wallet sends one proof using either the supported single-proof shape or the
single-entry proof-array shape. Multiple proofs and mixed shapes are rejected.
The proof must be fresh, have the Notary issuer as audience, contain the
transaction-bound nonce from the token response, use EdDSA, and identify the
holder as did:jwk.
The response contains the immediate credential in its compatibility envelope. It does not return a new proof nonce. The wallet or verifier should check:
- issuer signature and configured issuer algorithm;
- expected
vctand credential lifetime; - exact EdDSA
did:jwkholder binding; - SD-JWT disclosure hashes;
- live status when the credential contains a status claim.
For a status-bearing credential, verification fails closed on an unavailable, untrusted, malformed, expired, suspended, revoked, or otherwise invalid status response. Status retrieval is restricted to the configured exact HTTPS trusted origin.
Security invariants
Section titled “Security invariants”- Notary creates the offer only after the identity binding and registry-backed evaluation succeed.
- The credential endpoint reloads the stored transaction and verifies the active claim, profile, purpose, contract hash, Relay ULID, acquisition time, and claim provenance before signer access.
- Wallet input cannot select a free-form subject or replace stored evidence.
- Pre-authorized codes, access tokens, proof nonces, and transaction bindings are time bounded and replay protected.
- Identity-provider codes, wallet grants, access tokens, proof JWTs, subjects, registry rows, and disclosures must not appear in logs.
Compatibility evidence
Section titled “Compatibility evidence”Record the wallet and verifier product, exact version, configuration override,
artifact digest, and observed result for every external run. Local source tests
cover EdDSA and ES256 issuer variants with an EdDSA did:jwk holder. External
wallet, verifier, OIDF, EUDI, or HAIP conformance remains candidate-only until a
frozen candidate artifact and immutable evidence are published.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Likely cause | Check |
|---|---|---|
| OID4VCI routes are unavailable | The facade or pre-authorized flow is disabled | Expanded config and startup diagnostics |
| Configuration is rejected | A source-free, delegated, mixed, or one-sided credential binding remains | Claim evidence mode, profile bindings, and OID4VCI projections |
| Offer is not rendered | Identity binding, Relay execution, or stored transaction creation failed | Sanitized audit records and Relay availability |
| Wallet asks for a different grant | Wallet does not support issuer-initiated pre-authorized code | Wallet version and imported offer |
| PIN is rejected | Wrong, expired, replayed, or locked offer | Offer age and rate-limit diagnostics |
| Wallet cannot send a PIN | Compatibility profile needs explicit bearer-offer mode | tx_code.required: false and TTL no more than 300 seconds |
| Proof is rejected | Unsupported algorithm or binding, stale proof, wrong audience, or nonce mismatch | Wallet proof header and claims |
| Credential is denied after token redemption | Stored registry transaction is missing, stale, or does not match active compiler pins | Re-run the browser journey under one project generation |
| Wallet cannot verify | Issuer JWKS, kid, algorithm, vct, holder binding, or status mismatch | Active signing profile and verifier diagnostics |
| Multiple instances disagree | In-memory correctness state is in use | Install and select Notary-owned PostgreSQL state |