Skip to main content
Version: v0.25.0 (Latest)

Provenance & Operations

The first four pages authored a model and drove the issuer and verifier from it. This page covers what the model gives you operationally: the usage lineage recorded along the way, how governance flows from the catalog into the wire forms, how versioning and pinning behave in practice, and the boundaries that shape the public API surface.

Usage Lineage

Every derivation writes lineage. The issuer's design-from-channel call (Issuing) wrote role = ISSUE rows; the verifier's DCQL author call (Verifying) wrote role = REQUEST rows. Each row in semantic_attribute_usage carries the full path from the consumed traversal entry to the artifact:

ColumnMeaning / values
role_refThe role from the attribute set that provided this path (e.g. employee, employer).
source_entityThe catalog entity where the traversal starts (e.g. Person, Organization).
traversal_pathThe path array that was consumed, as a serialised form. For a direct attribute this is a single-element path (e.g. ["email"]); for a relationship traversal this is the full path (e.g. ["residentialAddress","country"]).
roleISSUE (written by the issuer design-from-channel) or REQUEST (written by the DCQL author).
artifact_kindCREDENTIAL_DESIGN or DCQL_QUERY.
artifact_id, artifact_versionThe rendered artifact and the version it was captured at.
channel_id, channel_versionThe VC channel (L4) that was rendered.

The path back to the profile and catalog runs through the channel_id reference on the row, so the lineage table stays compact while remaining fully traceable to the model behind each artifact.

For a relationship traversal, the row records the full traversal_path together with source_entity and role_ref, so a query by attribute path (including traversed paths) still reaches the row. For example, the path ["residentialAddress","country"] sourced from Person via the employee role appears as its own row, distinct from the direct ["country"] path on the Address entity, so by-attribute reasoning can distinguish direct from traversed usage.

The cross-role questions it answers

Because every row carries role_ref, source_entity, traversal_path, role, and the artifact identity, the platform can reason across both roles in ways that opaque per-service stores cannot:

QuestionHow lineage answers it
Where do we issue a given traversal path?All role = ISSUE rows where traversal_path matches and source_entity matches.
Where do verifiers request a given traversal path?All role = REQUEST rows for the same path and source entity.
Which artifact and channel version consumed a path?The artifact_id/artifact_version and channel_id/channel_version on every matching row.
Which roles and source entities contribute to a given artifact?All rows for artifact_id = A, grouped by role_ref and source_entity.
Which artifacts touch a sensitive or special-category path?Join rows on the catalog attribute's overlays.classification and other governance overlays.

In the walkthrough, the Employee design and the multi-credential DCQL both touch email (via role_ref = employee, source_entity = Person, traversal_path = ["email"]). A by-path query for that row returns both the ISSUE-role design row and the REQUEST-role query row. The same applies to the traversed path ["residentialAddress","country"]: the issuer maps it to address.country in the SD-JWT and the verifier requests it in the DCQL, and both facts appear in lineage under the same traversal path. This cross-role reasoning over meaning, security classification, and provenance is the operational deliverable of the enterprise tier.

Reading lineage

The provenance read APIs (by-path, by-artifact, by-channel) are part of the VDX platform layer. The EDK writes the semantic_attribute_usage rows on every derivation but does not expose a public REST read endpoint for them.

Governance Flow-Through

The catalog attribute is canonical, and its governance metadata flows down the tiers and into the wire forms without being retyped at each layer:

Governance flow-through: the catalog attribute given_name carries its classification, sdPolicy, and other governance overlays (retention, legal basis); these flow into the issued credential (SD-JWT or mdoc) as the same attribute identity and selective-disclosure entry, and into the verifier DCQL as the same attribute identity and nested claim path.
  • Selective disclosure. The catalog attribute's sdPolicy overlay (ALWAYS, NEVER, or OPTIONAL-style) is what the VC channel's claimMappings records as the disclosure value, and what the issuer design uses to decide which claims are independently disclosable. The wallet can present given_name without revealing employment_status because the policy was set at the catalog and flowed through. The verifier requests exactly the claims the DCQL names, derived from the same channel.
  • Data classification. The overlays.classification overlay on the catalog attribute (its pii flag and specialCategory marker) travels with the attribute through every derived artifact. An operator can audit which issued credentials and which verifier requests touch personal or special-category data by joining lineage rows against the catalog's attribute metadata.
  • Governance overlays. Retention, legal basis, and jurisdiction constraints are authored once in the catalog's governance block and per-attribute governance overlays. They flow into every credential design and DCQL query derived from that catalog, rather than being re-specified on each artifact.

The point: governance is authored once at the catalog (L1) and is derived into every downstream artifact rather than retyped per artifact.

Versioning and Pinning in Practice

Each layer is versioned, and each layer pins the version of the layer below it. Resolution and lineage flow through the whole stack:

  • A catalog (L1) is versioned; publishing a revised catalog increments the version.
  • An attribute profile (L2) pins one catalog version per referenced catalog via catalogRefs (each entry carries catalogId and catalogVersion). The Acme Employee profile v1 pins catalog v1.
  • An attribute set (L3) pins the profile version it was authored against via profileRef.profileVersion.
  • A VC channel (L4) pins the set version via setRef.setVersion. The two channels in the walkthrough (Employee SD-JWT and Business Card mdoc) each pin set v1.
  • A rendered design or authored DCQL captures the VC channel id and version it derived from.

