Conversation
The contract manager now has an optional vlaId field that points to a VLA (Verifiable Legal Agreement) in the DVA. To let the PDC read this field when it fetches a contract, add vlaId?: string to both response types. contract.response.ts: - Add vlaId?: string to ContractResponseType (after useDVCT). bilateral.response.ts: - Add vlaId?: string to BilateralResponseType. contractVlaId.spec.ts (4 tests): - should parse an ecosystem contract WITH vlaId (field present) - should parse an ecosystem contract WITHOUT vlaId (optional) - should parse a bilateral contract WITH vlaId - should parse a bilateral contract WITHOUT vlaId (optional) The field is optional on both sides. Existing contracts without a VLA parse correctly — vlaId is just undefined.
Wire the DVA (Data Veracity Assurance) service URL into the PDC
configuration system, following exactly the existing consentUri /
dvctUri pattern so operators configure it the same way.
config.sample.json:
- Add "dvaUri": "" and "dvaApiKey": "" next to the existing
consentUri / dvctUri block.
configuration.ts (types):
- Add dvaUri?: string and dvaApiKey?: string to the IConfiguration
TypeScript interface.
- Add dvaUri: String and dvaApiKey: String to the Mongoose schema so
the values can be overridden via DB.
configuration.ts (loaders):
- Add getDvaUri() and getDvaApiKey() accessors. Both follow the existing
getConsentUri() pattern: read from DB first, fall back to the JSON
config file, return undefined if neither is set.
- Wire both into setUpConfig() and reloadConfigurationFromFile().
- Add to the module export list.
configuration.private.router.ts:
- Add body('dvaUri').optional().isString().custom(urlValidation) and
body('dvaApiKey').optional().isString() validators on
PUT /private/configuration/, alongside the existing consentUri
validator.
- Add matching @Swagger requestBody schema entries.
- No controller or service change needed: the controller forwards
req.body verbatim, and the service's findOneAndUpdate({}, {...data})
persists any new field on the IConfiguration interface.
dataExchangeStatusEnum.ts:
- Add VERACITY_ERROR = 'VERACITY_ERROR' to the DataExchangeStatusEnum
(single-line addition next to NODE_CALLBACK_ERROR / PENDING). Used by
the provider and consumer hooks when the DVA reports failed veracity
or the JWS verification fails.
libs/third-party/dva.ts (new):
- axios-based DVA client matching the existing libs/third-party/
contract.ts / consumer.ts / provider.ts style.
- requestAttestation({dvaUri, vlaId, exchangeId, contract, data,
attesterDid, apiKey?}): POSTs to
${dvaUri}/attestation with {exchangeID, contract, vlaId, data,
attesterID}. Sends optional Authorization: Bearer <apiKey> header.
Returns AttestationResponse {requestId, issuerDidKey, jws, vcId,
evaluationPassing, evaluationResults, vcIssuedDate}.
- verifyAttestation({dvaUri, jws, attesterDid, apiKey?}): POSTs to
${dvaUri}/attestation/verify with {jws, attesterDidKey}. Returns
{verified, reason?, payload?}.
- Each function shares a buildHeaders(apiKey?) helper.
dvaClient.spec.ts (3 tests):
- requestAttestation POSTs to the DVA /attestation endpoint and returns
the JWS on success (uses axios-mock-adapter, no Express app import so
it dodges the Node 25 buffer-equal-constant-time crash).
- requestAttestation returns null JWS when evaluation fails.
- verifyAttestation POSTs to the DVA /attestation/verify endpoint and
returns {verified: true}.
Wire the PDC's data provider to call the DVA before pushing data to a
consumer. When a contract has a vlaId AND dvaUri is configured, the
PDC asks the DVA to attest the data; if the DVA says the data failed
quality checks, the push is skipped and the exchange is marked
VERACITY_ERROR.
provider.public.service.ts (ProviderExportService):
- After fetching the data and the contract, before pushing to consumer:
read vlaId from contractResp and dvaUri from getDvaUri().
- If both present, call requestAttestation() with {dvaUri, vlaId,
exchangeId, contract: contractResp (the full object, not the URL
string), data, attesterDid: PDC endpoint, apiKey: getDvaApiKey()}.
- On evaluationPassing === false: updateStatus(VERACITY_ERROR, {reason,
aov}) and continue (skip this resource's push).
- On thrown error: updateStatus(VERACITY_ERROR, {reason: 'DVA request
failed: ' + e.message}) and continue.
- On success: capture aov.jws and aov.issuerDidKey into aovJws /
aovAttesterDid locals and pass them to triggerGenericFlow().
- Hook placed in both the service-chain branch and the generic branch,
inside the per-resource loop so multi-resource exchanges check each
resource independently.
triggerGenericFlow():
- Add aovJws? and aovAttesterDid? to the props interface.
- Forward them to consumerImport() (new last two params).
Critical design: when vlaId is absent OR dvaUri is empty, the hook is a
no-op. The existing flow runs exactly as before, so deployments that
don't use the DVA are unaffected.
Thread the JWS attestation from the provider through to the consumer so
the consumer's PDC can verify it on receipt. This closes the end-to-end
veracity loop: provider attests → consumer verifies.
libs/third-party/consumer.ts:
- Add two optional params to consumerImport(): aovJws? and
aovAttesterDid?. When present, they're added as x-ptx-aov-jws and
x-ptx-aov-attester-did HTTP headers on the axios POST to the
consumer's /consumer/import endpoint (both the JSON and non-JSON
branches). When absent, headers are not set — zero impact on existing
callers.
controllers/public/v1/consumer.public.controller.ts:
- Pass headers: req.headers into consumerImportService({...}) so the
service can read x-ptx-aov-jws / x-ptx-aov-attester-did.
services/public/v1/consumer.public.service.ts (consumerImportService):
- Accept a new optional headers param.
- If x-ptx-aov-jws is present (case-insensitive):
- If getDvaUri() is empty: Logger.warn('DVA not configured for
verification; skipping AoV check') and continue (best-effort).
- Otherwise call verifyAttestation({dvaUri, jws, attesterDid,
apiKey: getDvaApiKey()}).
- If verified === false: updateStatus(VERACITY_ERROR, {reason}) and
return (block the exchange).
- If the request throws: updateStatus(VERACITY_ERROR, {reason:
'DVA verification request failed: ' + e.message}) and return.
- If verified === true: continue normally.
- If no JWS present: continue normally (the provider didn't attest,
which is fine for contracts without a VLA).
Design: consumer verification is best-effort — if the consumer hasn't
configured a DVA, it logs a warning and continues instead of blocking.
This is because not every participant will run a DVA, and blocking
data flows would be too aggressive for an opt-in feature.
If the dvaUri configuration value ends with a slash (e.g. 'http://dva/'), the generated URL becomes 'http://dva//attestation' (double slash). Many HTTP servers and proxies treat this as a different path and return 404, causing silent attestation failures with no obvious cause in the logs. Applied a .replace(/\/+$/, '') to dvaUri in both requestAttestation() and verifyAttestation() before string-concatenating the path segments.
…romFile() dvaApiKey was omitted from two configuration functions: 1. setUpConfig(): run once on first startup to persist configuration to the database. Because dvaApiKey was not included, getDvaApiKey() would always fall back to reading from config.json at runtime instead of from the DB. This breaks in container deployments where config.json is not mounted after startup, causing all DVA attestation requests to be sent without auth. 2. reloadConfigurationFromFile(): triggered by PUT /config/reload. Because dvaApiKey was not in the update payload, a config reload would effectively clear any previously stored key from the database, causing unauthenticated DVA calls silently from that point on. Both functions now include dvaApiKey from the config file / DB getter.
…ranch
In ProviderExportService the service-chain branch called requestAttestation()
to obtain a signed AoV token, but immediately discarded aov.jws and
aov.issuerDidKey. The JWS was computed, signed with the DVA server's Ed25519
key, and then silently dropped — the consumer node in a service-chain exchange
had no way to verify data integrity.
Changes:
- Capture aovJwsChain / aovAttesterDidChain when attestation succeeds.
- Persist them on the exchange record via updateStatus(VERACITY_ATTESTED, {…})
so downstream infrastructure nodes and the consumer can retrieve them.
- Add VERACITY_ATTESTED to DataExchangeStatusEnum (previously only VERACITY_ERROR
existed, so a successful attestation had no typed status value).
The triggerInfrastructureFlowService() signature is unchanged — adding AoV
to signedConsent/encrypted would have been semantically wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Cleans up the dataspace-connector attestation branch to match the slimmed dva-api contract:
vcId,vcIssuedDate,attesterDidKeyandpayloadfields, which are all derivable from the JWS)decodeJwsIssuer(jws)to extract the issuerdid:keyfrom the JWS payload so downstreamattesterDidcan be derived without a separate headerattesterDidfrom theverifyAttestationcall site inconsumer.public.service.ts(the field is no longer part ofVerifyAttestationRequest)aovAttesterDidheader read in the consumer import branchdecodeJwsIssuerunit test fixture (z6Mkk->z6Mkto match the base64url-decoded payload)pnpm run buildclean;npx ts-mocha src/tests/api/dvaClient.spec.ts-> 5 passingLockstep with Prometheus-X-association/data-veracity#76 (synchronous attestation endpoints).
Depends on: none (PDC-side)
@bzp99