@openvtc/trust-tasks 0.12.6 → 0.12.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/vtc/endorsement-types/list/0.1/payload.d.ts +20 -0
  2. package/dist/vtc/endorsement-types/list/0.1/payload.d.ts.map +1 -1
  3. package/dist/vtc/endorsement-types/list/0.1/payload.js +8 -0
  4. package/dist/vtc/endorsement-types/list/0.1/payload.js.map +1 -1
  5. package/dist/vtc/endorsement-types/register/0.1/payload.d.ts +20 -0
  6. package/dist/vtc/endorsement-types/register/0.1/payload.d.ts.map +1 -1
  7. package/dist/vtc/endorsement-types/register/0.1/payload.js +8 -0
  8. package/dist/vtc/endorsement-types/register/0.1/payload.js.map +1 -1
  9. package/dist/vtc/join-requests/decide/0.1/payload.d.ts +42 -0
  10. package/dist/vtc/join-requests/decide/0.1/payload.d.ts.map +1 -1
  11. package/dist/vtc/join-requests/decide/0.1/payload.js +28 -0
  12. package/dist/vtc/join-requests/decide/0.1/payload.js.map +1 -1
  13. package/dist/vtc/members/list/0.1/payload.d.ts +108 -0
  14. package/dist/vtc/members/list/0.1/payload.d.ts.map +1 -1
  15. package/dist/vtc/members/list/0.1/payload.js +62 -0
  16. package/dist/vtc/members/list/0.1/payload.js.map +1 -1
  17. package/dist/vtc/members/show/0.1/payload.d.ts +108 -0
  18. package/dist/vtc/members/show/0.1/payload.d.ts.map +1 -1
  19. package/dist/vtc/members/show/0.1/payload.js +62 -0
  20. package/dist/vtc/members/show/0.1/payload.js.map +1 -1
  21. package/dist/vtc/members/update/0.1/payload.d.ts +120 -0
  22. package/dist/vtc/members/update/0.1/payload.d.ts.map +1 -1
  23. package/dist/vtc/members/update/0.1/payload.js +69 -0
  24. package/dist/vtc/members/update/0.1/payload.js.map +1 -1
  25. package/dist/vtc/relationships/publish/0.1/payload.d.ts +106 -0
  26. package/dist/vtc/relationships/publish/0.1/payload.d.ts.map +1 -1
  27. package/dist/vtc/relationships/publish/0.1/payload.js +49 -0
  28. package/dist/vtc/relationships/publish/0.1/payload.js.map +1 -1
  29. package/dist/vtc/relationships/publish/0.2/payload.d.ts +76 -2
  30. package/dist/vtc/relationships/publish/0.2/payload.d.ts.map +1 -1
  31. package/dist/vtc/relationships/publish/0.2/payload.js +29 -0
  32. package/dist/vtc/relationships/publish/0.2/payload.js.map +1 -1
  33. package/package.json +1 -1
  34. package/src/vtc/endorsement-types/list/0.1/payload.ts +12 -0
  35. package/src/vtc/endorsement-types/register/0.1/payload.ts +12 -0
  36. package/src/vtc/join-requests/decide/0.1/payload.ts +38 -0
  37. package/src/vtc/members/list/0.1/payload.ts +82 -0
  38. package/src/vtc/members/show/0.1/payload.ts +82 -0
  39. package/src/vtc/members/update/0.1/payload.ts +93 -0
  40. package/src/vtc/relationships/publish/0.1/payload.ts +76 -0
  41. package/src/vtc/relationships/publish/0.2/payload.ts +57 -2
