Skip to content

Commit 80f42ea

Browse files
committed
fix: hold sd-jwt at 0.19.0, absorb minor-bump breaks in the deps group
The @sd-jwt 0.20.0 pair in this group enforces SD-JWT digest uniqueness and rejects the pinned AP2 / VI reference artifacts, whose L2 payloads reference the same disclosure digest from both _sd and delegate_payload. Held at 0.19.0 with a dependabot ignore until the upstream duplicate-reference question is resolved and the reference artifacts regenerate cleanly (tracked follow-up). Forward-compatible pieces land now: the synthetic VI fixtures are single-reference (the immediate fixture is regenerated from deterministic seeds instead of its unrecoverable hand-signed keys), and the crypto-conformance duplicate-digest mutation handles both payload shapes. The @google/adk and @openai/agents minor bumps moved the version facts the runtime receipt proofs pin; pins updated to 1.3.0 and 0.13.4.
1 parent 8b6f2fc commit 80f42ea

9 files changed

Lines changed: 135 additions & 148 deletions

.github/dependabot.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@ updates:
5555
interval: weekly
5656
day: monday
5757
open-pull-requests-limit: 5
58+
ignore:
59+
# Held at 0.19.0: 0.20.0 enforces SD-JWT digest uniqueness and rejects
60+
# the pinned AP2 / VI reference artifacts, whose L2 payloads carry the
61+
# same disclosure digest in both _sd and delegate_payload. Taking the
62+
# bump needs the upstream duplicate-reference question resolved and the
63+
# reference artifacts regenerated first (tracked follow-up, 2026-07-15).
64+
- dependency-name: "@sd-jwt/core"
65+
- dependency-name: "@sd-jwt/sd-jwt-vc"
5866
groups:
5967
npm-minor-patch:
6068
update-types:

packages/agent/test/fixtures/ap2/generate-vi-fixtures.mjs

Lines changed: 90 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ const keys = {
5151
agent: keyFromSeed('agt-fixture-1', 'agent'),
5252
}
5353

