@openvtc/trust-tasks 0.16.9 → 0.16.11

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 (166) hide show
  1. package/CHANGELOG.md +220 -0
  2. package/dist/_shared/components.d.ts +206 -5
  3. package/dist/_shared/components.d.ts.map +1 -1
  4. package/dist/index.d.ts +27 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +27 -0
  7. package/dist/index.js.map +1 -1
  8. package/dist/persona/_shared/0.1/persona-record.d.ts +22 -0
  9. package/dist/persona/_shared/0.1/persona-record.d.ts.map +1 -0
  10. package/dist/persona/_shared/0.1/persona-record.js +6 -0
  11. package/dist/persona/_shared/0.1/persona-record.js.map +1 -0
  12. package/dist/persona/attribute/delete/1.0/payload.d.ts +351 -0
  13. package/dist/persona/attribute/delete/1.0/payload.d.ts.map +1 -0
  14. package/dist/persona/attribute/delete/1.0/payload.js +188 -0
  15. package/dist/persona/attribute/delete/1.0/payload.js.map +1 -0
  16. package/dist/persona/attribute/list/1.0/payload.d.ts +895 -0
  17. package/dist/persona/attribute/list/1.0/payload.d.ts.map +1 -0
  18. package/dist/persona/attribute/list/1.0/payload.js +530 -0
  19. package/dist/persona/attribute/list/1.0/payload.js.map +1 -0
  20. package/dist/persona/attribute/put/1.0/payload.d.ts +829 -0
  21. package/dist/persona/attribute/put/1.0/payload.d.ts.map +1 -0
  22. package/dist/persona/attribute/put/1.0/payload.js +487 -0
  23. package/dist/persona/attribute/put/1.0/payload.js.map +1 -0
  24. package/dist/persona/binding/get/1.0/payload.d.ts +382 -0
  25. package/dist/persona/binding/get/1.0/payload.d.ts.map +1 -0
  26. package/dist/persona/binding/get/1.0/payload.js +206 -0
  27. package/dist/persona/binding/get/1.0/payload.js.map +1 -0
  28. package/dist/persona/binding/list/1.0/payload.d.ts +387 -0
  29. package/dist/persona/binding/list/1.0/payload.d.ts.map +1 -0
  30. package/dist/persona/binding/list/1.0/payload.js +210 -0
  31. package/dist/persona/binding/list/1.0/payload.js.map +1 -0
  32. package/dist/persona/binding/set/1.0/payload.d.ts +545 -0
  33. package/dist/persona/binding/set/1.0/payload.d.ts.map +1 -0
  34. package/dist/persona/binding/set/1.0/payload.js +302 -0
  35. package/dist/persona/binding/set/1.0/payload.js.map +1 -0
  36. package/dist/persona/contact/delete/1.0/payload.d.ts +313 -0
  37. package/dist/persona/contact/delete/1.0/payload.d.ts.map +1 -0
  38. package/dist/persona/contact/delete/1.0/payload.js +174 -0
  39. package/dist/persona/contact/delete/1.0/payload.js.map +1 -0
  40. package/dist/persona/contact/get/1.0/payload.d.ts +1027 -0
  41. package/dist/persona/contact/get/1.0/payload.d.ts.map +1 -0
  42. package/dist/persona/contact/get/1.0/payload.js +606 -0
  43. package/dist/persona/contact/get/1.0/payload.js.map +1 -0
  44. package/dist/persona/contact/list/1.0/payload.d.ts +490 -0
  45. package/dist/persona/contact/list/1.0/payload.d.ts.map +1 -0
  46. package/dist/persona/contact/list/1.0/payload.js +262 -0
  47. package/dist/persona/contact/list/1.0/payload.js.map +1 -0
  48. package/dist/persona/contact/put/1.0/payload.d.ts +885 -0
  49. package/dist/persona/contact/put/1.0/payload.d.ts.map +1 -0
  50. package/dist/persona/contact/put/1.0/payload.js +518 -0
  51. package/dist/persona/contact/put/1.0/payload.js.map +1 -0
  52. package/dist/persona/correlation/analyze/1.0/payload.d.ts +648 -0
  53. package/dist/persona/correlation/analyze/1.0/payload.d.ts.map +1 -0
  54. package/dist/persona/correlation/analyze/1.0/payload.js +338 -0
  55. package/dist/persona/correlation/analyze/1.0/payload.js.map +1 -0
  56. package/dist/persona/disclosure/history/1.0/payload.d.ts +639 -0
  57. package/dist/persona/disclosure/history/1.0/payload.d.ts.map +1 -0
  58. package/dist/persona/disclosure/history/1.0/payload.js +339 -0
  59. package/dist/persona/disclosure/history/1.0/payload.js.map +1 -0
  60. package/dist/persona/disclosure/present/1.0/payload.d.ts +400 -0
  61. package/dist/persona/disclosure/present/1.0/payload.d.ts.map +1 -0
  62. package/dist/persona/disclosure/present/1.0/payload.js +211 -0
  63. package/dist/persona/disclosure/present/1.0/payload.js.map +1 -0
  64. package/dist/persona/disclosure/preview/1.0/payload.d.ts +858 -0
  65. package/dist/persona/disclosure/preview/1.0/payload.d.ts.map +1 -0
  66. package/dist/persona/disclosure/preview/1.0/payload.js +480 -0
  67. package/dist/persona/disclosure/preview/1.0/payload.js.map +1 -0
  68. package/dist/persona/local/binding/set/1.0/payload.d.ts +394 -0
  69. package/dist/persona/local/binding/set/1.0/payload.d.ts.map +1 -0
  70. package/dist/persona/local/binding/set/1.0/payload.js +226 -0
  71. package/dist/persona/local/binding/set/1.0/payload.js.map +1 -0
  72. package/dist/persona/local/profile/delete/1.0/payload.d.ts +311 -0
  73. package/dist/persona/local/profile/delete/1.0/payload.d.ts.map +1 -0
  74. package/dist/persona/local/profile/delete/1.0/payload.js +173 -0
  75. package/dist/persona/local/profile/delete/1.0/payload.js.map +1 -0
  76. package/dist/persona/local/profile/get/1.0/payload.d.ts +368 -0
  77. package/dist/persona/local/profile/get/1.0/payload.d.ts.map +1 -0
  78. package/dist/persona/local/profile/get/1.0/payload.js +210 -0
  79. package/dist/persona/local/profile/get/1.0/payload.js.map +1 -0
  80. package/dist/persona/local/profile/list/1.0/payload.d.ts +387 -0
  81. package/dist/persona/local/profile/list/1.0/payload.d.ts.map +1 -0
  82. package/dist/persona/local/profile/list/1.0/payload.js +214 -0
  83. package/dist/persona/local/profile/list/1.0/payload.js.map +1 -0
  84. package/dist/persona/local/profile/put/1.0/payload.d.ts +545 -0
  85. package/dist/persona/local/profile/put/1.0/payload.d.ts.map +1 -0
  86. package/dist/persona/local/profile/put/1.0/payload.js +315 -0
  87. package/dist/persona/local/profile/put/1.0/payload.js.map +1 -0
  88. package/dist/persona/profile/delete/1.0/payload.d.ts +350 -0
  89. package/dist/persona/profile/delete/1.0/payload.d.ts.map +1 -0
  90. package/dist/persona/profile/delete/1.0/payload.js +189 -0
  91. package/dist/persona/profile/delete/1.0/payload.js.map +1 -0
  92. package/dist/persona/profile/get/1.0/payload.d.ts +1307 -0
  93. package/dist/persona/profile/get/1.0/payload.d.ts.map +1 -0
  94. package/dist/persona/profile/get/1.0/payload.js +802 -0
  95. package/dist/persona/profile/get/1.0/payload.js.map +1 -0
  96. package/dist/persona/profile/list/1.0/payload.d.ts +1120 -0
  97. package/dist/persona/profile/list/1.0/payload.d.ts.map +1 -0
  98. package/dist/persona/profile/list/1.0/payload.js +680 -0
  99. package/dist/persona/profile/list/1.0/payload.js.map +1 -0
  100. package/dist/persona/profile/put/1.0/payload.d.ts +1115 -0
  101. package/dist/persona/profile/put/1.0/payload.d.ts.map +1 -0
  102. package/dist/persona/profile/put/1.0/payload.js +675 -0
  103. package/dist/persona/profile/put/1.0/payload.js.map +1 -0
  104. package/dist/persona/renderers/list/1.0/payload.d.ts +362 -0
  105. package/dist/persona/renderers/list/1.0/payload.d.ts.map +1 -0
  106. package/dist/persona/renderers/list/1.0/payload.js +197 -0
  107. package/dist/persona/renderers/list/1.0/payload.js.map +1 -0
  108. package/dist/rooms/owner/claim/0.1/payload.d.ts +391 -0
  109. package/dist/rooms/owner/claim/0.1/payload.d.ts.map +1 -0
  110. package/dist/rooms/owner/claim/0.1/payload.js +214 -0
  111. package/dist/rooms/owner/claim/0.1/payload.js.map +1 -0
  112. package/dist/rooms/owner/transfer/0.1/payload.d.ts +391 -0
  113. package/dist/rooms/owner/transfer/0.1/payload.d.ts.map +1 -0
  114. package/dist/rooms/owner/transfer/0.1/payload.js +214 -0
  115. package/dist/rooms/owner/transfer/0.1/payload.js.map +1 -0
  116. package/dist/vta/_shared/0.1/app-state-record.d.ts +1 -1
  117. package/dist/vta/_shared/0.1/app-state-record.d.ts.map +1 -1
  118. package/dist/vta/app-state/delete/1.0/payload.d.ts +1 -1
  119. package/dist/vta/app-state/delete/1.0/payload.d.ts.map +1 -1
  120. package/dist/vta/app-state/get/1.0/payload.d.ts +1 -1
  121. package/dist/vta/app-state/get/1.0/payload.d.ts.map +1 -1
  122. package/dist/vta/app-state/get-many/1.0/payload.d.ts +1 -1
  123. package/dist/vta/app-state/get-many/1.0/payload.d.ts.map +1 -1
  124. package/dist/vta/app-state/list/1.0/payload.d.ts +1 -1
  125. package/dist/vta/app-state/list/1.0/payload.d.ts.map +1 -1
  126. package/dist/vta/app-state/put/1.0/payload.d.ts +1 -1
  127. package/dist/vta/app-state/put/1.0/payload.d.ts.map +1 -1
  128. package/dist/vta/app-state/put-many/1.0/payload.d.ts +1 -1
  129. package/dist/vta/app-state/put-many/1.0/payload.d.ts.map +1 -1
  130. package/package.json +1 -1
  131. package/src/_shared/components.ts +213 -5
  132. package/src/index.ts +27 -0
  133. package/src/persona/_shared/0.1/persona-record.ts +23 -0
  134. package/src/persona/attribute/delete/1.0/payload.ts +238 -0
  135. package/src/persona/attribute/list/1.0/payload.ts +588 -0
  136. package/src/persona/attribute/put/1.0/payload.ts +559 -0
  137. package/src/persona/binding/get/1.0/payload.ts +257 -0
  138. package/src/persona/binding/list/1.0/payload.ts +260 -0
  139. package/src/persona/binding/set/1.0/payload.ts +370 -0
  140. package/src/persona/contact/delete/1.0/payload.ts +216 -0
  141. package/src/persona/contact/get/1.0/payload.ts +662 -0
  142. package/src/persona/contact/list/1.0/payload.ts +323 -0
  143. package/src/persona/contact/put/1.0/payload.ts +580 -0
  144. package/src/persona/correlation/analyze/1.0/payload.ts +466 -0
  145. package/src/persona/disclosure/history/1.0/payload.ts +415 -0
  146. package/src/persona/disclosure/present/1.0/payload.ts +274 -0
  147. package/src/persona/disclosure/preview/1.0/payload.ts +597 -0
  148. package/src/persona/local/binding/set/1.0/payload.ts +267 -0
  149. package/src/persona/local/profile/delete/1.0/payload.ts +215 -0
  150. package/src/persona/local/profile/get/1.0/payload.ts +251 -0
  151. package/src/persona/local/profile/list/1.0/payload.ts +260 -0
  152. package/src/persona/local/profile/put/1.0/payload.ts +373 -0
  153. package/src/persona/profile/delete/1.0/payload.ts +236 -0
  154. package/src/persona/profile/get/1.0/payload.ts +844 -0
  155. package/src/persona/profile/list/1.0/payload.ts +723 -0
  156. package/src/persona/profile/put/1.0/payload.ts +741 -0
  157. package/src/persona/renderers/list/1.0/payload.ts +248 -0
  158. package/src/rooms/owner/claim/0.1/payload.ts +268 -0
  159. package/src/rooms/owner/transfer/0.1/payload.ts +268 -0
  160. package/src/vta/_shared/0.1/app-state-record.ts +1 -1
  161. package/src/vta/app-state/delete/1.0/payload.ts +1 -1
  162. package/src/vta/app-state/get/1.0/payload.ts +1 -1
  163. package/src/vta/app-state/get-many/1.0/payload.ts +1 -1
  164. package/src/vta/app-state/list/1.0/payload.ts +1 -1
  165. package/src/vta/app-state/put/1.0/payload.ts +1 -1
  166. package/src/vta/app-state/put-many/1.0/payload.ts +1 -1