@@ -73,6 +73,26 @@ export interface MemberResponse {
73
73
  * Id of the member's current role Verifiable Endorsement Credential, if issued.
74
74
  */
75
75
  currentRoleVecId?: string | null;
76
+ /**
77
+ * Whether the community has asserted that this member is a distinct real person. Read-only here: it is set and cleared by the personhood verbs, and cleared by a renewal-policy downgrade. Load-bearing rather than informational — a community that recognises members of another community may gate on it, so a consumer that cannot read it cannot make that decision.
78
+ */
79
+ personhood?: boolean;
80
+ /**
81
+ * When personhood was most recently asserted. Absent where it never has been. Operator-facing: it belongs on admin-gated reads, and a member's own view of themselves need carry only the flag.
82
+ */
83
+ personhoodAssertedAt?: string | null;
84
+ /**
85
+ * Whether this member joined by presenting an invitation credential rather than by an admin's decision on an open request. The two routes to membership carry different evidence, and an operator reviewing a roster can otherwise not tell them apart.
86
+ */
87
+ joinedViaInvitation?: boolean;
88
+ /**
89
+ * `id` of the member-issued reciprocal membership credential — the member half of the pair — once the member has sent it. Absent until then, which is the useful signal: it distinguishes a membership the community has asserted from one the member has acknowledged. The credential body is not echoed here.
90
+ */
91
+ memberVmcId?: string | null;
92
+ /**
93
+ * When that reciprocal credential was received. Paired with `memberVmcId`.
94
+ */
95
+ memberVmcReceivedAt?: string | null;
76
96
  /**
77
97
  * Opaque community-defined extension bag. Keys are maintainer-defined; the maintainer caps its size (16 KiB in the reference implementation).
78
98
  */
@@ -242,6 +262,37 @@ export const PAYLOAD_SCHEMA = {
242
262
  ],
243
263
  "description": "Id of the member's current role Verifiable Endorsement Credential, if issued."
244
264
  },
265
+ "personhood": {
266
+ "type": "boolean",
267
+ "description": "Whether the community has asserted that this member is a distinct real person. Read-only here: it is set and cleared by the personhood verbs, and cleared by a renewal-policy downgrade. Load-bearing rather than informational — a community that recognises members of another community may gate on it, so a consumer that cannot read it cannot make that decision."
268
+ },
269
+ "personhoodAssertedAt": {
270
+ "type": [
271
+ "string",
272
+ "null"
273
+ ],
274
+ "format": "date-time",
275
+ "description": "When personhood was most recently asserted. Absent where it never has been. Operator-facing: it belongs on admin-gated reads, and a member's own view of themselves need carry only the flag."
276
+ },
277
+ "joinedViaInvitation": {
278
+ "type": "boolean",
279
+ "description": "Whether this member joined by presenting an invitation credential rather than by an admin's decision on an open request. The two routes to membership carry different evidence, and an operator reviewing a roster can otherwise not tell them apart."
280
+ },
281
+ "memberVmcId": {
282
+ "type": [
283
+ "string",
284
+ "null"
285
+ ],
286
+ "description": "`id` of the member-issued reciprocal membership credential — the member half of the pair — once the member has sent it. Absent until then, which is the useful signal: it distinguishes a membership the community has asserted from one the member has acknowledged. The credential body is not echoed here."
287
+ },
288
+ "memberVmcReceivedAt": {
289
+ "type": [
290
+ "string",
291
+ "null"
292
+ ],
293
+ "format": "date-time",
294
+ "description": "When that reciprocal credential was received. Paired with `memberVmcId`."
295
+ },
245
296
  "extensions": {
246
297
  "type": "object",
247
298
  "description": "Opaque community-defined extension bag. Keys are maintainer-defined; the maintainer caps its size (16 KiB in the reference implementation)."
@@ -371,6 +422,37 @@ export const RESPONSE_PAYLOAD_SCHEMA = {
371
422
  ],
372
423
  "description": "Id of the member's current role Verifiable Endorsement Credential, if issued."
373
424
  },
