Skip to main content

auths_sdk/
error.rs

1use thiserror::Error;
2
3/// Wrapper for storage errors originating from `auths-id` traits.
4/// Preserves the full error display string from the underlying storage layer.
5///
6/// Usage:
7/// ```ignore
8/// storage.save(data)
9///     .map_err(|e| SetupError::StorageError(SdkStorageError::OperationFailed(e.to_string())))?;
10/// ```
11#[derive(Debug, Error)]
12pub enum SdkStorageError {
13    /// A storage operation failed with the given message.
14    #[error("storage operation failed: {0}")]
15    OperationFailed(String),
16}
17
18/// Errors from identity setup operations (developer, CI, agent).
19///
20/// Usage:
21/// ```ignore
22/// match sdk_result {
23///     Err(SetupError::IdentityAlreadyExists { did }) => { /* reuse or abort */ }
24///     Err(e) => return Err(e.into()),
25///     Ok(result) => { /* success */ }
26/// }
27/// ```
28#[derive(Debug, Error)]
29#[non_exhaustive]
30pub enum SetupError {
31    /// An identity already exists at the configured path.
32    #[error("identity already exists: {did}")]
33    IdentityAlreadyExists {
34        /// The DID of the existing identity.
35        did: String,
36    },
37
38    /// The platform keychain is unavailable or inaccessible.
39    #[error("keychain unavailable ({backend}): {reason}")]
40    KeychainUnavailable {
41        /// The keychain backend name (e.g. "macOS Keychain").
42        backend: String,
43        /// The reason the keychain is unavailable.
44        reason: String,
45    },
46
47    /// A cryptographic operation failed.
48    #[error("crypto error: {0}")]
49    CryptoError(#[source] auths_core::AgentError),
50
51    /// A storage operation failed.
52    #[error("storage error: {0}")]
53    StorageError(#[source] SdkStorageError),
54
55    /// Setting a git configuration key failed.
56    #[error("git config error: {0}")]
57    GitConfigError(String),
58
59    /// Remote registry registration failed.
60    #[error("registration failed: {0}")]
61    RegistrationFailed(#[source] RegistrationError),
62
63    /// Platform identity verification failed.
64    #[error("platform verification failed: {0}")]
65    PlatformVerificationFailed(String),
66}
67
68/// Errors from device linking and revocation operations.
69///
70/// Usage:
71/// ```ignore
72/// match link_result {
73///     Err(DeviceError::IdentityNotFound { did }) => { /* identity missing */ }
74///     Err(e) => return Err(e.into()),
75///     Ok(result) => { /* success */ }
76/// }
77/// ```
78#[derive(Debug, Error)]
79#[non_exhaustive]
80pub enum DeviceError {
81    /// The identity could not be found in storage.
82    #[error("identity not found: {did}")]
83    IdentityNotFound {
84        /// The DID that was not found.
85        did: String,
86    },
87
88    /// The device could not be found in attestation records.
89    #[error("device not found: {did}")]
90    DeviceNotFound {
91        /// The DID of the missing device.
92        did: String,
93    },
94
95    /// Attestation creation or validation failed.
96    #[error("attestation error: {0}")]
97    AttestationError(String),
98
99    /// A cryptographic operation failed.
100    #[error("crypto error: {0}")]
101    CryptoError(#[source] auths_core::AgentError),
102
103    /// A storage operation failed.
104    #[error("storage error: {0}")]
105    StorageError(#[source] SdkStorageError),
106}
107
108/// Errors from device authorization extension operations.
109///
110/// Usage:
111/// ```ignore
112/// match extend_result {
113///     Err(DeviceExtensionError::AlreadyRevoked { device_did }) => { /* already gone */ }
114///     Err(e) => return Err(e.into()),
115///     Ok(result) => { /* success */ }
116/// }
117/// ```
118#[derive(Debug, Error)]
119#[non_exhaustive]
120pub enum DeviceExtensionError {
121    /// The identity could not be found in storage.
122    #[error("identity not found")]
123    IdentityNotFound,
124
125    /// No attestation exists for the specified device.
126    #[error("no attestation found for device {device_did}")]
127    NoAttestationFound {
128        /// The DID of the device with no attestation.
129        device_did: String,
130    },
131
132    /// The device has already been revoked.
133    #[error("device {device_did} is already revoked")]
134    AlreadyRevoked {
135        /// The DID of the revoked device.
136        device_did: String,
137    },
138
139    /// Creating a new attestation failed.
140    #[error("attestation creation failed: {0}")]
141    AttestationFailed(String),
142
143    /// A storage operation failed.
144    #[error("storage error: {0}")]
145    StorageError(String),
146}
147
148/// Errors from identity rotation operations.
149///
150/// Usage:
151/// ```ignore
152/// match rotate_result {
153///     Err(RotationError::KelHistoryFailed(msg)) => { /* no prior events */ }
154///     Err(e) => return Err(e.into()),
155///     Ok(result) => { /* success */ }
156/// }
157/// ```
158#[derive(Debug, Error)]
159#[non_exhaustive]
160pub enum RotationError {
161    /// The identity was not found at the expected path.
162    #[error("identity not found at {path}")]
163    IdentityNotFound {
164        /// The filesystem path where the identity was expected.
165        path: std::path::PathBuf,
166    },
167
168    /// The requested key alias was not found in the keychain.
169    #[error("key not found: {0}")]
170    KeyNotFound(String),
171
172    /// Decrypting the key material failed (e.g. wrong passphrase).
173    #[error("key decryption failed: {0}")]
174    KeyDecryptionFailed(String),
175
176    /// Reading or validating the KEL history failed.
177    #[error("KEL history error: {0}")]
178    KelHistoryFailed(String),
179
180    /// The rotation operation failed.
181    #[error("rotation failed: {0}")]
182    RotationFailed(String),
183
184    /// KEL event was written but the new key could not be persisted to the keychain.
185    /// Recovery: re-run rotation with the same new key to replay the keychain write.
186    #[error(
187        "rotation event committed to KEL but keychain write failed — manual recovery required: {0}"
188    )]
189    PartialRotation(String),
190}
191
192/// Errors from remote registry operations.
193///
194/// Usage:
195/// ```ignore
196/// match register_result {
197///     Err(RegistrationError::AlreadyRegistered) => { /* skip */ }
198///     Err(RegistrationError::QuotaExceeded) => { /* retry later */ }
199///     Err(e) => return Err(e.into()),
200///     Ok(outcome) => { /* success */ }
201/// }
202/// ```
203#[derive(Debug, Error)]
204#[non_exhaustive]
205pub enum RegistrationError {
206    /// The identity is already registered at the target registry.
207    #[error("identity already registered at this registry")]
208    AlreadyRegistered,
209
210    /// The registration rate limit has been exceeded.
211    #[error("registration quota exceeded — try again later")]
212    QuotaExceeded,
213
214    /// A network error occurred during registration.
215    #[error("network error: {0}")]
216    NetworkError(#[source] auths_core::ports::network::NetworkError),
217
218    /// Local identity or attestation data is invalid.
219    #[error("local data error: {0}")]
220    LocalDataError(String),
221}
222
223impl From<auths_core::AgentError> for SetupError {
224    fn from(err: auths_core::AgentError) -> Self {
225        SetupError::CryptoError(err)
226    }
227}
228
229impl From<RegistrationError> for SetupError {
230    fn from(err: RegistrationError) -> Self {
231        SetupError::RegistrationFailed(err)
232    }
233}
234
235impl From<auths_core::AgentError> for DeviceError {
236    fn from(err: auths_core::AgentError) -> Self {
237        DeviceError::CryptoError(err)
238    }
239}
240
241impl From<auths_core::ports::network::NetworkError> for RegistrationError {
242    fn from(err: auths_core::ports::network::NetworkError) -> Self {
243        RegistrationError::NetworkError(err)
244    }
245}
246
247/// Errors from organization member management workflows.
248///
249/// Usage:
250/// ```ignore
251/// match result {
252///     Err(OrgError::AdminNotFound { .. }) => { /* 403 Forbidden */ }
253///     Err(OrgError::MemberNotFound { .. }) => { /* 404 Not Found */ }
254///     Err(e) => return Err(e.into()),
255///     Ok(att) => { /* proceed */ }
256/// }
257/// ```
258#[derive(Debug, Error)]
259#[non_exhaustive]
260pub enum OrgError {
261    /// No admin matching the given public key was found in the organization.
262    #[error("no admin with the given public key found in organization '{org}'")]
263    AdminNotFound {
264        /// The organization identifier.
265        org: String,
266    },
267
268    /// The specified member was not found in the organization.
269    #[error("member '{did}' not found in organization '{org}'")]
270    MemberNotFound {
271        /// The organization identifier.
272        org: String,
273        /// The DID of the member that was not found.
274        did: String,
275    },
276
277    /// The member has already been revoked.
278    #[error("member '{did}' is already revoked")]
279    AlreadyRevoked {
280        /// The DID of the already-revoked member.
281        did: String,
282    },
283
284    /// The capability string could not be parsed.
285    #[error("invalid capability '{cap}': {reason}")]
286    InvalidCapability {
287        /// The invalid capability string.
288        cap: String,
289        /// The reason parsing failed.
290        reason: String,
291    },
292
293    /// The organization DID is malformed.
294    #[error("invalid organization DID: {0}")]
295    InvalidDid(String),
296
297    /// The hex-encoded public key is invalid.
298    #[error("invalid public key: {0}")]
299    InvalidPublicKey(String),
300
301    /// A storage operation failed.
302    #[error("storage error: {0}")]
303    Storage(String),
304}