package/CHANGELOG.md CHANGED
@@ -11,6 +11,226 @@ The package versions over **its own API** — what a consumer compiles against
11
11
  not over `SPEC.md`. Below 1.0 a breaking change bumps the leading non-zero
12
12
  component.
13
13
 
14
+ ## 0.16.11 — 2026-09-05
15
+
16
+
17
+ ### Specifications
18
+
19
+ - **persona**: Attribute.value is OPTIONAL, so the default listing is representable (#367)
20
+
21
+ The schema required `value` while its own description said the member is
22
+ absent in two situations. That contradiction made the DEFAULT path of
23
+ persona/attribute/list unrepresentable: `includeValues` defaults to false
24
+ precisely so a picker can render the pool without decrypting every fact in it,
25
+ and a maintainer taking that path had to choose between disclosing every value
26
+ in bulk and emitting a non-conformant response.
27
+
28
+ The same contradiction blocked the other documented case — a credential-backed
29
+ value that could not be re-derived, which is returned carrying `stale` so the
30
+ holder learns a claim has stopped being presentable rather than seeing a pool
31
+ that looks smaller than it is.
32
+
33
+ Found by a conformance witness in verifiable-trust-infrastructure while wiring
34
+ the family: the witness for the default listing would not round-trip. Which is
35
+ what witnesses are for — the defect was in the schema, not the implementation,
36
+ and nothing else would have caught it before the first non-disclosing read
37
+ went out.
38
+
39
+ Both bindings regenerated; conformance checks 403 specs against 403 TypeScript
40
+ and 398 Rust modules.
41
+
42
+ - **rooms**: The Consent/purpose section six specs shipped without (#365)
43
+
44
+ The Security & Privacy lint landed 2026-08-26 (#273). These six specs landed
45
+ between 3 and 5 September, each carrying `Data carried`, `Correlation` and
46
+ `Retention` but not `Consent/purpose`. That is not the debt the allowlist is
47
+ for — its own message says it is "only for content predating this lint" — so
48
+ the fix is to write the sections, not to list the specs.
49
+
50
+ Each says what the disclosure is for, what records the basis, and where the
51
+ purpose stops. What the six have in common is that the interesting part is the
52
+ limit:
53
+
54
+ - `rooms/records/curate` — the `reason` is addressed to the room, not to the
55
+ record's author, who is neither asked nor notified; and no consent withdrawn
56
+ here reaches an export taken before the retraction.
57
+ - `rooms/owner/claim` — the consent authorizing a claim was given in advance by
58
+ the previous owner, and a nomination with no expiry cannot be withdrawn by an
59
+ owner who has stopped, which is definitionally what a dormant owner has done.
60
+ The room's other members are not party to it.
61
+ - `rooms/owner/transfer` — the outgoing owner's consent is contemporaneous; the
62
+ incoming owner's is not recorded at all, and the specification defines no
63
+ member through which they accept or decline a role that carries quota, abuse
64
+ and lifecycle obligations.
65
+ - `rooms/keys/commit` — there is no per-commit decision to make. A member who
66
+ does not apply one has declined nothing; consent operates at the boundaries
67
+ of membership, not on each epoch inside it.
68
+ - `rooms/keys/key-package` — minting is not joining, the expiry is a purpose
69
+ bound rather than housekeeping, and reuse across rooms is a disclosure the
70
+ party never made and cannot detect.
71
+ - `rooms/keys/welcome` — consent reaches the member's own key-holding agent and
72
+ no further, which is why a `private` room's Welcome is not routed through the
73
+ host: accepting an invitation is not consenting to the host learning you are
74
+ in the room.
75
+
76
+ Prose only. No front matter, schema, or generated code moves.
77
+
78
+ Also turns on TT_STRICT_SECURITY_PRIVACY in the CI build. With these six
79
+ written the non-allowlisted count is zero, so strict mode passes today and
80
+ fires only on a spec added without the section — the allowlist is closed to new
81
+ content by definition. It was six specs in three days that made the case: the
82
+ warning was invisible in a green build, and nothing else would have caught
83
+ them. Negative-tested by removing one section and confirming the build fails.
84
+
85
+ ## 0.16.10 — 2026-09-05
86
+
87
+
88
+ ### Added
89
+
90
+ - **persona**: A holder's own identity, composed once and disclosed under control (#360)
91
+
92
+ * feat(persona): attribute pool — shared schema and attribute/put
93
+
94
+ First slice of the persona family: the holder's own identity attributes,
95
+ agent-scoped above the context boundary.
96
+
97
+ The shared schema carries the family's load-bearing definitions — the three
98
+ provenance kinds, the four profile-entry forms, the proof-rung ordering, and
99
+ the two-scope rule that keeps a context-scoped caller from reading the pool.
100
+
101
+ attribute/put establishes the pattern: holder-authorized and unscoped,
102
+ value validated against its declared type, credential-backed provenance
103
+ resolved at write time so an attribute cannot read back stale forever, and an
104
+ advisory correlation count returned on the write so a builder can warn while
105
+ the holder is still composing.
106
+
107
+ - **rooms/owner**: Transfer and claim, so a room outlives one person (#359)
108
+
109
+ §10 of the data-rooms design was the last part with no specification.
110
+ Ownership there is load-bearing for liveness, not just administration: a
111
+ room's owner is its sole committer, so a room with no reachable owner
112
+ cannot advance an epoch, cannot be renewed, and lapses to read-only. One
113
+ person becoming unreachable ends a shared space.
114
+
115
+ Two tasks, not one with a flag. They end the same way and differ in
116
+ everything else - who initiates, what authorizes, and whether the room
117
+ has to have lapsed. Collapsing them would mean either a transfer that
118
+ waits for a lapse or a claim that works while the owner is still
119
+ renewing, and both are wrong.
120
+
121
+ There is deliberately no nominate task. A nomination is a credential the
122
+ room issues and the claimant presents, the same shape as an invitation
123
+ and for the same reason: a host keeping a roster of successors would hold
124
+ part of the room's authority structure, and the room could no longer move
125
+ hosts without rebuilding it. The consequence is that 'no successor' and
126
+ 'the successor has not claimed yet' are the same observation to a host -
127
+ which is fine, because both resolve identically when retention runs out.
128
+
129
+ A claim is an ACT, never an automatic promotion, and that is not
130
+ ceremony. An automatic one is an ownership change nobody performed: no
131
+ actor to audit, no moment to point at, and an owner who returns finds the
132
+ room changed hands with no event to examine.
133
+
134
+ Three conditions, all required, each closing a different route to a
135
+ takeover: a nomination the room itself issued naming this claimant; the
136
+ room dormant rather than merely lapsed, so the grace window has passed
137
+ and the owner has had their notice; and the claimant already a member,
138
+ because a successor who cannot commit inherits a room they cannot renew.
139
+
140
+ Renewing cancels a pending claim, and that is the property worth
141
+ noticing: the defence against a hostile claim is the same act as ordinary
142
+ use. An owner who was merely away fixes it by doing what they would have
143
+ done anyway - nothing revoked, no dispute raised.
144
+
145
+ Transfer requires the incoming owner to be a member for the same reason.
146
+ Handing someone a room they cannot renew looks like success and produces
147
+ a room that lapses on schedule a year later with nobody able to save it.
148
+
149
+ Three things the Security sections say plainly. A transfer does NOT
150
+ remove the outgoing owner - they keep whatever their credentials confer,
151
+ usually still admin, so handing over and leaving are two more acts. Every
152
+ credential stays valid because the room's identifier does not change,
153
+ which is the whole reason a room has an identifier of its own rather than
154
+ borrowing its owner's. And the host is not an arbiter: it records what an
155
+ authorized party told it, and ownership of an identifier is settled by
156
+ whoever controls that identifier, not by a service that stores
157
+ ciphertext.
158
+
159
+
160
+
161
+ ### Documentation
162
+
163
+ - Base every example on the registered acl/change-role task (#356)
164
+
165
+ SPEC.md, both READMEs, the loopback example and the framework unit tests
166
+ all illustrated themselves with `kyc-handoff/1.0`, a slug that has never
167
+ been registered. Three consequences, in increasing order of cost:
168
+
169
+ - `https://trusttasks.org/spec/kyc-handoff/1.0` is a live URL in the
170
+ Abstract and in five worked examples. It resolves — the registry site
171
+ serves the SPA for any /spec/ path — and rendered `acl/change-role`,
172
+ the alphabetically first entry, because SpecPage fell back to
173
+ `TT_TASKS[0]` for an unresolvable slug. A reader following the URL in
174
+ §1 got a complete, confident page for a different task.
175
+ - No example could be checked against anything. The build validates every
176
+ fenced JSON block against the framework envelope schema; a payload
177
+ naming a slug with no schema was structurally unverifiable.
178
+ - The `trust-task-error` extended-code example was non-conforming: it
179
+ answered an ACL request with a `kyc-handoff:`-namespaced code, which
180
+ §8.5 forbids in the same breath as it defines namespacing.
181
+
182
+ Everything now names `acl/change-role/0.1`, whose payloads validate
183
+ against its published schema, whose error codes are the two it declares,
184
+ and whose REQUIRED `proof` the examples honour. Appendix A is reproduced
185
+ from that registry entry rather than invented. The framework parser tests
186
+ that used the slug as an arbitrary string now use real ones —
187
+ `trust-task-discovery` and `trust-task-control` where a single-segment
188
+ slug is the point.
189
+
190
+ SpecPage is strict as a result: an unregistered slug, or a known slug at
191
+ an unknown version, renders a not-found page offering the versions that
192
+ do exist, the way BindingSpecPage and CeremonyPage already did. The
193
+ lookup moved into a hook-free wrapper so a route that stops resolving
194
+ unmounts the detail view rather than changing a mounted component's hook
195
+ count.
196
+
197
+ SPEC.md is also maintained canonically at trustoverip/dtgwg-trust-tasks-spec;
198
+ the matching change lands there in parallel.
199
+
200
+
201
+
202
+ ### Fixed
203
+
204
+ - **rooms/owner**: A host cannot see the MLS group, so stop implying it can (#361)
205
+
206
+ Both tasks required a host to check that a party is a member of the
207
+ room's group. A host holds no roster and no group state, so neither check
208
+ was implementable as written. Found while starting the implementation,
209
+ which is what implementation is for.
210
+
211
+ claim now carries a presentation. The host has exactly one membership
212
+ signal available - the VMC the room itself issued - and the claim payload
213
+ did not ask for it, so condition 3 was unimplementable and the claimant's
214
+ own standing went unverified. It is now required, and it is the same
215
+ presentation every other room task carries.
216
+
217
+ The spec is also exact about what that proxy is worth: a party removed
218
+ from the MLS group while still holding an unexpired VMC would pass. That
219
+ gap closes by revoking the credential, which is the room's job, not the
220
+ host's. Better said than implied.
221
+
222
+ transfer moves the obligation to the party who can discharge it. The
223
+ incoming owner is not the one making the request, presents nothing, and
224
+ may be someone the host has never seen - so 'MUST refuse if not a member'
225
+ asked a host to judge a third party from no evidence. The requirement is
226
+ now on the transferring owner, who can see the group; a host that can
227
+ independently establish non-membership MAY still refuse, and one that
228
+ cannot MUST NOT invent a check or treat its own ignorance as evidence.
229
+
230
+ That last point is the same boundary the rest of the family draws,
231
+ arriving from an unfamiliar direction: a host verifies what is presented
232
+ to it, and a claim about a third party is not that.
233
+
14
234
  ## 0.16.9 — 2026-09-04
15
235
 
16
236
 
@@ -27,6 +27,14 @@ export type Capability_DeviceV0_1 = "vault-read" | "vault-write" | "proxy-login"
27
27
  * Fine-grained capability flag scoped to the device's allowed contexts. See SPEC.md for the full semantics of each. Capability values are additive: a consumer MUST ignore a value it does not recognise rather than reject the binding, and MUST NOT treat an unrecognised value as conferring anything.
28
28
  */
29
29
  export type Capability_DeviceV0_2 = "vaultRead" | "vaultWrite" | "proxyLogin" | "fillRelease" | "policyAdmin" | "deviceAdmin" | "sign" | "keyMint" | "signTrustTask" | "credentialWrite" | "memoryRead" | "memoryWrite" | "roomPresent" | "roomOpen";
30
+ /**
31
+ * The vocabulary token naming what a value IS — `name.legal`, `phone.mobile`, `address.postal`, `person.birthDate`. Dotted, most-general segment first, so that a consumer with no knowledge of the specific token can still group by its prefix.
32
+ *
33
+ * The token is the maintainer's own; no external vocabulary is primary. External vocabularies (vCard/jCard, OIDC standard claims, schema.org) are mappings applied at PRESENTATION by a renderer, not at rest, so that a query written in any of them can be matched without the store having to live inside any one of them.
34
+ *
35
+ * The `x:` prefix is an open extension namespace and is not decoration. The closest prior art — Windows CardSpace's self-issued card — supported exactly fifteen predefined claim types with no extensibility, and that is the specific way it failed the requirement a holder actually has. An `x:` attribute stores, composes, binds and discloses exactly like a known one; it renders generically and matches only an explicit query.
36
+ */
37
+ export type ClaimType = string;
30
38
  /**
31
39
  * Discriminator: is this consumer a user-driven Companion or a headless Service?
32
40
  */
@@ -65,10 +73,14 @@ export type DigestMultibase = string;
65
73
  * Whether the subject is permitted. The ABSENCE of any grant is treated as `deny` (default-deny).
66
74
  */
67
75
  export type Effect_ConsentV0_1 = "allow" | "deny";
76
+ /**
77
+ * Optimistic-concurrency precondition. A positive value requires the record's current `version` to equal it exactly; zero means create-only and applies only when no live record exists at the address.
78
+ */
79
+ export type ExpectedVersion_PersonaV0_1 = number;
68
80
  /**
69
81
  * Optimistic-concurrency precondition on a write. A positive value requires that the record's current `version` equals it exactly. Zero means "create only" — the write applies only if no LIVE record exists at the address, which is what makes lease acquisition safe: without it two instances can each read "absent", each write, and each believe it won. A tombstone is not a live record, so `expectedVersion: 0` succeeds over one; the created record takes the namespace's next counter value, which is necessarily greater than the tombstone's.
70
82
  */
71
- export type ExpectedVersion = number;
83
+ export type ExpectedVersion_VtaV0_1 = number;
72
84
  /**
73
85
  * `issued` is outstanding and revocable; `consumed` and `expired` are terminal.
74
86
  */
@@ -97,6 +109,88 @@ export type Kind = "dm" | "group" | "channel";
97
109
  * Scopes one application's records within a context, so several tools can share a context without colliding — `openvtc`, `cnm`, an agent runtime. The maintainer MUST NOT interpret the value; it is an opaque partition name. Namespaces are first-come and unreserved, so an application SHOULD pick a stable, specific one: a future per-namespace ACL would grant on this exact string, which makes renaming a namespace a migration rather than an edit.
98
110
  */
99
111
  export type Namespace = string;
112
+ /**
113
+ * One line of a profile, in exactly one of four forms. Together they are the whole of a profile's flexibility, and each exists for a case the others handle badly.
114
+ *
115
+ * `{ref}` — use the pool attribute, live. Editing the pool updates every profile that references it, which is the point.
116
+ *
117
+ * `{ref, pinVersion}` — use the value as it was at that version. For a profile that must keep presenting the value a counterparty already verified.
118
+ *
119
+ * `{ref, override}` — the same fact, a different value here. ("In the gaming profile my display name is different.")
120
+ *
121
+ * `{inline}` — a value that never enters the pool, and so never leaks into another profile.
122
+ *
123
+ * Omission is exclusion; there is no removal marker.
124
+ */
125
+ export type ProfileEntry = {
126
+ ref: Ulid;
127
+ } | {
128
+ ref: Ulid;
129
+ pinVersion: Version_PersonaV0_1;
130
+ } | {
131
+ ref: Ulid;
132
+ /**
133
+ * Replaces the pool attribute's value for this profile only. `type`, `valueType` and `provenance` are inherited from the referenced attribute and MUST NOT be overridden — an override that changed provenance would let a self-asserted value present as attested.
134
+ */
135
+ override: {
136
+ value: unknown;
137
+ label?: string;
138
+ };
139
+ } | {
140
+ inline: {
141
+ type: ClaimType;
142
+ valueType: ValueType;
143
+ value: unknown;
144
+ label?: string;
145
+ provenance: Provenance;
146
+ };
147
+ };
148
+ /**
149
+ * How strongly a credential-backed claim is hidden when presented, ordered most private first. `predicate` proves a statement over a claim without disclosing the claim. `derived` discloses exactly the claims needed via an unlinkable derived proof, so two presentations cannot be joined. `selectiveDisclosure` discloses exactly the claims needed but carries the issuer's signature unchanged, so two presentations ARE linkable. `whole` discloses the entire credential.
150
+ *
151
+ * The distinction between the first two and the last two is of kind, not degree: only `predicate` and `derived` avoid handing two verifiers a join key. A maintainer MUST default to the highest rung the credential's format supports, and MUST NOT silently fall to a lower one — a request that cannot be satisfied at the rung a producer asked for is refused, because a silent privacy downgrade discloses material the holder believed was hidden.
152
+ */
153
+ export type ProofRung = "predicate" | "derived" | "selectiveDisclosure" | "whole";
154
+ /**
155
+ * Where a value comes from, and the member that makes this family worth building on a trust stack rather than in an address book. It survives to the verifier, so a recipient can tell — per field — what the holder typed from what an issuer attested.
156
+ *
157
+ * `selfAsserted` — the holder supplied it.
158
+ *
159
+ * `credentialBacked` — the value is derived from a credential in the vault at `claimPath`. The stored value is a CACHE FOR DISPLAY; the credential is the truth. A maintainer MUST re-derive it on read and MUST fail closed (never presenting a stale value) when the credential has been revoked, has expired, or has been archived or deleted.
160
+ *
161
+ * `generated` — the value is minted per verifier at disclosure time and recorded against that verifier, so every relying party receives a different one that routes back to the holder. This is the shape of the most widely adopted consumer privacy feature in this space; a maintainer need not operate a relay to conform, but the shape must exist, because retrofitting per-verifier values into a pool-of-values model is a migration rather than an addition.
162
+ */
163
+ export type Provenance = {
164
+ kind: "selfAsserted";
165
+ } | {
166
+ kind: "credentialBacked";
167
+ /**
168
+ * Vault identifier of the backing credential.
169
+ */
170
+ credentialId: string;
171
+ /**
172
+ * RFC 6901 JSON Pointer to the claim within the credential, e.g. `/credentialSubject/familyName`.
173
+ */
174
+ claimPath: string;
175
+ /**
176
+ * Issuer of the backing credential. Advisory: a consumer MUST verify the credential rather than trusting this member.
177
+ */
178
+ issuerDid?: string;
179
+ /**
180
+ * The disclosure rung this claim was, or will be, presented at.
181
+ */
182
+ proof?: ProofRung;
183
+ } | {
184
+ kind: "generated";
185
+ /**
186
+ * Names the minting scheme, e.g. `relayEmail`. Maintainer-defined.
187
+ */
188
+ generator: string;
189
+ /**
190
+ * When true (the default and the only useful setting), a distinct value is minted for each verifier.
191
+ */
192
+ perVerifier?: boolean;
193
+ };
100
194
  /**
101
195
  * A device's platform push channel — the body the device registers with its push GATEWAY (push wake-up binding, https://trusttasks.org/binding/push/0.1; modeled on Aries RFC 0699/0734). The gateway holds this token and returns an opaque WakeHandle in exchange; the token is held by the gateway ONLY, never by the mediator or the maintainer/VTA. The gateway uses it to send a contentless wake-up when an authorized trigger asks — the push payload never carries Trust Task content. Tagged union over the discriminator `platform`.
102
196
  */
@@ -167,6 +261,14 @@ export type SiteTarget_VaultV0_1 = WebOrigin_VaultV0_1 | Did_VaultV0_1 | IosApp_
167
261
  export type SiteTarget_VaultV0_2 = WebOrigin_VaultV0_2 | Did_VaultV0_2 | IosApp_VaultV0_2 | AndroidApp_VaultV0_2;
168
262
  export type SyncEvent_SyncV0_1 = VaultUpsertedEvent_SyncV0_1 | VaultDeletedEvent_SyncV0_1 | AclChangedEvent_SyncV0_1 | PolicyChangedEvent_SyncV0_1;
169
263
  export type SyncEvent_SyncV0_2 = VaultUpsertedEvent_SyncV0_2 | VaultDeletedEvent_SyncV0_2 | AclChangedEvent_SyncV0_2 | PolicyChangedEvent_SyncV0_2;
264
+ /**
265
+ * A ULID in Crockford base32, uppercase. Used for `attributeId` and `profileId`. Chosen over a UUID because the leading 48 bits are a timestamp, so a key-ordered scan of the store is also creation-ordered and a `list` needs no secondary sort. Server-assigned on create; a producer MAY supply one to make a create idempotent, and a maintainer MUST reject a supplied value that already exists rather than silently overwriting.
266
+ */
267
+ export type Ulid = string;
268
+ /**
269
+ * The JSON shape of `value`, declared so that a consumer can render and compare without guessing. The maintainer validates that `value` agrees with this member and does nothing further: it does NOT validate a phone number against a phone-number grammar. That is a producer's affordance, and a store that grows opinions about the contents of its records eventually blocks its consumer's release.
270
+ */
271
+ export type ValueType = "string" | "number" | "boolean" | "date" | "object";
170
272
  /**
171
273
  * What the policy decided.
172
274
  *
@@ -175,10 +277,14 @@ export type SyncEvent_SyncV0_2 = VaultUpsertedEvent_SyncV0_2 | VaultDeletedEvent
175
277
  * The four are not reducible to a pending/decided pair. `refer` and `requestMore` are both 'not decided', but they place the next action with different parties: `refer` waits on the community, `requestMore` waits on the applicant. A consumer that cannot tell them apart cannot tell a user whether to wait or to act.
176
278
  */
177
279
  export type VerdictEffect = "allow" | "deny" | "refer" | "requestMore";
280
+ /**
281
+ * A value of the store's monotonic write counter. Server-assigned; a producer never chooses one.
282
+ */
283
+ export type Version_PersonaV0_1 = number;
178
284
  /**
179
285
  * A value of the namespace's monotonic write counter (see this schema's description). Server-assigned; a producer never chooses one.
180
286
  */
181
- export type Version = number;
287
+ export type Version_VtaV0_1 = number;
182
288
  /**
183
289
  * A Verifiable Identifier (SPEC §4.8). For a mediator-served account this is the account's controlling DID, carried verbatim and compared by exact string equality. For privacy — and because some mediators key accounts by a one-way hash and never hold the full DID — a stable hash of the DID (e.g. its SHA-256 digest) is an equally valid value here: producer and consumer simply agree on the same opaque identifier and compare by exact string equality. The field carries whichever form the issuing mediator uses.
184
290
  */
@@ -353,7 +459,7 @@ export interface AppStateRecord {
353
459
  /**
354
460
  * The namespace counter value this record's most recent write took. Supply it as `expectedVersion` on the next write to make that write conditional on nothing having changed in between.
355
461
  */
356
- version: Version;
462
+ version: Version_VtaV0_1;
357
463
  /**
358
464
  * The stored JSON, in whatever shape the owning application chose. Any JSON value, including `null`. The maintainer neither validates nor interprets it. Absent when this is a tombstone or a metadata-only view — see this definition's description for why that is not the same as a null value.
359
465
  */
@@ -450,6 +556,38 @@ export interface AttachmentRef_VaultV0_3 {
450
556
  */
451
557
  contentType?: string;
452
558
  }
559
+ /**
560
+ * One atomic fact a holder keeps about themselves. Several attributes MAY share a `type` — three phone numbers, a legal name and a preferred name — which is why `attributeId` is the identity of a fact and `type` is not. The pool is flat and unordered; ordering is a profile's concern.
561
+ */
562
+ export interface Attribute {
563
+ attributeId: Ulid;
564
+ type: ClaimType;
565
+ valueType: ValueType;
566
+ /**
567
+ * The fact itself, agreeing with `valueType`. Encrypted at rest by the maintainer.
568
+ *
569
+ * OPTIONAL, and its absence is meaningful in two distinct situations a consumer MUST NOT conflate: the caller asked for a metadata-only view (`list` without `includeValues`, which is the DEFAULT and the common case), or a credential-backed value could not be re-derived. `stale` tells them apart. Requiring this member would make the default listing unrepresentable — a maintainer would have to choose between disclosing every value in bulk and emitting a non-conformant response.
570
+ */
571
+ value?: {
572
+ [k: string]: unknown | undefined;
573
+ };
574
+ /**
575
+ * The holder's own words for this attribute, shown in a picker. Never disclosed to a verifier; it is a note to self.
576
+ */
577
+ label?: string;
578
+ provenance: Provenance;
579
+ /**
580
+ * Present and true when a `credentialBacked` value could not be re-derived. `staleReason` says why. A maintainer MUST refuse to disclose a stale attribute.
581
+ */
582
+ stale?: boolean;
583
+ /**
584
+ * Why re-derivation failed. Present only alongside `stale`.
585
+ */
586
+ staleReason?: "revoked" | "expired" | "archived" | "deleted" | "notFound";
587
+ version: Version_PersonaV0_1;
588
+ createdAt?: string;
589
+ updatedAt: string;
590
+ }
453
591
  /**
454
592
  * One record in the mediator's privileged-change audit log: one change, by one actor, at one time.
455
593
  */
@@ -908,6 +1046,46 @@ export interface Service_DeviceV0_2 {
908
1046
  kind: "service";
909
1047
  serviceKind: "mediator" | "aiAgent" | "daemon";
910
1048
  }
1049
+ /**
1050
+ * What a peer disclosed, as received. Structurally the same claim set a holder composes and presents — a profile and a contact card are one schema seen from two sides — which is why a maintainer validates both with the same code and a consumer renders both with the same view.
1051
+ */
1052
+ export interface ContactDocument {
1053
+ /**
1054
+ * The DID that published the document, as it appeared. Normally a pairwise identifier, so it names the relationship rather than the person.
1055
+ */
1056
+ publisher?: string;
1057
+ /**
1058
+ * The publisher's own monotonic counter, if they supplied one. Advisory: it orders the publisher's revisions relative to each other and MUST NOT be trusted to order them against anything else.
1059
+ */
1060
+ cardVersion?: number;
1061
+ /**
1062
+ * @maxItems 256
1063
+ */
1064
+ claims: {
1065
+ type: ClaimType;
1066
+ valueType: ValueType;
1067
+ value: unknown;
1068
+ /**
1069
+ * As asserted by the publisher. A recipient MUST NOT treat a claimed credentialBacked provenance as verified — it is a statement about what the publisher says backs the claim, and verification is a separate act against the credential itself.
1070
+ */
1071
+ provenance?: Provenance;
1072
+ }[];
1073
+ }
1074
+ /**
1075
+ * One received version of a contact's document. Revisions exist because an address book that silently replaces a payment address is a phishing surface, and one that says what changed and when is a defence.
1076
+ */
1077
+ export interface ContactRevision {
1078
+ /**
1079
+ * Monotonic per contact, assigned by the recipient. Not the publisher's cardVersion — a recipient counts what it received, which is the only sequence it can vouch for.
1080
+ */
1081
+ rev: number;
1082
+ receivedAt: string;
1083
+ /**
1084
+ * When a later revision replaced this one. Null for the current revision.
1085
+ */
1086
+ supersededAt?: string | null;
1087
+ document?: ContactDocument;
1088
+ }
911
1089
  /**
912
1090
  * Per-context restrictions the VTA enforces. Every member is optional, and **absence means unrestricted, not empty** — a policy that omits `presentableTypes` permits every type, while one that sets it to `[]` permits none. The two are opposite instructions and a consumer MUST NOT collapse them.
913
1091
  */
@@ -2118,6 +2296,29 @@ export interface PolicyModule {
2118
2296
  updatedAt: string;
2119
2297
  ext?: Ext;
2120
2298
  }
2299
+ /**
2300
+ * A named projection over the pool. Agent-scoped, like the pool it draws from. `entries` is ordered and the order is display order.
2301
+ */
2302
+ export interface Profile {
2303
+ profileId: Ulid;
2304
+ /**
2305
+ * The holder's name for this profile — "Work", "Gaming". Not disclosed.
2306
+ */
2307
+ name: string;
2308
+ /**
2309
+ * @maxItems 256
2310
+ */
2311
+ entries: ProfileEntry[];
2312
+ /**
2313
+ * Vault identifiers of credentials associated with this profile as INVENTORY, distinct from the evidence relationship a `credentialBacked` attribute expresses. The two answer different questions — what can this persona prove, versus what backs this specific claim — and a consumer MUST NOT read one as the other.
2314
+ *
2315
+ * @maxItems 256
2316
+ */
2317
+ credentialRefs?: string[];
2318
+ version: Version_PersonaV0_1;
2319
+ createdAt?: string;
2320
+ updatedAt: string;
2321
+ }
2121
2322
  /**
2122
2323
  * Server-issued options for `navigator.credentials.create({ publicKey: ... })`. Mirrors the WebAuthn Level 2 `PublicKeyCredentialCreationOptions` dictionary; binary fields are base64url-encoded strings (rather than ArrayBuffers) so the value is JSON-safe over the wire.
2123
2324
  */
@@ -3166,7 +3367,7 @@ export interface WriteResult {
3166
3367
  /**
3167
3368
  * The new version, on `written`.
3168
3369
  */
3169
- version?: Version;
3370
+ version?: Version_VtaV0_1;
3170
3371
  /**
3171
3372
  * On `written`: true when no live record existed at the address beforehand.
3172
3373
  */
@@ -3174,7 +3375,7 @@ export interface WriteResult {
3174
3375
  /**
3175
3376
  * On `conflict`: the version the maintainer actually holds. Absent when the conflict is that no record exists (`expectedVersion` was positive and the address is empty).
3176
3377
  */
3177
- currentVersion?: Version;
3378
+ currentVersion?: Version_VtaV0_1;
3178
3379
  /**
3179
3380
  * On `conflict`: the value the maintainer actually holds, returned WITH the rejection rather than left for the caller to re-read. A bare rejection has no fixed point under contention — between the rejection and the re-read the record can change again — so returning the winner's view removes the race rather than narrowing it. Absent when `currentDeleted` is true or no record exists.
3180
3381
  */