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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
use auths_core::error::AuthsErrorInfo;
use thiserror::Error;
/// Errors from organization member management workflows.
///
/// Usage:
/// ```ignore
/// match result {
/// Err(OrgError::AdminNotFound { .. }) => { /* 403 Forbidden */ }
/// Err(OrgError::MemberNotFound { .. }) => { /* 404 Not Found */ }
/// Err(e) => return Err(e.into()),
/// Ok(att) => { /* proceed */ }
/// }
/// ```
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum OrgError {
/// No admin matching the given public key was found in the organization.
#[error("no admin with the given public key found in organization '{org}'")]
AdminNotFound {
/// The organization identifier.
org: String,
},
/// The specified member was not found in the organization.
#[error("member '{did}' not found in organization '{org}'")]
MemberNotFound {
/// The organization identifier.
org: String,
/// The DID of the member that was not found.
did: String,
},
/// The member has already been revoked.
#[error("member '{did}' is already revoked")]
AlreadyRevoked {
/// The DID of the already-revoked member.
did: String,
},
/// The capability string could not be parsed.
#[error("invalid capability '{cap}': {reason}")]
InvalidCapability {
/// The invalid capability string.
cap: String,
/// The reason parsing failed.
reason: String,
},
/// The organization DID is malformed.
#[error("invalid organization DID: {0}")]
InvalidDid(String),
/// The hex-encoded public key is invalid.
#[error("invalid public key: {0}")]
InvalidPublicKey(String),
/// A signing operation failed while creating or revoking an attestation.
#[error("signing error: {0}")]
Signing(String),
/// The identity could not be loaded from storage.
#[error("identity error: {0}")]
Identity(String),
/// A key storage operation failed.
#[error("key storage error: {0}")]
KeyStorage(String),
/// A storage operation failed.
#[error("storage error: {0}")]
Storage(#[source] auths_id::storage::registry::backend::RegistryError),
/// KEL anchoring failed.
#[error("anchor error: {0}")]
Anchor(#[from] auths_id::keri::AnchorError),
/// The organization's controller threshold is `kt≥2` (multi-signature).
/// KERI-native member delegation currently anchors single-author (`kt=1`)
/// interaction events only; multi-sig org anchoring is a tracked follow-up.
#[error(
"organization '{org}' uses a multi-signature controller (kt≥2); KERI-native member delegation requires a single-signature (kt=1) org"
)]
OrgThresholdDelegationUnsupported {
/// The organization identifier.
org: String,
},
/// A key already exists under the requested member alias — minting a member
/// there would clobber an existing delegated key. Choose a fresh alias.
#[error("a member key already exists under alias '{alias}'")]
MemberKeyExists {
/// The keychain alias already in use.
alias: String,
},
/// The supplied member identity is not a delegated identifier of this org —
/// its delegated inception (`dip`) does not name the org as delegator, so the
/// org cannot off-board it. Fail closed.
#[error("member '{did}' is not a delegated identifier of organization '{org}'")]
MemberNotDelegable {
/// The member's `did:keri:`.
did: String,
/// The organization identifier.
org: String,
},
/// A cryptographic operation failed (e.g. resolving the org key's curve).
#[error("crypto error: {0}")]
CryptoError(#[source] auths_core::AgentError),
/// Authoring or anchoring the member's delegated identifier failed.
#[error("member delegation failed: {0}")]
Delegation(#[source] auths_id::error::InitError),
/// An identity already exists where the org would be created — refusing to
/// clobber it.
#[error("an identity already exists at {location}; refusing to create an organization over it")]
IdentityExists {
/// Where the existing identity was found (repository path or registry).
location: String,
},
/// Initializing the organization's KERI identity failed.
#[error("failed to initialize organization identity: {0}")]
IdentityInit(#[source] auths_id::error::InitError),
/// Creating the organization's admin self-attestation failed.
#[error("failed to create admin attestation: {0}")]
Attestation(#[source] auths_verifier::error::AttestationError),
/// An air-gapped bundle event failed its self-addressing integrity check —
/// recomputing the SAID did not match the stored `d` (the bundle was tampered).
#[error("bundle integrity failure for '{id}': {reason}")]
BundleIntegrity {
/// The identifier whose KEL failed integrity.
id: String,
/// Why integrity failed.
reason: String,
},
/// The bundle delegates a member on the org KEL but omits that member's own KEL —
/// the bundle is incomplete and cannot be verified. Fail closed.
#[error("bundle is missing the KEL for delegated member '{member}'")]
BundleMissingMemberKel {
/// The member's `did:keri:`.
member: String,
},
/// The queried member has no delegation seal in the org KEL — the org never
/// delegated it, so there is no authority to verify. Fail closed.
#[error("member '{member}' has no delegation seal in the org KEL")]
BundleMissingDelegatorSeal {
/// The member's `did:keri:`.
member: String,
},
/// The supplied org policy did not parse or compile (invalid JSON, an invalid
/// DID/capability/glob, or it exceeds the policy size/complexity bounds). A
/// policy that does not compile is never anchored — fail closed.
#[error("invalid org policy: {reason}")]
PolicyCompile {
/// The compile/parse failure(s).
reason: String,
},
/// The org KEL anchors a policy hash but its content-addressed blob is missing —
/// the policy cannot be loaded, so authority cannot be evaluated. Fail closed.
#[error("org policy blob for hash '{hash}' is missing from storage")]
PolicyBlobMissing {
/// The policy source-hash anchored on the org KEL.
hash: String,
},
/// The loaded policy blob does not hash to the value the org KEL committed — the
/// blob was tampered with after anchoring. Fail closed.
#[error(
"org policy integrity failure: KEL committed hash '{expected}' but the stored blob hashes to '{actual}'"
)]
PolicyIntegrity {
/// The hash anchored on the org KEL.
expected: String,
/// The recomputed hash of the stored blob.
actual: String,
},
/// A delegation chain walk followed a `di` link back to an identifier already on
/// the chain — a cyclic/malformed delegation. Fail closed (no infinite loop).
#[error("delegation chain cycle detected at '{did}'")]
ChainCycle {
/// The identifier the cycle returned to.
did: String,
},
/// A delegation chain exceeds the maximum hop depth the walker will follow.
#[error("delegation chain exceeds the maximum depth of {max} hops")]
ChainTooDeep {
/// The maximum number of hops allowed.
max: u32,
},
/// A delegation chain names a delegator/identifier whose KEL is absent from
/// storage — the chain cannot be reconstructed. Fail closed.
#[error("delegation chain is broken: no KEL found for '{did}'")]
ChainBrokenHop {
/// The identifier whose KEL is missing.
did: String,
},
}
impl AuthsErrorInfo for OrgError {
fn error_code(&self) -> &'static str {
match self {
Self::AdminNotFound { .. } => "AUTHS-E5601",
Self::MemberNotFound { .. } => "AUTHS-E5602",
Self::AlreadyRevoked { .. } => "AUTHS-E5603",
Self::InvalidCapability { .. } => "AUTHS-E5604",
Self::InvalidDid(_) => "AUTHS-E5605",
Self::InvalidPublicKey(_) => "AUTHS-E5606",
Self::Signing(_) => "AUTHS-E5607",
Self::Identity(_) => "AUTHS-E5608",
Self::KeyStorage(_) => "AUTHS-E5609",
Self::Storage(_) => "AUTHS-E5610",
Self::Anchor(_) => "AUTHS-E5611",
Self::OrgThresholdDelegationUnsupported { .. } => "AUTHS-E5612",
Self::MemberKeyExists { .. } => "AUTHS-E5613",
Self::MemberNotDelegable { .. } => "AUTHS-E5618",
Self::CryptoError(e) => e.error_code(),
Self::Delegation(_) => "AUTHS-E5614",
Self::IdentityExists { .. } => "AUTHS-E5615",
Self::IdentityInit(_) => "AUTHS-E5616",
Self::Attestation(_) => "AUTHS-E5617",
Self::BundleIntegrity { .. } => "AUTHS-E5619",
Self::BundleMissingMemberKel { .. } => "AUTHS-E5620",
Self::BundleMissingDelegatorSeal { .. } => "AUTHS-E5621",
Self::PolicyCompile { .. } => "AUTHS-E5622",
Self::PolicyBlobMissing { .. } => "AUTHS-E5623",
Self::PolicyIntegrity { .. } => "AUTHS-E5624",
Self::ChainCycle { .. } => "AUTHS-E5625",
Self::ChainTooDeep { .. } => "AUTHS-E5626",
Self::ChainBrokenHop { .. } => "AUTHS-E5627",
}
}
fn suggestion(&self) -> Option<&'static str> {
match self {
Self::AdminNotFound { .. } => {
Some("Verify you are using the correct admin key for this organization")
}
Self::MemberNotFound { .. } => {
Some("Run `auths org list-members` to see current members")
}
Self::AlreadyRevoked { .. } => {
Some("This member has already been revoked from the organization")
}
Self::InvalidCapability { .. } => {
Some("Use a valid capability (e.g., 'sign_commit', 'manage_members', 'admin')")
}
Self::InvalidDid(_) => Some("Organization DIDs must be valid did:keri identifiers"),
Self::InvalidPublicKey(_) => Some("Public keys must be hex-encoded Ed25519 keys"),
Self::Signing(_) => {
Some("The signing operation failed; check your key access with `auths key list`")
}
Self::Identity(_) => {
Some("Failed to load identity; run `auths id show` to check identity status")
}
Self::KeyStorage(_) => {
Some("Failed to access key storage; run `auths doctor` to diagnose")
}
Self::Storage(_) => {
Some("Failed to access organization storage; check repository permissions")
}
Self::Anchor(_) => Some("KEL anchoring failed; check identity and registry state"),
Self::OrgThresholdDelegationUnsupported { .. } => Some(
"Multi-signature org anchoring is not yet supported; use a single-signature (kt=1) org",
),
Self::MemberKeyExists { .. } => Some(
"Choose a different member alias; run `auths org list-members` to see existing members",
),
Self::MemberNotDelegable { .. } => Some(
"The member must first incept a delegated identity naming this org as delegator (pairing) before it can be off-boarded",
),
Self::CryptoError(e) => e.suggestion(),
Self::Delegation(_) => Some(
"The member delegation could not be authored or anchored; check the org identity",
),
Self::IdentityExists { .. } => Some(
"An identity already exists here; use a fresh repository path to create a new organization",
),
Self::IdentityInit(_) => {
Some("Failed to initialize the org identity; check key access and repository state")
}
Self::Attestation(_) => Some(
"Failed to sign the admin attestation; check your key access with `auths key list`",
),
Self::BundleIntegrity { .. } => Some(
"The bundle was modified after it was produced; obtain a fresh, untampered bundle",
),
Self::BundleMissingMemberKel { .. } | Self::BundleMissingDelegatorSeal { .. } => {
Some("The bundle is incomplete; re-produce it with `auths org bundle`")
}
Self::PolicyCompile { .. } => Some(
"Fix the policy JSON (a serialized `Expr`); see `auths org policy show` for the current policy",
),
Self::PolicyBlobMissing { .. } => {
Some("The policy blob is missing; re-anchor it with `auths org policy set`")
}
Self::PolicyIntegrity { .. } => Some(
"The stored policy was modified after anchoring; re-anchor a trusted policy with `auths org policy set`",
),
Self::ChainCycle { .. } => {
Some("The delegation chain is malformed (a cycle); inspect the identifiers' KELs")
}
Self::ChainTooDeep { .. } => {
Some("The delegation chain is too deep; reduce delegation nesting")
}
Self::ChainBrokenHop { .. } => Some(
"A KEL in the delegation chain is missing; ensure all delegators' KELs are present",
),
}
}
}