425
+ "personhood": {
426
+ "type": "boolean",
427
+ "description": "Whether the community has asserted that this member is a distinct real person. Read-only here: it is set and cleared by the personhood verbs, and cleared by a renewal-policy downgrade. Load-bearing rather than informational — a community that recognises members of another community may gate on it, so a consumer that cannot read it cannot make that decision."
428
+ },
429
+ "personhoodAssertedAt": {
430
+ "type": [
431
+ "string",
432
+ "null"
433
+ ],
434
+ "format": "date-time",
435
+ "description": "When personhood was most recently asserted. Absent where it never has been. Operator-facing: it belongs on admin-gated reads, and a member's own view of themselves need carry only the flag."
436
+ },
437
+ "joinedViaInvitation": {
438
+ "type": "boolean",
439
+ "description": "Whether this member joined by presenting an invitation credential rather than by an admin's decision on an open request. The two routes to membership carry different evidence, and an operator reviewing a roster can otherwise not tell them apart."
440
+ },
441
+ "memberVmcId": {
442
+ "type": [
443
+ "string",
444
+ "null"
445
+ ],
446
+ "description": "`id` of the member-issued reciprocal membership credential — the member half of the pair — once the member has sent it. Absent until then, which is the useful signal: it distinguishes a membership the community has asserted from one the member has acknowledged. The credential body is not echoed here."
447
+ },
448
+ "memberVmcReceivedAt": {
449
+ "type": [
450
+ "string",
451
+ "null"
452
+ ],
453
+ "format": "date-time",
454
+ "description": "When that reciprocal credential was received. Paired with `memberVmcId`."
455
+ },
374
456
  "extensions": {
375
457
  "type": "object",
376
458
  "description": "Opaque community-defined extension bag. Keys are maintainer-defined; the maintainer caps its size (16 KiB in the reference implementation)."
@@ -57,6 +57,26 @@ export interface MemberResponse {
57
57
  * Id of the member's current role Verifiable Endorsement Credential, if issued.
58
58
  */
59
59
  currentRoleVecId?: string | null;
60
+ /**
61
+ * Whether the community has asserted that this member is a distinct real person. Read-only here: it is set and cleared by the personhood verbs, and cleared by a renewal-policy downgrade. Load-bearing rather than informational — a community that recognises members of another community may gate on it, so a consumer that cannot read it cannot make that decision.
62
+ */
63
+ personhood?: boolean;
64
+ /**
65
+ * When personhood was most recently asserted. Absent where it never has been. Operator-facing: it belongs on admin-gated reads, and a member's own view of themselves need carry only the flag.
66
+ */
67
+ personhoodAssertedAt?: string | null;
68
+ /**
69
+ * Whether this member joined by presenting an invitation credential rather than by an admin's decision on an open request. The two routes to membership carry different evidence, and an operator reviewing a roster can otherwise not tell them apart.
70
+ */
71
+ joinedViaInvitation?: boolean;
72
+ /**
73
+ * `id` of the member-issued reciprocal membership credential — the member half of the pair — once the member has sent it. Absent until then, which is the useful signal: it distinguishes a membership the community has asserted from one the member has acknowledged. The credential body is not echoed here.
74
+ */
75
+ memberVmcId?: string | null;
76
+ /**
77
+ * When that reciprocal credential was received. Paired with `memberVmcId`.
78
+ */
79
+ memberVmcReceivedAt?: string | null;
60
80
  /**
61
81
  * Opaque community-defined extension bag. Keys are maintainer-defined; the maintainer caps its size (16 KiB in the reference implementation).
62
82
  */
@@ -202,6 +222,37 @@ export const PAYLOAD_SCHEMA = {
202
222
  ],
203
223
  "description": "Id of the member's current role Verifiable Endorsement Credential, if issued."
204
224
  },
225
+ "personhood": {
226
+ "type": "boolean",
227
+ "description": "Whether the community has asserted that this member is a distinct real person. Read-only here: it is set and cleared by the personhood verbs, and cleared by a renewal-policy downgrade. Load-bearing rather than informational — a community that recognises members of another community may gate on it, so a consumer that cannot read it cannot make that decision."
228
+ },
229
+ "personhoodAssertedAt": {
230
+ "type": [
231
+ "string",
232
+ "null"
233
+ ],
234
+ "format": "date-time",
235
+ "description": "When personhood was most recently asserted. Absent where it never has been. Operator-facing: it belongs on admin-gated reads, and a member's own view of themselves need carry only the flag."
236
+ },
237
+ "joinedViaInvitation": {
238
+ "type": "boolean",
239
+ "description": "Whether this member joined by presenting an invitation credential rather than by an admin's decision on an open request. The two routes to membership carry different evidence, and an operator reviewing a roster can otherwise not tell them apart."
240
+ },
241
+ "memberVmcId": {
242
+ "type": [
243
+ "string",
244
+ "null"
245
+ ],
246
+ "description": "`id` of the member-issued reciprocal membership credential — the member half of the pair — once the member has sent it. Absent until then, which is the useful signal: it distinguishes a membership the community has asserted from one the member has acknowledged. The credential body is not echoed here."
247
+ },
248
+ "memberVmcReceivedAt": {
249
+ "type": [
250
+ "string",
251
+ "null"
252
+ ],
253
+ "format": "date-time",
254
+ "description": "When that reciprocal credential was received. Paired with `memberVmcId`."
255
+ },
205
256
  "extensions": {
206
257
  "type": "object",
207
258
  "description": "Opaque community-defined extension bag. Keys are maintainer-defined; the maintainer caps its size (16 KiB in the reference implementation)."
@@ -314,6 +365,37 @@ export const RESPONSE_PAYLOAD_SCHEMA = {
314
365
  ],
315
366
  "description": "Id of the member's current role Verifiable Endorsement Credential, if issued."
316
367
  },
368
+ "personhood": {
369
+ "type": "boolean",
370
+ "description": "Whether the community has asserted that this member is a distinct real person. Read-only here: it is set and cleared by the personhood verbs, and cleared by a renewal-policy downgrade. Load-bearing rather than informational — a community that recognises members of another community may gate on it, so a consumer that cannot read it cannot make that decision."
371
+ },
372
+ "personhoodAssertedAt": {
373
+ "type": [
374
+ "string",
375
+ "null"
376
+ ],
377
+ "format": "date-time",
378
+ "description": "When personhood was most recently asserted. Absent where it never has been. Operator-facing: it belongs on admin-gated reads, and a member's own view of themselves need carry only the flag."
379
+ },
380
+ "joinedViaInvitation": {
381
+ "type": "boolean",
382
+ "description": "Whether this member joined by presenting an invitation credential rather than by an admin's decision on an open request. The two routes to membership carry different evidence, and an operator reviewing a roster can otherwise not tell them apart."
383
+ },
384
+ "memberVmcId": {
385
+ "type": [
386
+ "string",
387
+ "null"
388
+ ],
389
+ "description": "`id` of the member-issued reciprocal membership credential — the member half of the pair — once the member has sent it. Absent until then, which is the useful signal: it distinguishes a membership the community has asserted from one the member has acknowledged. The credential body is not echoed here."
390
+ },
391
+ "memberVmcReceivedAt": {
392
+ "type": [
393
+ "string",
394
+ "null"
395
+ ],
396
+ "format": "date-time",
397
+ "description": "When that reciprocal credential was received. Paired with `memberVmcId`."
398
+ },
317
399
  "extensions": {
318
400
  "type": "object",
319
401
  "description": "Opaque community-defined extension bag. Keys are maintainer-defined; the maintainer caps its size (16 KiB in the reference implementation)."
@@ -12,6 +12,10 @@ export interface VTCMembersUpdatePayload {
12
12
  * New role (wire form). MUST NOT be `admin` — promotion to admin is a separate, gated flow, not a metadata patch.
13
13
  */
14
14
  role?: string;
15
+ /**
16
+ * Operator-facing display label for this member. Editable, unlike the DID it labels — which is the point: an operator needs a name they can read on a roster of identifiers they cannot. `null` clears it; omit to leave it unchanged.
17
+ */
18
+ label?: string | null;
15
19
  /**
16
20
  * New directory-publish consent.
17
21
  */
@@ -70,6 +74,26 @@ export interface MemberResponse {
70
74
  * Id of the member's current role Verifiable Endorsement Credential, if issued.
71
75
  */
72
76
  currentRoleVecId?: string | null;
77
+ /**
78
+ * Whether the community has asserted that this member is a distinct real person. Read-only here: it is set and cleared by the personhood verbs, and cleared by a renewal-policy downgrade. Load-bearing rather than informational — a community that recognises members of another community may gate on it, so a consumer that cannot read it cannot make that decision.
79
+ */
80
+ personhood?: boolean;
81
+ /**
82
+ * When personhood was most recently asserted. Absent where it never has been. Operator-facing: it belongs on admin-gated reads, and a member's own view of themselves need carry only the flag.
83
+ */
84
+ personhoodAssertedAt?: string | null;
85
+ /**
86
+ * Whether this member joined by presenting an invitation credential rather than by an admin's decision on an open request. The two routes to membership carry different evidence, and an operator reviewing a roster can otherwise not tell them apart.
87
+ */
88
+ joinedViaInvitation?: boolean;
89
+ /**
90
+ * `id` of the member-issued reciprocal membership credential — the member half of the pair — once the member has sent it. Absent until then, which is the useful signal: it distinguishes a membership the community has asserted from one the member has acknowledged. The credential body is not echoed here.
91
+ */
92
+ memberVmcId?: string | null;
93
+ /**
94
+ * When that reciprocal credential was received. Paired with `memberVmcId`.
95
+ */
96
+ memberVmcReceivedAt?: string | null;
73
97
  /**
74
98
  * Opaque community-defined extension bag. Keys are maintainer-defined; the maintainer caps its size (16 KiB in the reference implementation).
75
99
  */
@@ -117,6 +141,13 @@ export const PAYLOAD_SCHEMA = {
117
141
  "minLength": 1,
118
142
  "description": "New role (wire form). MUST NOT be `admin` — promotion to admin is a separate, gated flow, not a metadata patch."
119
143
  },
144
+ "label": {
145
+ "type": [
146
+ "string",
147
+ "null"
148
+ ],
149
+ "description": "Operator-facing display label for this member. Editable, unlike the DID it labels — which is the point: an operator needs a name they can read on a roster of identifiers they cannot. `null` clears it; omit to leave it unchanged."
150
+ },
120
151
  "publishConsent": {
121
152
  "type": "boolean",
122
153
  "description": "New directory-publish consent."
@@ -237,6 +268,37 @@ export const PAYLOAD_SCHEMA = {
237
268
  ],
238
269
  "description": "Id of the member's current role Verifiable Endorsement Credential, if issued."
239
270
  },
271
+ "personhood": {
272
+ "type": "boolean",
273
+ "description": "Whether the community has asserted that this member is a distinct real person. Read-only here: it is set and cleared by the personhood verbs, and cleared by a renewal-policy downgrade. Load-bearing rather than informational — a community that recognises members of another community may gate on it, so a consumer that cannot read it cannot make that decision."
274
+ },
275
+ "personhoodAssertedAt": {
276
+ "type": [
277
+ "string",
278
+ "null"
279
+ ],
280
+ "format": "date-time",
281
+ "description": "When personhood was most recently asserted. Absent where it never has been. Operator-facing: it belongs on admin-gated reads, and a member's own view of themselves need carry only the flag."
282
+ },
283
+ "joinedViaInvitation": {
284
+ "type": "boolean",
285
+ "description": "Whether this member joined by presenting an invitation credential rather than by an admin's decision on an open request. The two routes to membership carry different evidence, and an operator reviewing a roster can otherwise not tell them apart."
286
+ },
287
+ "memberVmcId": {
288
+ "type": [
289
+ "string",
290
+ "null"
291
+ ],
292
+ "description": "`id` of the member-issued reciprocal membership credential — the member half of the pair — once the member has sent it. Absent until then, which is the useful signal: it distinguishes a membership the community has asserted from one the member has acknowledged. The credential body is not echoed here."
293
+ },
294
+ "memberVmcReceivedAt": {
295
+ "type": [
296
+ "string",
297
+ "null"
298
+ ],
299
+ "format": "date-time",
300
+ "description": "When that reciprocal credential was received. Paired with `memberVmcId`."
301
+ },
240
302
  "extensions": {
241
303
  "type": "object",
242
304
  "description": "Opaque community-defined extension bag. Keys are maintainer-defined; the maintainer caps its size (16 KiB in the reference implementation)."
@@ -349,6 +411,37 @@ export const RESPONSE_PAYLOAD_SCHEMA = {
349
411
  ],
350
412
  "description": "Id of the member's current role Verifiable Endorsement Credential, if issued."
351
413
  },
414
+ "personhood": {
415
+ "type": "boolean",
416
+ "description": "Whether the community has asserted that this member is a distinct real person. Read-only here: it is set and cleared by the personhood verbs, and cleared by a renewal-policy downgrade. Load-bearing rather than informational — a community that recognises members of another community may gate on it, so a consumer that cannot read it cannot make that decision."
417
+ },
418
+ "personhoodAssertedAt": {
419
+ "type": [
420
+ "string",
421
+ "null"
422
+ ],
423
+ "format": "date-time",
424
+ "description": "When personhood was most recently asserted. Absent where it never has been. Operator-facing: it belongs on admin-gated reads, and a member's own view of themselves need carry only the flag."
425
+ },
426
+ "joinedViaInvitation": {
427
+ "type": "boolean",
428
+ "description": "Whether this member joined by presenting an invitation credential rather than by an admin's decision on an open request. The two routes to membership carry different evidence, and an operator reviewing a roster can otherwise not tell them apart."
429
+ },
430
+ "memberVmcId": {
431
+ "type": [
432
+ "string",
433
+ "null"
434
+ ],
435
+ "description": "`id` of the member-issued reciprocal membership credential — the member half of the pair — once the member has sent it. Absent until then, which is the useful signal: it distinguishes a membership the community has asserted from one the member has acknowledged. The credential body is not echoed here."
436
+ },
437
+ "memberVmcReceivedAt": {
438
+ "type": [
439
+ "string",
440
+ "null"
441
+ ],
442
+ "format": "date-time",
443
+ "description": "When that reciprocal credential was received. Paired with `memberVmcId`."
444
+ },
352
445
  "extensions": {
353
446
  "type": "object",
354
447
  "description": "Opaque community-defined extension bag. Keys are maintainer-defined; the maintainer caps its size (16 KiB in the reference implementation)."
@@ -3,11 +3,38 @@
3
3
  * Source: specs/vtc/relationships/publish/0.1/payload.schema.json
4
4
  */
5
5
 
6
+ /**
7
+ * Digest over the RFC 8785 canonicalization of the VRC this authorizes. Binds the authorization to one credential, so it cannot be moved to another.
8
+ */
9
+ export type DigestMultibase = string;
10
+
6
11
  export interface VTCRelationshipsPublishPayload {
7
12
  /**
8
13
  * A signed W3C Verifiable Relationship Credential (opaque here). Its issuer MUST equal the document proof signer; credentialSubject.id names the subject member; it carries its own DataIntegrityProof.
9
14
  */
10
15
  vrc: {};
16
+ /**
17
+ * Proof that the caller controls the key behind the VRC's `issuer`, when that is not the party issuing this document.
18
+ *
19
+ * A member publishes an edge under a relationship DID — an identifier scoped to one counterparty, which names nobody. The community still has to know a member published it, and the document's own `proof` says only that the member sent it, not that they control the credential's issuing key. Without this, any member handed a VRC could publish another party's edge.
20
+ *
21
+ * Omit it when the VRC's `issuer` is this document's issuer: the document's own proof already establishes control.
22
+ */
23
+ pop?: {
24
+ /**
25
+ * Guards against a signature the issuer made over some other object being replayed here as authorization to publish.
26
+ */
27
+ type: "VrcPublishAuthorization";
28
+ /**
29
+ * The `id` of the Trust Task document carrying this authorization. Binds the authorization to one document, so a captured one cannot be replayed in another — including by a different member. Every document carries a unique `id` (SPEC §4.3), which is why the binding needs no transport-specific notion of a session.
30
+ */
31
+ documentId: string;
32
+ vrcDigestMultibase: DigestMultibase;
33
+ /**
34
+ * Data-integrity proof over this object, by a verification method the VRC's `issuer` controls. This is the proof of possession; the members above only bound what it authorizes.
35
+ */
36
+ proof: {};
37
+ };
11
38
  ext?: Ext;
12
39
  }
13
40
  /**
@@ -65,6 +92,35 @@ export const PAYLOAD_SCHEMA = {
65
92
  "type": "object",
66
93
  "description": "A signed W3C Verifiable Relationship Credential (opaque here). Its issuer MUST equal the document proof signer; credentialSubject.id names the subject member; it carries its own DataIntegrityProof."
67
94
  },
95
+ "pop": {
96
+ "type": "object",
97
+ "description": "Proof that the caller controls the key behind the VRC's `issuer`, when that is not the party issuing this document.\n\nA member publishes an edge under a relationship DID — an identifier scoped to one counterparty, which names nobody. The community still has to know a member published it, and the document's own `proof` says only that the member sent it, not that they control the credential's issuing key. Without this, any member handed a VRC could publish another party's edge.\n\nOmit it when the VRC's `issuer` is this document's issuer: the document's own proof already establishes control.",
98
+ "properties": {
99
+ "type": {
100
+ "const": "VrcPublishAuthorization",
101
+ "description": "Guards against a signature the issuer made over some other object being replayed here as authorization to publish."
102
+ },
103
+ "documentId": {
104
+ "type": "string",
105
+ "description": "The `id` of the Trust Task document carrying this authorization. Binds the authorization to one document, so a captured one cannot be replayed in another — including by a different member. Every document carries a unique `id` (SPEC §4.3), which is why the binding needs no transport-specific notion of a session."
106
+ },
107
+ "vrcDigestMultibase": {
108
+ "$ref": "#/$defs/DigestMultibase",
109
+ "description": "Digest over the RFC 8785 canonicalization of the VRC this authorizes. Binds the authorization to one credential, so it cannot be moved to another."
110
+ },
111
+ "proof": {
112
+ "type": "object",
113
+ "description": "Data-integrity proof over this object, by a verification method the VRC's `issuer` controls. This is the proof of possession; the members above only bound what it authorizes."
114
+ }
115
+ },
116
+ "required": [
117
+ "type",
118
+ "documentId",
119
+ "vrcDigestMultibase",
120
+ "proof"
121
+ ],
122
+ "additionalProperties": false
123
+ },
68
124
  "ext": {
69
125
  "$ref": "#/$defs/Ext"
70
126
  }
@@ -114,6 +170,16 @@ export const PAYLOAD_SCHEMA = {
114
170
  "propertyNames": {
115
171
  "pattern": "^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$"
116
172
  }
173
+ },
174
+ "DigestMultibase": {
175
+ "title": "DigestMultibase",
176
+ "description": "A cryptographic digest as a multibase-encoded multihash — the encoding the W3C Verifiable Credentials Data Model 2.0 defines for `digestMultibase`, and the one `did:webvh` uses for its SCID and entry hashes.\n\nMultihash carries the hash algorithm in-band, so the value is self-describing and the wire format survives an algorithm change without a schema revision; multibase does the same for the base encoding, so a verifier never infers base58 from base64url by context. A bare hex string or a `sha-256:`-style prefix hard-codes one algorithm into the wire contract and is non-conforming here.\n\nThis definition constrains the *encoding only*. What the digest is computed over is stated by each referencing field, because it differs legitimately: a digest over a JSON document is taken over its RFC 8785 (JCS) canonicalization, while a digest over an opaque artifact is taken over its bytes. A field whose input is a JSON document and which does not name a canonicalization is not reproducible.\n\nRestricted to the two multibase headers W3C Controlled Identifiers 1.0 §2.4 normatively requires — `z` (base58btc) and `u` (base64url-no-pad). CID permits others but states that \"interoperability is not guaranteed between implementations using such values\", and a registry whose purpose is interoperability should not mint digests a conforming verifier may be unable to read. The alphabets are enforced rather than assumed: base58btc excludes 0, O, I and l, and an earlier permissive pattern let three published examples carry digests that were not valid base58 at all. base58btc is RECOMMENDED, for consistency with `did:key` and `did:webvh`.",
177
+ "type": "string",
178
+ "minLength": 16,
179
+ "pattern": "^(z[1-9A-HJ-NP-Za-km-z]+|u[A-Za-z0-9_-]+)$",
180
+ "examples": [
181
+ "zQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR"
182
+ ]
117
183
  }
118
184
  }
119
185
  } as const;
@@ -167,6 +233,16 @@ export const RESPONSE_PAYLOAD_SCHEMA = {
167
233
  "propertyNames": {
168
234
  "pattern": "^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$"
169
235
  }
236
+ },
237
+ "DigestMultibase": {
238
+ "title": "DigestMultibase",
239
+ "description": "A cryptographic digest as a multibase-encoded multihash — the encoding the W3C Verifiable Credentials Data Model 2.0 defines for `digestMultibase`, and the one `did:webvh` uses for its SCID and entry hashes.\n\nMultihash carries the hash algorithm in-band, so the value is self-describing and the wire format survives an algorithm change without a schema revision; multibase does the same for the base encoding, so a verifier never infers base58 from base64url by context. A bare hex string or a `sha-256:`-style prefix hard-codes one algorithm into the wire contract and is non-conforming here.\n\nThis definition constrains the *encoding only*. What the digest is computed over is stated by each referencing field, because it differs legitimately: a digest over a JSON document is taken over its RFC 8785 (JCS) canonicalization, while a digest over an opaque artifact is taken over its bytes. A field whose input is a JSON document and which does not name a canonicalization is not reproducible.\n\nRestricted to the two multibase headers W3C Controlled Identifiers 1.0 §2.4 normatively requires — `z` (base58btc) and `u` (base64url-no-pad). CID permits others but states that \"interoperability is not guaranteed between implementations using such values\", and a registry whose purpose is interoperability should not mint digests a conforming verifier may be unable to read. The alphabets are enforced rather than assumed: base58btc excludes 0, O, I and l, and an earlier permissive pattern let three published examples carry digests that were not valid base58 at all. base58btc is RECOMMENDED, for consistency with `did:key` and `did:webvh`.",
240
+ "type": "string",
241
+ "minLength": 16,
242
+ "pattern": "^(z[1-9A-HJ-NP-Za-km-z]+|u[A-Za-z0-9_-]+)$",
243
+ "examples": [
244
+ "zQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR"
245
+ ]
170
246
  }
171
247
  }
172
248
  } as const;
@@ -4,15 +4,41 @@
4
4
  */
5
5
 
6
6
  /**
7
- * Digest over the RFC 8785 canonicalization of the stored VRC, for out-of-band integrity checks. The same value vtc/relationships/request reports on issuance and vtc/relationships/list reports per entry, so a member can tie the three together without re-hashing.
7
+ * Digest over the RFC 8785 canonicalization of the VRC this authorizes. Binds the authorization to one credential, so it cannot be moved to another.
8
8
  */
9
9
  export type DigestMultibase = string;
10
+ /**
11
+ * Digest over the RFC 8785 canonicalization of the stored VRC, for out-of-band integrity checks. The same value vtc/relationships/request reports on issuance and vtc/relationships/list reports per entry, so a member can tie the three together without re-hashing.
12
+ */
13
+ export type DigestMultibase1 = string;
10
14
 
11
15
  export interface VTCRelationshipsPublishPayload {
12
16
  /**
13
17
  * A signed W3C Verifiable Relationship Credential (opaque here). Its issuer MUST equal the document proof signer; credentialSubject.id names the subject member; it carries its own DataIntegrityProof.
14
18
  */
15
19
  vrc: {};
20
+ /**
21
+ * Proof that the caller controls the key behind the VRC's `issuer`, when that is not the party issuing this document.
22
+ *
23
+ * A member publishes an edge under a relationship DID — an identifier scoped to one counterparty, which names nobody. The community still has to know a member published it, and the document's own `proof` says only that the member sent it, not that they control the credential's issuing key. Without this, any member handed a VRC could publish another party's edge.
24
+ *
25
+ * Omit it when the VRC's `issuer` is this document's issuer: the document's own proof already establishes control.
26
+ */
27
+ pop?: {
28
+ /**
29
+ * Guards against a signature the issuer made over some other object being replayed here as authorization to publish.
30
+ */
31
+ type: "VrcPublishAuthorization";
32
+ /**
33
+ * The `id` of the Trust Task document carrying this authorization. Binds the authorization to one document, so a captured one cannot be replayed in another — including by a different member. Every document carries a unique `id` (SPEC §4.3), which is why the binding needs no transport-specific notion of a session.
34
+ */
35
+ documentId: string;
36
+ vrcDigestMultibase: DigestMultibase;
37
+ /**
38
+ * Data-integrity proof over this object, by a verification method the VRC's `issuer` controls. This is the proof of possession; the members above only bound what it authorizes.
39
+ */
40
+ proof: {};
41
+ };
16
42
  ext?: Ext;
17
43
  }
18
44
  /**
@@ -28,7 +54,7 @@ export interface VTCRelationshipsPublishResponsePayload {
28
54
  id: string;
29
55
  issuerDid: string;
30
56
  subjectDid: string;
31
- vrcDigestMultibase: DigestMultibase;
57
+ vrcDigestMultibase: DigestMultibase1;
32
58
  ext?: Ext;
33
59
  }
34
60
 
@@ -67,6 +93,35 @@ export const PAYLOAD_SCHEMA = {
67
93
  "type": "object",
68
94
  "description": "A signed W3C Verifiable Relationship Credential (opaque here). Its issuer MUST equal the document proof signer; credentialSubject.id names the subject member; it carries its own DataIntegrityProof."
69
95
  },
96
+ "pop": {
97
+ "type": "object",
98
+ "description": "Proof that the caller controls the key behind the VRC's `issuer`, when that is not the party issuing this document.\n\nA member publishes an edge under a relationship DID — an identifier scoped to one counterparty, which names nobody. The community still has to know a member published it, and the document's own `proof` says only that the member sent it, not that they control the credential's issuing key. Without this, any member handed a VRC could publish another party's edge.\n\nOmit it when the VRC's `issuer` is this document's issuer: the document's own proof already establishes control.",
99
+ "properties": {
100
+ "type": {
101
+ "const": "VrcPublishAuthorization",
102
+ "description": "Guards against a signature the issuer made over some other object being replayed here as authorization to publish."
103
+ },
104
+ "documentId": {
105
+ "type": "string",
106
+ "description": "The `id` of the Trust Task document carrying this authorization. Binds the authorization to one document, so a captured one cannot be replayed in another — including by a different member. Every document carries a unique `id` (SPEC §4.3), which is why the binding needs no transport-specific notion of a session."
107
+ },
108
+ "vrcDigestMultibase": {
109
+ "$ref": "#/$defs/DigestMultibase",
110
+ "description": "Digest over the RFC 8785 canonicalization of the VRC this authorizes. Binds the authorization to one credential, so it cannot be moved to another."
111
+ },
112
+ "proof": {
113
+ "type": "object",
114
+ "description": "Data-integrity proof over this object, by a verification method the VRC's `issuer` controls. This is the proof of possession; the members above only bound what it authorizes."
115
+ }
116
+ },
117
+ "required": [
118
+ "type",
119
+ "documentId",
120
+ "vrcDigestMultibase",
121
+ "proof"
122
+ ],
123
+ "additionalProperties": false
124
+ },
70
125
  "ext": {
71
126
  "$ref": "#/$defs/Ext"
72
127
  }