The practical effect: a VC channel authored against set v1 keeps rendering the v1 attribute shape even after the catalog and profile are revised to v2. To adopt v2, you re-author the channel against the new pin. This is what makes derivation deterministic and auditable across a changing model, and it is why issuer and verifier stay in agreement: both derive from the same pinned VC channels.

Rolling forward

Because the verifier's DCQL is persisted in the versioned DCQL store and an auth request references a query_id, you can publish a new DCQL version and repoint verifiers to it on a planned cutover, without disturbing in-flight requests. See Verifier Bindings for per-verifier version pinning and scheduled activations.

How This Differs From Hand-Authored Flows

For contrast, the naive flow hand-authors both wire forms: a literal claims[] on the design and a literal credentials[] on the DCQL, each typed independently. The enterprise model inverts this:

  • The semantic model drives the wire form; you do not type claims[] on the design or credentials[] on the DCQL.
  • Selective-disclosure policy, data classification, and governance flow from the catalog, not from hand-typed design fields.
  • Issuer and verifier agree by construction, because both derive from the same VC channels.
  • Usage lineage is recorded, so an operator can audit where any traversal path is issued or requested.

The free-form path still exists for callers that want a hand-written claim list with no model behind it; it is ungated and bypasses derivation and lineage. The layered path is the license-gated, governed default.

Operational Boundaries

Two boundaries are important when reading the walkthrough against a running deployment.

The license gate returns FORBIDDEN only when a license carries a non-empty feature set that omits semantic-attribute-binding. With no license features set, the unbounded default applies and every gated write passes. See the license gate.

Usage lineage is written by the EDK on every derivation. The public read surface for by-path, by-artifact, and by-channel queries is part of the VDX platform layer, so EDK library consumers should treat the lineage store as an internal persistence contract unless they expose their own admin surface.

The Full Sequence

Here is the complete call sequence the reader followed across the pages, in order:

  1. Operator token. The issuer's embedded authorization server issued an OIDC bearer JWT via auth-code and PKCE. The JWT carries the tenant identity; all subsequent calls pass it as the bearer credential.
  2. Catalog shell. A catalog shell was created with a name, owner party reference, and governance overlays (jurisdictions, data classification scheme, controller). Five entities were added: Person, Address, and Organization as predefined party types, then Employee as a specialization of Person and Employer as a specialization of Organization. Two relationships were added: residentialAddress from Person to Address and employment from Employee to Employer. The catalog was published and resolved.
  3. Attribute profile. A profile was created over the published catalog, binding the employee role to Person (specializing Employee) and the employer role to Organization (specializing Employer), selecting both relationships, and applying a narrow override (employee email made mandatory). The profile was published and resolved.
  4. Branding. A shared credential branding overlay was created with layout, locales, logo, and color scheme. It was published and referenced by both VC channels.
  5. Attribute set. A set was created over the published profile, selecting nine traversal paths: six direct employee attributes (given_name, family_name, employee_id, job_title, employment_status, email), two relationship traversals (residentialAddress.postal_code and residentialAddress.country), and one employer attribute (legal_name). An override narrowed employment_status to the active and suspended codes. The set was published and resolved.
  6. Two VC channels. The Employee SD-JWT channel (dc+sd-jwt, vct https://issuer.acme.example/employee) was created with eight claim mappings that map each set traversal path to a wire claim path, including the relationship traversal ["residentialAddress","country"] mapped to ["address","country"]. The Business Card mdoc channel (mso_mdoc, doctype org.acme.businesscard.1) was created with five claim mappings using namespace-qualified two-segment mdoc paths. Both channels reference the same set version and the same branding.
  7. OID4VCI issuance channel. An issuance channel was created composing both VC channels by reference (no set reference on this artifact). It carries the credentialConfigurationId and offer configuration.
  8. Two designs from channels. The issuer rendered each credential design by posting display overlays and an alias alongside a channelRef pointing at each VC channel. The claims list, selective-disclosure values, credential-type identity, and ordering were all derived. Each call wrote ISSUE-role lineage rows for every consumed traversal path.
  9. Credential offers. One offer was created per credential configuration (a wallet obtains one credential per offer), each returning its own correlation_id and offer_uri. An offer URI can be rendered as a QR code, a link, an NFC payload, or a deeplink.
  10. DCQL preview and author. The verifier first previewed a two-credential DCQL from both VC channels (ungated, persists nothing). The verifier then authored and persisted the same query with a queryId, writing REQUEST-role lineage rows.
  11. Authorization request. The verifier created an auth request referencing the authored query_id, returning a correlation_id and an openid4vp:// request_uri.
  12. Holder obtains and presents. The wallet obtained each credential from its own offer via the auth-code grant, then presented both against the two-credential DCQL in a single response (SD-JWT with KB-JWT holder binding; mdoc with DeviceAuth COSE_Sign1 over the OpenID4VP session transcript).
  13. Poll to verified. The verifier was polled until status reached authorization_response_verified. The response carried credential_claims for both credentials, with SD-JWT claims as plain keys and mdoc disclosed elements under their namespace-qualified keys.
  14. Lineage rows. Each design-from-channel render and the authored DCQL write semantic_attribute_usage rows: ISSUE-role rows from the two designs and REQUEST-role rows from the DCQL, each carrying role_ref, source_entity, and traversal_path.

See the per-page sections for the full request and response details at each step.

Next Steps