54+
const immediateKeys = {
55+
issuer: keyFromSeed('iss-1', 'immediate-issuer-2026-07'),
56+
user: keyFromSeed('usr-1', 'immediate-user-2026-07'),
57+
}
58+
5459
function jsonBase64url(value) {
5560
return base64url(JSON.stringify(value))
5661
}
@@ -195,7 +200,9 @@ const l2 = signJwt(
195200
exp: expiresSeconds,
196201
sd_hash: hashAscii(l1),
197202
_sd_alg: 'sha-256',
198-
_sd: [openCheckoutDisclosure.digest, openPaymentDisclosure.digest],
203+
// SD-JWT digest uniqueness: each disclosure digest appears exactly once
204+
// in the payload (enforced by @sd-jwt/core >= 0.20.0), so the mandate
205+
// digests live only in the delegate_payload array-element refs.
199206
delegate_payload: [
200207
{ '...': openCheckoutDisclosure.digest },
201208
{ '...': openPaymentDisclosure.digest },
@@ -218,7 +225,6 @@ const l3Checkout = signJwt(
218225
exp: expiresSeconds,
219226
sd_hash: hashAscii(l2Presentation),
220227
_sd_alg: 'sha-256',
221-
_sd: [closedCheckoutDisclosure.digest],
222228
delegate_payload: [{ '...': closedCheckoutDisclosure.digest }],
223229
},
224230
keys.agent,
@@ -233,7 +239,6 @@ const l3Payment = signJwt(
233239
exp: expiresSeconds,
234240
sd_hash: hashAscii(l2Presentation),
235241
_sd_alg: 'sha-256',
236-
_sd: [closedPaymentDisclosure.digest],
237242
delegate_payload: [{ '...': closedPaymentDisclosure.digest }],
238243
},
239244
keys.agent,
@@ -359,3 +364,85 @@ await writeJson('vi_autonomous_negative_matrix.json', {
359364
},
360365
],
361366
})
367+
368+
// ── vi_immediate_evidence.json ────────────────────────────────────────
369+
// Regenerated here 2026-07-15 (originally hand-signed in PR #145, keys not
370+
// recoverable). Every literal value matches the original fixture; only the
371+
// signing keys rotated to deterministic seeds and the L2 payload dropped
372+
// its duplicate `_sd` digest listing: SD-JWT digest uniqueness (enforced by
373+
// @sd-jwt/core >= 0.20.0) allows each disclosure digest exactly once, so
374+
// the mandate digests live only in the delegate_payload array-element refs.
375+
// Disclosures are byte-identical to the original, so the receipt
376+
// references and closed-mandate encodings are reproduced verbatim.
377+
378+
const immCheckoutDisclosure = disclosure('c', {
379+
vct: 'mandate.checkout.1',
380+
checkout_jwt: 'checkout.jwt.sig',
381+
checkout_hash: 'sC6pCJS01hDsD4kcqTFzpYUr-gc9f4K8k7SM6ho9BCY',
382+
})
383+
const immPaymentDisclosure = disclosure('p', {
384+
vct: 'mandate.payment.1',
385+
payment_amount: { currency: 'USD', amount: 123 },
386+
payee: { name: 'M' },
387+
transaction_id: 'sC6pCJS01hDsD4kcqTFzpYUr-gc9f4K8k7SM6ho9BCY',
388+
})
389+
390+
const immL1 = signJwt(
391+
{
392+
iss: 'iss',
393+
sub: 'u',
394+
iat: 1,
395+
exp: 9_999_999_999,
396+
vct: 'credential.provider.1',
397+
cnf: { jwk: publicJwk(immediateKeys.user) },
398+
},
399+
immediateKeys.issuer,
400+
'sd+jwt',
401+
)
402+
403+
const immL2 = signJwt(
404+
{
405+
nonce: 'n',
406+
aud: 'net',
407+
iat: 2,
408+
exp: 9_999_999_999,
409+
sd_hash: hashAscii(immL1),
410+
_sd_alg: 'sha-256',
411+
delegate_payload: [
412+
{ '...': immCheckoutDisclosure.digest },
413+
{ '...': immPaymentDisclosure.digest },
414+
],
415+
},
416+
immediateKeys.user,
417+
'kb-sd-jwt',
418+
)
419+
420+
await writeJson('vi_immediate_evidence.json', {
421+
trustedIssuerKeys: [publicJwk(immediateKeys.issuer)],
422+
ap2: {
423+
paymentReceipt: {
424+
status: 'Success',
425+
iss: 'pisp',
426+
iat: 3,
427+
reference: immPaymentDisclosure.digest,
428+
payment_id: 'pay',
429+
psp_confirmation_id: 'psp',
430+
network_confirmation_id: 'net',
431+
},
432+
checkoutReceipt: {
433+
status: 'Success',
434+
iss: 'm',
435+
iat: 3,
436+
reference: immCheckoutDisclosure.digest,
437+
order_id: 'ord',
438+
},
439+
closedPaymentMandate: immPaymentDisclosure.encoded,
440+
closedCheckoutMandate: immCheckoutDisclosure.encoded,
441+
},
442+
vi: {
443+
credentials: [
444+
{ layer: 'L1', sdJwt: immL1 },
445+
{ layer: 'L2', sdJwt: `${immL2}~${immCheckoutDisclosure.encoded}~${immPaymentDisclosure.encoded}~` },
446+
],
447+
},
448+
})

packages/agent/test/fixtures/ap2/vi_autonomous_success_evidence.json

Lines changed: 6 additions & 6 deletions
Large diffs are not rendered by default.

