Skip to main content

auths_sdk/domains/org/
error.rs

1use auths_core::error::AuthsErrorInfo;
2use thiserror::Error;
3
4/// Errors from organization member management workflows.
5///
6/// Usage:
7/// ```ignore
8/// match result {
9///     Err(OrgError::AdminNotFound { .. }) => { /* 403 Forbidden */ }
10///     Err(OrgError::MemberNotFound { .. }) => { /* 404 Not Found */ }
11///     Err(e) => return Err(e.into()),
12///     Ok(att) => { /* proceed */ }
13/// }
14/// ```
15#[derive(Debug, Error)]
16#[non_exhaustive]
17pub enum OrgError {
18    /// No admin matching the given public key was found in the organization.
19    #[error("no admin with the given public key found in organization '{org}'")]
20    AdminNotFound {
21        /// The organization identifier.
22        org: String,
23    },
24
25    /// The specified member was not found in the organization.
26    #[error("member '{did}' not found in organization '{org}'")]
27    MemberNotFound {
28        /// The organization identifier.
29        org: String,
30        /// The DID of the member that was not found.
31        did: String,
32    },
33
34    /// The member has already been revoked.
35    #[error("member '{did}' is already revoked")]
36    AlreadyRevoked {
37        /// The DID of the already-revoked member.
38        did: String,
39    },
40
41    /// The capability string could not be parsed.
42    #[error("invalid capability '{cap}': {reason}")]
43    InvalidCapability {
44        /// The invalid capability string.
45        cap: String,
46        /// The reason parsing failed.
47        reason: String,
48    },
49
50    /// The organization DID is malformed.
51    #[error("invalid organization DID: {0}")]
52    InvalidDid(String),
53
54    /// The hex-encoded public key is invalid.
55    #[error("invalid public key: {0}")]
56    InvalidPublicKey(String),
57
58    /// A signing operation failed while creating or revoking an attestation.
59    #[error("signing error: {0}")]
60    Signing(String),
61
62    /// The identity could not be loaded from storage.
63    #[error("identity error: {0}")]
64    Identity(String),
65
66    /// A key storage operation failed.
67    #[error("key storage error: {0}")]
68    KeyStorage(String),
69
70    /// A storage operation failed.
71    #[error("storage error: {0}")]
72    Storage(#[source] auths_id::storage::registry::backend::RegistryError),
73
74    /// KEL anchoring failed.
75    #[error("anchor error: {0}")]
76    Anchor(#[from] auths_id::keri::AnchorError),
77
78    /// The organization's controller threshold is `kt≥2` (multi-signature).
79    /// KERI-native member delegation currently anchors single-author (`kt=1`)
80    /// interaction events only; multi-sig org anchoring is a tracked follow-up.
81    #[error(
82        "organization '{org}' uses a multi-signature controller (kt≥2); KERI-native member delegation requires a single-signature (kt=1) org"
83    )]
84    OrgThresholdDelegationUnsupported {
85        /// The organization identifier.
86        org: String,
87    },
88
89    /// A key already exists under the requested member alias — minting a member
90    /// there would clobber an existing delegated key. Choose a fresh alias.
91    #[error("a member key already exists under alias '{alias}'")]
92    MemberKeyExists {
93        /// The keychain alias already in use.
94        alias: String,
95    },
96
97    /// The supplied member identity is not a delegated identifier of this org —
98    /// its delegated inception (`dip`) does not name the org as delegator, so the
99    /// org cannot off-board it. Fail closed.
100    #[error("member '{did}' is not a delegated identifier of organization '{org}'")]
101    MemberNotDelegable {
102        /// The member's `did:keri:`.
103        did: String,
104        /// The organization identifier.
105        org: String,
106    },
107
108    /// A cryptographic operation failed (e.g. resolving the org key's curve).
109    #[error("crypto error: {0}")]
110    CryptoError(#[source] auths_core::AgentError),
111
112    /// Authoring or anchoring the member's delegated identifier failed.
113    #[error("member delegation failed: {0}")]
114    Delegation(#[source] auths_id::error::InitError),
115
116    /// An identity already exists where the org would be created — refusing to
117    /// clobber it.
118    #[error("an identity already exists at {location}; refusing to create an organization over it")]
119    IdentityExists {
120        /// Where the existing identity was found (repository path or registry).
121        location: String,
122    },
123
124    /// Initializing the organization's KERI identity failed.
125    #[error("failed to initialize organization identity: {0}")]
126    IdentityInit(#[source] auths_id::error::InitError),
127
128    /// Creating the organization's admin self-attestation failed.
129    #[error("failed to create admin attestation: {0}")]
130    Attestation(#[source] auths_verifier::error::AttestationError),
131
132    /// An air-gapped bundle event failed its self-addressing integrity check —
133    /// recomputing the SAID did not match the stored `d` (the bundle was tampered).
134    #[error("bundle integrity failure for '{id}': {reason}")]
135    BundleIntegrity {
136        /// The identifier whose KEL failed integrity.
137        id: String,
138        /// Why integrity failed.
139        reason: String,
140    },
141
142    /// The bundle delegates a member on the org KEL but omits that member's own KEL —
143    /// the bundle is incomplete and cannot be verified. Fail closed.
144    #[error("bundle is missing the KEL for delegated member '{member}'")]
145    BundleMissingMemberKel {
146        /// The member's `did:keri:`.
147        member: String,
148    },
149
150    /// The queried member has no delegation seal in the org KEL — the org never
151    /// delegated it, so there is no authority to verify. Fail closed.
152    #[error("member '{member}' has no delegation seal in the org KEL")]
153    BundleMissingDelegatorSeal {
154        /// The member's `did:keri:`.
155        member: String,
156    },
157
158    /// The supplied org policy did not parse or compile (invalid JSON, an invalid
159    /// DID/capability/glob, or it exceeds the policy size/complexity bounds). A
160    /// policy that does not compile is never anchored — fail closed.
161    #[error("invalid org policy: {reason}")]
162    PolicyCompile {
163        /// The compile/parse failure(s).
164        reason: String,
165    },
166
167    /// The org KEL anchors a policy hash but its content-addressed blob is missing —
168    /// the policy cannot be loaded, so authority cannot be evaluated. Fail closed.
169    #[error("org policy blob for hash '{hash}' is missing from storage")]
170    PolicyBlobMissing {
171        /// The policy source-hash anchored on the org KEL.
172        hash: String,
173    },
174
175    /// The loaded policy blob does not hash to the value the org KEL committed — the
176    /// blob was tampered with after anchoring. Fail closed.
177    #[error(
178        "org policy integrity failure: KEL committed hash '{expected}' but the stored blob hashes to '{actual}'"
179    )]
180    PolicyIntegrity {
181        /// The hash anchored on the org KEL.
182        expected: String,
183        /// The recomputed hash of the stored blob.
184        actual: String,
185    },
186
187    /// A delegation chain walk followed a `di` link back to an identifier already on
188    /// the chain — a cyclic/malformed delegation. Fail closed (no infinite loop).
189    #[error("delegation chain cycle detected at '{did}'")]
190    ChainCycle {
191        /// The identifier the cycle returned to.
192        did: String,
193    },
194
195    /// A delegation chain exceeds the maximum hop depth the walker will follow.
196    #[error("delegation chain exceeds the maximum depth of {max} hops")]
197    ChainTooDeep {
198        /// The maximum number of hops allowed.
199        max: u32,
200    },
201
202    /// A delegation chain names a delegator/identifier whose KEL is absent from
203    /// storage — the chain cannot be reconstructed. Fail closed.
204    #[error("delegation chain is broken: no KEL found for '{did}'")]
205    ChainBrokenHop {
206        /// The identifier whose KEL is missing.
207        did: String,
208    },
209}
210
211impl AuthsErrorInfo for OrgError {
212    fn error_code(&self) -> &'static str {
213        match self {
214            Self::AdminNotFound { .. } => "AUTHS-E5601",
215            Self::MemberNotFound { .. } => "AUTHS-E5602",
216            Self::AlreadyRevoked { .. } => "AUTHS-E5603",
217            Self::InvalidCapability { .. } => "AUTHS-E5604",
218            Self::InvalidDid(_) => "AUTHS-E5605",
219            Self::InvalidPublicKey(_) => "AUTHS-E5606",
220            Self::Signing(_) => "AUTHS-E5607",
221            Self::Identity(_) => "AUTHS-E5608",
222            Self::KeyStorage(_) => "AUTHS-E5609",
223            Self::Storage(_) => "AUTHS-E5610",
224            Self::Anchor(_) => "AUTHS-E5611",
225            Self::OrgThresholdDelegationUnsupported { .. } => "AUTHS-E5612",
226            Self::MemberKeyExists { .. } => "AUTHS-E5613",
227            Self::MemberNotDelegable { .. } => "AUTHS-E5618",
228            Self::CryptoError(e) => e.error_code(),
229            Self::Delegation(_) => "AUTHS-E5614",
230            Self::IdentityExists { .. } => "AUTHS-E5615",
231            Self::IdentityInit(_) => "AUTHS-E5616",
232            Self::Attestation(_) => "AUTHS-E5617",
233            Self::BundleIntegrity { .. } => "AUTHS-E5619",
234            Self::BundleMissingMemberKel { .. } => "AUTHS-E5620",
235            Self::BundleMissingDelegatorSeal { .. } => "AUTHS-E5621",
236            Self::PolicyCompile { .. } => "AUTHS-E5622",
237            Self::PolicyBlobMissing { .. } => "AUTHS-E5623",
238            Self::PolicyIntegrity { .. } => "AUTHS-E5624",
239            Self::ChainCycle { .. } => "AUTHS-E5625",
240            Self::ChainTooDeep { .. } => "AUTHS-E5626",
241            Self::ChainBrokenHop { .. } => "AUTHS-E5627",
242        }
243    }
244
245    fn suggestion(&self) -> Option<&'static str> {
246        match self {
247            Self::AdminNotFound { .. } => {
248                Some("Verify you are using the correct admin key for this organization")
249            }
250            Self::MemberNotFound { .. } => {
251                Some("Run `auths org list-members` to see current members")
252            }
253            Self::AlreadyRevoked { .. } => {
254                Some("This member has already been revoked from the organization")
255            }
256            Self::InvalidCapability { .. } => {
257                Some("Use a valid capability (e.g., 'sign_commit', 'manage_members', 'admin')")
258            }
259            Self::InvalidDid(_) => Some("Organization DIDs must be valid did:keri identifiers"),
260            Self::InvalidPublicKey(_) => Some("Public keys must be hex-encoded Ed25519 keys"),
261            Self::Signing(_) => {
262                Some("The signing operation failed; check your key access with `auths key list`")
263            }
264            Self::Identity(_) => {
265                Some("Failed to load identity; run `auths id show` to check identity status")
266            }
267            Self::KeyStorage(_) => {
268                Some("Failed to access key storage; run `auths doctor` to diagnose")
269            }
270            Self::Storage(_) => {
271                Some("Failed to access organization storage; check repository permissions")
272            }
273            Self::Anchor(_) => Some("KEL anchoring failed; check identity and registry state"),
274            Self::OrgThresholdDelegationUnsupported { .. } => Some(
275                "Multi-signature org anchoring is not yet supported; use a single-signature (kt=1) org",
276            ),
277            Self::MemberKeyExists { .. } => Some(
278                "Choose a different member alias; run `auths org list-members` to see existing members",
279            ),
280            Self::MemberNotDelegable { .. } => Some(
281                "The member must first incept a delegated identity naming this org as delegator (pairing) before it can be off-boarded",
282            ),
283            Self::CryptoError(e) => e.suggestion(),
284            Self::Delegation(_) => Some(
285                "The member delegation could not be authored or anchored; check the org identity",
286            ),
287            Self::IdentityExists { .. } => Some(
288                "An identity already exists here; use a fresh repository path to create a new organization",
289            ),
290            Self::IdentityInit(_) => {
291                Some("Failed to initialize the org identity; check key access and repository state")
292            }
293            Self::Attestation(_) => Some(
294                "Failed to sign the admin attestation; check your key access with `auths key list`",
295            ),
296            Self::BundleIntegrity { .. } => Some(
297                "The bundle was modified after it was produced; obtain a fresh, untampered bundle",
298            ),
299            Self::BundleMissingMemberKel { .. } | Self::BundleMissingDelegatorSeal { .. } => {
300                Some("The bundle is incomplete; re-produce it with `auths org bundle`")
301            }
302            Self::PolicyCompile { .. } => Some(
303                "Fix the policy JSON (a serialized `Expr`); see `auths org policy show` for the current policy",
304            ),
305            Self::PolicyBlobMissing { .. } => {
306                Some("The policy blob is missing; re-anchor it with `auths org policy set`")
307            }
308            Self::PolicyIntegrity { .. } => Some(
309                "The stored policy was modified after anchoring; re-anchor a trusted policy with `auths org policy set`",
310            ),
311            Self::ChainCycle { .. } => {
312                Some("The delegation chain is malformed (a cycle); inspect the identifiers' KELs")
313            }
314            Self::ChainTooDeep { .. } => {
315                Some("The delegation chain is too deep; reduce delegation nesting")
316            }
317            Self::ChainBrokenHop { .. } => Some(
318                "A KEL in the delegation chain is missing; ensure all delegators' KELs are present",
319            ),
320        }
321    }
322}