Use Case
An operator issuing SD-JWT VC credentials from Registry Notary. A credential's holder binding is governed by the credential profile's holder_binding.mode, and that field defaults to "none" (unbound):
HolderBindingConfig::default sets mode from default_holder_binding_mode(), which returns "none" (crates/registry-notary-core/src/config.rs).
- Issuance only attaches a holder confirmation (
cnf) when mode != "none" (crates/registry-notary-core/src/sd_jwt.rs); the test credential_without_holder_uses_registry_subject_ref confirms an unbound credential is issued (registry subject_ref as sub) when no holder is supplied.
- Binding is forced only on the self-attestation / OID4VCI (wallet) path —
validate_self_attestation_profile requires mode == "did", proof_of_possession == "required", and allowed_did_methods = ["did:jwk"].
An unbound SD-JWT VC is effectively bearer-style: anyone who comes into possession of it can present it. So the out-of-the-box default for a credential-issuing profile is the less secure posture, and an operator has to know to opt in to binding.
This also diverges from the spec: RS-PR-NOTARY (REQ-PR-NOTARY-013 / -015) states an issued credential MUST bind its holder via did:jwk. So the default profile can issue credentials that don't meet the spec's binding requirement.
Proposed Behavior
Make holder binding the default (secure-by-default), so an operator must consciously opt out rather than opt in:
- Change
default_holder_binding_mode() to "did" (with did:jwk as the default allowed_did_methods), or
- If a profile legitimately needs to issue unbound credentials, keep
"none" available but require it to be set explicitly and surface it loudly (e.g. a registryctl doctor warning when a credential-issuing profile runs unbound).
Either way, the bare/default issuance path should produce a holder-bound credential, matching the spec's MUST.
Boundaries
- The wallet / self-attestation (OID4VCI) path already binds correctly — this is specifically about the default credential profile and any backend/direct issuance that leaves
holder_binding at its default.
- Flipping the default is a behaviour change for any existing deployment relying on unbound issuance, so it needs a migration note and a clear way to keep the old behaviour deliberately.
did:jwk is currently the only supported binding method, so binding-by-default does not need new method support.
Surfaced while reviewing the Stage-1 trust/credential documentation against the implementation: the docs had described credentials as holder-bound, but the default issuance path is unbound. Filed per the secure-by-default principle.
Use Case
An operator issuing SD-JWT VC credentials from Registry Notary. A credential's holder binding is governed by the credential profile's
holder_binding.mode, and that field defaults to"none"(unbound):HolderBindingConfig::defaultsetsmodefromdefault_holder_binding_mode(), which returns"none"(crates/registry-notary-core/src/config.rs).cnf) whenmode != "none"(crates/registry-notary-core/src/sd_jwt.rs); the testcredential_without_holder_uses_registry_subject_refconfirms an unbound credential is issued (registrysubject_refassub) when no holder is supplied.validate_self_attestation_profilerequiresmode == "did",proof_of_possession == "required", andallowed_did_methods=["did:jwk"].An unbound SD-JWT VC is effectively bearer-style: anyone who comes into possession of it can present it. So the out-of-the-box default for a credential-issuing profile is the less secure posture, and an operator has to know to opt in to binding.
This also diverges from the spec: RS-PR-NOTARY (REQ-PR-NOTARY-013 / -015) states an issued credential MUST bind its holder via
did:jwk. So the default profile can issue credentials that don't meet the spec's binding requirement.Proposed Behavior
Make holder binding the default (secure-by-default), so an operator must consciously opt out rather than opt in:
default_holder_binding_mode()to"did"(withdid:jwkas the defaultallowed_did_methods), or"none"available but require it to be set explicitly and surface it loudly (e.g. aregistryctl doctorwarning when a credential-issuing profile runs unbound).Either way, the bare/default issuance path should produce a holder-bound credential, matching the spec's
MUST.Boundaries
holder_bindingat its default.did:jwkis currently the only supported binding method, so binding-by-default does not need new method support.Surfaced while reviewing the Stage-1 trust/credential documentation against the implementation: the docs had described credentials as holder-bound, but the default issuance path is unbound. Filed per the secure-by-default principle.