packages/agent/test/fixtures/ap2/vi_immediate_evidence.json

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
{
44
"kty": "EC",
55
"crv": "P-256",
6-
"x": "N-9LosuyCp_qrtzY3RY8c3cPMHa94-dQ6J3MTo9YF_4",
7-
"y": "lp3wkyC2n9heI8AYlIBSmMqcCQdhAuoOoGKX-i9srCM",
8-
"kid": "iss-1"
6+
"x": "Sybp1Sl1DZFQh3LX1GJwF1GqhAEf2eFd5UK5pqcjs00",
7+
"y": "m184NavoqZATeTYsggLZiv_slFCPwUnD1VKo8ZA6k00",
8+
"kid": "iss-1",
9+
"alg": "ES256"
910
}
1011
],
1112
"ap2": {
@@ -32,11 +33,11 @@
3233
"credentials": [
3334
{
3435
"layer": "L1",
35-
"sdJwt": "eyJhbGciOiJFUzI1NiIsInR5cCI6InNkK2p3dCIsImtpZCI6Imlzcy0xIn0.eyJpc3MiOiJpc3MiLCJzdWIiOiJ1IiwiaWF0IjoxLCJleHAiOjk5OTk5OTk5OTksInZjdCI6ImNyZWRlbnRpYWwucHJvdmlkZXIuMSIsImNuZiI6eyJqd2siOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiI1bUhzZ0NWQU1GZzlKRVJXMzFvajNVZ29yZFQtX08zMVRjWWNjT2tKRGhNIiwieSI6IjMzR2dyaDkySGk0aTJBTjAtMWJ0cFJ2cUR4ZHZ4Z3JhS0I2Yi1meGpYRHMiLCJraWQiOiJ1c3ItMSJ9fX0.ItDYjyerHl-pb1gHoO_wNalyOi11BoxQaEvZM-HbZ84jtg-B9paL_-w9ktRo2Ni94N7np-A5WBbrprcSK2hw4g"
36+
"sdJwt": "eyJhbGciOiJFUzI1NiIsInR5cCI6InNkK2p3dCIsImtpZCI6Imlzcy0xIn0.eyJpc3MiOiJpc3MiLCJzdWIiOiJ1IiwiaWF0IjoxLCJleHAiOjk5OTk5OTk5OTksInZjdCI6ImNyZWRlbnRpYWwucHJvdmlkZXIuMSIsImNuZiI6eyJqd2siOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiJWa1g3c0FSeTlac1BGVnJvTk43SjJsZzBOQ3A2TThVLTNRTURPZXZoNERvIiwieSI6IllDbllkRHluMDI1Z1RTS0pSbHgzR3JjeHB1aE1GcVduT1NDRXJaU1NONmMiLCJraWQiOiJ1c3ItMSIsImFsZyI6IkVTMjU2In19fQ.WdrwK5lCt5fhzn-FigZ-Nc_vga7Ohe-ysa7G2fSYtrSg3oSSJPiOSTkB68X-2pEDoNGMvxSNQg3sxQmZEogjBg"
3637
},
3738
{
3839
"layer": "L2",
39-
"sdJwt": "eyJhbGciOiJFUzI1NiIsInR5cCI6ImtiLXNkLWp3dCIsImtpZCI6InVzci0xIn0.eyJub25jZSI6Im4iLCJhdWQiOiJuZXQiLCJpYXQiOjIsImV4cCI6OTk5OTk5OTk5OSwic2RfaGFzaCI6Im50aTBkMHFzd29jRllSc3RMM0JidnpEVVpjT1VUdnY5aXNHN19xTmxRV1UiLCJfc2RfYWxnIjoic2hhLTI1NiIsIl9zZCI6WyJQTjFIX05BYk15eUZLUnRKT0pvTUlvQ3F6QWZaQVN6Q0EyWFlPWk1TWGxzIiwickFiSndoNDdaYnpYUjliYXlRMDJWdURlclFQeUN2Y1JUUXctVHh0NUFjdyJdLCJkZWxlZ2F0ZV9wYXlsb2FkIjpbeyIuLi4iOiJQTjFIX05BYk15eUZLUnRKT0pvTUlvQ3F6QWZaQVN6Q0EyWFlPWk1TWGxzIn0seyIuLi4iOiJyQWJKd2g0N1pielhSOWJheVEwMlZ1RGVyUVB5Q3ZjUlRRdy1UeHQ1QWN3In1dfQ.7noFvh1jojSajAHq31mKHiibFUSMXUT7feNtKDg9dUK34_sm5nR0eSiaW5itOOEbopw8kle2BGRcOr2DMlL_FQ~WyJjIix7InZjdCI6Im1hbmRhdGUuY2hlY2tvdXQuMSIsImNoZWNrb3V0X2p3dCI6ImNoZWNrb3V0Lmp3dC5zaWciLCJjaGVja291dF9oYXNoIjoic0M2cENKUzAxaERzRDRrY3FURnpwWVVyLWdjOWY0SzhrN1NNNmhvOUJDWSJ9XQ~WyJwIix7InZjdCI6Im1hbmRhdGUucGF5bWVudC4xIiwicGF5bWVudF9hbW91bnQiOnsiY3VycmVuY3kiOiJVU0QiLCJhbW91bnQiOjEyM30sInBheWVlIjp7Im5hbWUiOiJNIn0sInRyYW5zYWN0aW9uX2lkIjoic0M2cENKUzAxaERzRDRrY3FURnpwWVVyLWdjOWY0SzhrN1NNNmhvOUJDWSJ9XQ~"
40+
"sdJwt": "eyJhbGciOiJFUzI1NiIsInR5cCI6ImtiLXNkLWp3dCIsImtpZCI6InVzci0xIn0.eyJub25jZSI6Im4iLCJhdWQiOiJuZXQiLCJpYXQiOjIsImV4cCI6OTk5OTk5OTk5OSwic2RfaGFzaCI6IkxPZWtmSGsxUDlZelExOHp5WmRJemdLOEdXOE96U2lMWk16MGl6UXZNWGsiLCJfc2RfYWxnIjoic2hhLTI1NiIsImRlbGVnYXRlX3BheWxvYWQiOlt7Ii4uLiI6IlBOMUhfTkFiTXl5RktSdEpPSm9NSW9DcXpBZlpBU3pDQTJYWU9aTVNYbHMifSx7Ii4uLiI6InJBYkp3aDQ3WmJ6WFI5YmF5UTAyVnVEZXJRUHlDdmNSVFF3LVR4dDVBY3cifV19.Q_BHvE_FaM3uXqc7HXgsoSqeI7MQLlJgGNoD9mONBGq3X6J_PSrMYvGnegup6i6K6-hv0c_CBmEwlgu1dEyRLQ~WyJjIix7InZjdCI6Im1hbmRhdGUuY2hlY2tvdXQuMSIsImNoZWNrb3V0X2p3dCI6ImNoZWNrb3V0Lmp3dC5zaWciLCJjaGVja291dF9oYXNoIjoic0M2cENKUzAxaERzRDRrY3FURnpwWVVyLWdjOWY0SzhrN1NNNmhvOUJDWSJ9XQ~WyJwIix7InZjdCI6Im1hbmRhdGUucGF5bWVudC4xIiwicGF5bWVudF9hbW91bnQiOnsiY3VycmVuY3kiOiJVU0QiLCJhbW91bnQiOjEyM30sInBheWVlIjp7Im5hbWUiOiJNIn0sInRyYW5zYWN0aW9uX2lkIjoic0M2cENKUzAxaERzRDRrY3FURnpwWVVyLWdjOWY0SzhrN1NNNmhvOUJDWSJ9XQ~"
4041
}
4142
]
4243
}

