1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
//! Member-side membership-credential exchange (`members/*`).
//!
//! Membership between a persona DID and a VTC is a **pair of VMCs**: the VTC
//! issues a `MembershipCredential` to the member at admission
//! (community → member), and the member issues one back to the VTC
//! (member → community), so each side holds a credential asserting the other's
//! membership edge. This family carries the **full reciprocal VMC** and lets
//! it be (re)exchanged at any point — at join time with
//! [`MemberVmcBody::request_id`] set (which also closes the approved join
//! request; the retired `join-requests/accept` semantics), or unprompted /
//! on request later:
//!
//! - [`MEMBER_REQUEST_VMC_TYPE`] — VTC → member: "please issue + send your VMC".
//! Admin-triggered from the VTC; delivered over DIDComm to the member's agent.
//! - [`MEMBER_VMC_TYPE`] — member → VTC: the member-issued VMC (a Data-Integrity
//! VC whose `issuer` is the member and whose `credentialSubject.id` is the
//! community DID). The VTC verifies the proof + binding and stores it.
//! - [`MEMBER_VMC_RESPONSE_TYPE`] — VTC → member: a receipt acknowledging the
//! stored VMC.
use ;
use Value;
/// The `type` array tag a member-issued membership credential must carry
/// (alongside `VerifiableCredential`). Same credential type the VTC issues for
/// its half of the pair — the direction is given by `issuer` /
/// `credentialSubject.id`, not the type.
///
/// The canonical DTG tag, exactly as `dtg-credentials` emits it
/// (`DTGCredentialType::Membership`) and as the VTC's own issuance stamps it.
///
/// The name previously carried a `VERIFIABLE_` prefix that the *value* never
/// had. That gap is not cosmetic: it is how a `Verifiable`-prefixed type tag
/// came to be hand-rolled into the recognition path, where it matched nothing
/// any VTC issues and silently
/// broke cross-community recognition for every real presentation
/// (OpenVTC/verifiable-trust-infrastructure#1062). A constant whose name
/// disagrees with its value invites exactly that.
pub const MEMBERSHIP_CREDENTIAL_TYPE: &str = "MembershipCredential";
/// Wire `type` tag of a VAC (Verifiable Authority Credential) — the credential
/// a community role is conferred in (`authority.actions` `["role:<name>"]` at
/// the community's DID), per DTG Credentials §VAC.
///
/// Sibling of [`MEMBERSHIP_CREDENTIAL_TYPE`], and here for the same reason:
/// the recognition path once had its role-credential type as a bare literal,
/// spelled wrongly, with nothing to compare it against.
pub const AUTHORITY_CREDENTIAL_TYPE: &str = "AuthorityCredential";
/// Wire `type` tag of a VSC (Verifiable Statement Credential), per DTG
/// Credentials §VSC. A statement's meaning is its `credentialSubject.predicate`,
/// never a subtype: every endorsement, witness attestation and vetting
/// statement carries this one type.
pub const STATEMENT_CREDENTIAL_TYPE: &str = "StatementCredential";
/// `vtc/members/update`'s refusal of an administrator's `publishConsent: true`
/// for a member who has not consented to trust-registry publication.
///
/// That consent is the member's own decision, given with `registryConsent` on
/// `vtc/join-requests/submit` (and again on a rejoin). An administrator may
/// withdraw it — a takedown — but never grant it. Re-stating the current value
/// is a no-op, not a refusal.
///
/// A literal, like `join_requests::JOIN_REQUEST_SUBMIT_ERR_REQUEST_ALREADY_OPEN`:
/// `vtc/members/update/0.1` declares no code for it, so it is minted under the
/// task's own namespace (Trust Tasks SPEC §8.5). A client that does not
/// recognise it treats it as `taskFailed`. The VTC's error-code census lists it
/// as consumer-minted and fails the day the specification declares it.
pub const MEMBER_UPDATE_ERR_CONSENT_GRANT_FORBIDDEN: &str =
"vtc/members/update:consentGrantForbidden";
/// VTC → member: request that the member issue and send their reciprocal VMC.
pub const MEMBER_REQUEST_VMC_TYPE: &str = "https://trusttasks.org/spec/vtc/members/request-vmc/0.1";
/// Member → VTC: a member-issued [`MEMBERSHIP_CREDENTIAL_TYPE`] VMC,
/// the member → community half of the membership pair.
pub const MEMBER_VMC_TYPE: &str = "https://trusttasks.org/spec/vtc/members/vmc/0.1";
/// VTC → member: receipt acknowledging a stored member VMC. The `#response`
/// variant of [`MEMBER_VMC_TYPE`].
pub const MEMBER_VMC_RESPONSE_TYPE: &str =
"https://trusttasks.org/spec/vtc/members/vmc/0.1#response";
/// VTC → member: **unsolicited** notice that the community removed them.
///
/// Distinct from the self-remove receipt
/// ([`crate::protocols::join_requests::MEMBER_SELF_REMOVE_RECEIPT_TYPE`]) in the
/// way that matters: a receipt answers a request the member made and is
/// correlated to it, so the member is already waiting for it. This answers
/// nothing — the member did not ask, is not waiting, and may be offline. Sending
/// one for a member-initiated departure would tell somebody who chose to leave
/// that they were removed.
pub const MEMBER_REMOVAL_NOTICE_TYPE: &str =
"https://trusttasks.org/spec/vtc/members/removal-notice/0.1";
/// Which removal happened, as carried in [`RemovalNoticeBody::code`].
///
/// Two variants rather than one `removed` flag because they differ in what
/// recourse the member has: an administrator's removal ran the community's
/// removal policy, a super-administrator's purge deliberately skipped it.
/// Body of a [`MEMBER_REMOVAL_NOTICE_TYPE`] notice.
///
/// Every field except `reason` is required, because a notice that omits any of
/// them fails to answer one of the questions a removed member has: what
/// happened, to what, when, and on whose say-so.
/// Body of a [`MEMBER_REQUEST_VMC_TYPE`] request. The member should issue a VMC
/// whose `credentialSubject.id` is `community_did` and send it back as a
/// [`MEMBER_VMC_TYPE`] message.
/// Body of a [`MEMBER_VMC_TYPE`] submission: the member-issued VMC verbatim.
/// `vc.issuer` is the member DID (the authcrypt sender / DI-proof signer) and
/// `vc.credentialSubject.id` is the community DID.
/// Body of a [`MEMBER_VMC_RESPONSE_TYPE`] receipt.