Registry stack documentation: machine-readable Markdown.
Index of all pages: https://docs.registrystack.org/dev/llms.txt
Full corpus: https://docs.registrystack.org/dev/llms-full.txt

# Enable SD-JWT VC in a deployment

> Permit SD-JWT VC at both governed response-format gates, request it over HTTP, and preserve independent verifier trust.

Start with a complete governed deployment project. Evidence Gateway can serialize the same
stateless assertion as signed JWS or SD-JWT VC. The question, source access, derivation, supported
values, subject binding, purpose, audience, signing key, and audit boundaries remain the same.

For an executable introduction, complete [Explore SD-JWT VC locally](../../tutorials/request-evidence-as-sd-jwt-vc/)
first. Local question authoring uses `responseFormats` to compile both local permission gates. A
deployment instead keeps the bundle ceiling and authority grant under deployment governance.

## Permit the format at both gates

Keep signed JWS enabled and add `sd-jwt-vc` to the bundle-wide response formats:

```yaml
responseFormats: [signed-jws, sd-jwt-vc]
```

Add the same format to every exact authority grant that may return it:

```yaml
authorityProfiles:
  approved-caller:
    kind: explicit-request
    requesterTags: [approved-caller]
    grants:
      - requirement: urn:example:requirement:age-bracket:v1
        purpose: service-path-selection
        audienceFrom: authenticated-requester
        responseFormats: [signed-jws, sd-jwt-vc]
        subjects:
          - role: person
            selectorProfile: person-reference-v1
            valueOrigin: request
```

Both gates must permit the format. Enabling only one returns the ordinary authorization refusal
before source access. An unknown, combined, parameterized, or malformed `Accept` value returns
HTTP 406.

Run the candidate checks and deploy the exact reviewed revision:

```sh
evidencectl fixtures run --project "<evidence-project>"
evidence check --runtime <evidence-project>/runtime.yaml
```

## Choose root or structured disclosures

An ordinary scalar or unprojected governed value becomes one root disclosure named by its concept
URI. No extra requirement configuration is needed.

To make the direct fields of a reviewed structure independently encoded disclosures, add an
`sdJwtVc` projection to that concept:

```yaml
concepts:
  - id: urn:example:concept:birth-certificate:v1
    form: reviewed-structured-value
    required: true
    constraints:
      schema: urn:example:schema:birth-certificate:v1
      maximumSerializedBytes: 2048
    sdJwtVc:
      claim: birthCertificate
      disclosure: top-level
```

`birthCertificate` is adopter configuration, not a built-in Evidence Gateway claim. A direct
nested object remains one atomic disclosure. Review the complete and combined disclosure surfaces
before widening a production requirement.

## Request the serialization

The requester must retain its expected format before sending the request. For local integration
testing, `evidencectl request prepare` records that expectation:

```sh
evidencectl request prepare "<question>" \
  --purpose "<purpose>" \
  --subject "<role>:<field>=<value>" \
  --format sd-jwt-vc \
  --name "<request-name>"
```

Only response negotiation changes at the HTTP boundary:

```sh
curl --silent --show-error --fail-with-body \
  --config authorization.curl \
  --request POST \
  --url https://<evidence-host>/v1/evidence \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/dc+sd-jwt' \
  --data-binary @request.json \
  --output assertion.sd-jwt
```

Treat `assertion.sd-jwt` as untrusted opaque input. Do not decode its disclosures into application
state before verification.

## Verify with independent trust

Use an independently retained verification policy and trusted key set:

```sh
evidence verify \
  --sd-jwt-vc assertion.sd-jwt \
  --jwks trusted-issuer-keys.json \
  --policy verification-policy.json
```

The verifier opens no network connection. `trusted-issuer-keys.json` is the complete approved
trust set for the run, and `verification-policy.json` comes from the consumer's request and
governance records, not from the credential.

`evidence verify` reads a stored credential, which ends in a trailing tilde and offers no proof of
possession. A holder-bound presentation, which carries the holder's key-binding JWT after its last
tilde, is verified by a separate command against its own closed policy document:

```sh
evidence verify-presentation \
  --sd-jwt-vc-presentation presentation.sd-jwt \
  --jwks trusted-issuer-keys.json \
  --policy holder-bound-policy.yaml
```

Neither command accepts the other's document. Both open no network connection, and both accept
`--at <rfc3339-utc>` to verify at a historical decision instant. `evidence verify-presentation`
exits 0 when the presentation is authentic, possession is proven, and the assertion is currently
valid; 3 when it is authentic and possession is proven but the assertion is not currently valid;
and 1 for every other outcome. Comparing the expected challenge is not consuming it, so the same
presentation verifies again under the same policy.

Provider metadata is published at `/.well-known/jwt-vc-issuer`, and signing keys remain at
`/.well-known/evidence/jwks.json`. Both endpoints are discovery. Outside local mode, the service
provider identifier must be a stable HTTPS origin. It becomes the signed `iss`, and issuer
metadata publishes its exact `issuer` and `jwks_uri`.

Approve trust independently through [Manage Evidence Gateway verifier trust](../../tutorials/manage-evidence-verifier-trust/).

## Keep the boundary explicit

This response is not a credential lifecycle, in either subject binding mode:

- no OID4VCI offer, issuance session, token exchange, or wallet onboarding;
- no status list, revocation, refresh, or reissuance;
- no presentation exchange protocol, and no key-binding JWT appended at issuance;
- no replay prevention for a presented credential.

By default the subject binding remains scoped to the assertion's audience and purpose, and the
credential is usable only by the relying party it was issued to. `holderKeys` carries public P-256
JWKs, each echoed into the `cnf` claim of the credential issued for it. Their inclusion alone
proves no possession.

A requirement the bundle declares as `holder-bound` scopes its subject bindings to the presented
holder key instead, so the credential can be presented to a relying party that was not named at
issuance. That mode is a deployment declaration, never a caller option, and it narrows the
requirement to the SD-JWT VC serialization and its batch envelope.

The profile emits `application/dc+sd-jwt`, `typ: dc+sd-jwt`, `vct`, SHA-256 disclosures, optional
`cnf.jwk`, and a trailing tilde without a key-binding JWT. It is pinned to RFC 9901 and SD-JWT VC
draft v18. `evidence verify` checks the complete stored credential. A selectively disclosed
presentation carrying a holder-signed key-binding JWT is checked by `evidence verify-presentation`
instead. Neither command implements a wallet protocol, and full SD-JWT VC conformance stays
unclaimed.

For deterministic source-tree proof of the format and tamper refusals, see the
[maintained SD-JWT VC demo](https://github.com/registrystack/registry-stack/blob/main/products/evidence/SD-JWT-VC-DEMO.md).