packages/integration/test/google-adk-typescript-decision-ledger.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ describe('Google ADK TypeScript decision ledger proof', () => {
2323
expect(result.strategy).toBe('atrib-google-adk-typescript-decision-ledger-proof-v1')
2424
expect(result.adk).toMatchObject({
2525
package: '@google/adk',
26-
version: '1.2.0',
26+
version: '1.3.0',
2727
runner: 'InMemoryRunner',
2828
plugin: 'BasePlugin',
2929
tool: 'FunctionTool',

packages/integration/test/openai-agents-runtime-receipt.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ describe('OpenAI Agents runtime receipt example', () => {
7777
expect(result.ok).toBe(true)
7878
expect(result.openai_agents).toMatchObject({
7979
package: '@openai/agents',
80-
version: '0.11.6',
80+
version: '0.13.4',
8181
runner: 'run',
8282
agent: 'Agent',
8383
model: 'scripted',

packages/verify/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@
4343
"@atrib/mcp": "workspace:*",
4444
"@noble/ed25519": "^3.1.0",
4545
"@noble/hashes": "^2.2.0",
46-
"@sd-jwt/core": "0.20.0",
47-
"@sd-jwt/sd-jwt-vc": "0.20.0",
46+
"@sd-jwt/core": "0.19.0",
47+
"@sd-jwt/sd-jwt-vc": "0.19.0",
4848
"canonicalize": "^3.0.0",
4949
"jose": "6.2.3"
5050
},

packages/verify/test/ap2-vi-crypto-conformance.test.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,9 +278,22 @@ function applyViMutation(bundle: Ap2ViEvidenceBundle, mutation: ViMutation): voi
278278
keyForLayer(mutation.layer),
279279
(payload) => {
280280
const sd = Array.isArray(payload['_sd']) ? [...payload['_sd']] : []
281-
const digest = sd[mutation.digestIndex]
281+
const sdDigest = sd[mutation.digestIndex]
282+
if (typeof sdDigest === 'string') return { ...payload, _sd: [...sd, sdDigest] }
283+
// The base fixtures reference mandate digests only from the
284+
// delegate_payload array-element refs (SD-JWT digest uniqueness,
285+
// enforced by @sd-jwt/core >= 0.20.0), so duplicate the reference
286+
// there; the pinned vi_disclosure_digest_duplicate error is the same.
287+
const delegate = Array.isArray(payload['delegate_payload'])
288+
? [...payload['delegate_payload']]
289+
: []
290+
const entry = delegate[mutation.digestIndex]
291+
const digest =
292+
entry !== null && typeof entry === 'object'
293+
? (entry as Record<string, unknown>)['...']
294+
: undefined
282295
if (typeof digest !== 'string') throw new Error('missing digest')
283-
return { ...payload, _sd: [...sd, digest] }
296+
return { ...payload, delegate_payload: [...delegate, { '...': digest }] }
284297
},
285298
)
286299
return

0 commit comments

Comments
 (0)