1use serde::{Deserialize, Serialize};
21use thiserror::Error;
22
23#[derive(Debug, Error)]
25pub enum WebDIDError {
26 #[error("error-atproto-identity-web-1 Invalid DID format: missing 'did:web:' prefix")]
28 InvalidDIDPrefix,
29
30 #[error("error-atproto-identity-web-2 Invalid DID format: missing hostname component")]
32 MissingHostname,
33
34 #[error("error-atproto-identity-web-3 HTTP request failed: {url} {error}")]
36 HttpRequestFailed {
37 url: String,
39 error: reqwest::Error,
41 },
42
43 #[error("error-atproto-identity-web-4 Failed to parse DID document: {url} {error}")]
45 DocumentParseFailed {
46 url: String,
48 error: reqwest::Error,
50 },
51}
52
53#[derive(Debug, Error)]
55pub enum ConfigError {
56 #[error("error-atproto-identity-config-1 Required environment variable not found: {name}")]
58 MissingEnvironmentVariable {
59 name: String,
61 },
62
63 #[error("error-atproto-identity-config-2 Unable to parse nameserver IP: {value}")]
65 InvalidNameserverIP {
66 value: String,
68 },
69
70 #[error(
72 "error-atproto-identity-config-3 Version information not available: GIT_HASH or CARGO_PKG_VERSION must be set"
73 )]
74 VersionNotAvailable,
75}
76
77#[derive(Debug, Error)]
79pub enum ResolveError {
80 #[error(
82 "error-atproto-identity-resolve-1 Multiple DIDs resolved for handle: expected single DID"
83 )]
84 MultipleDIDsFound,
85
86 #[error(
88 "error-atproto-identity-resolve-2 No DIDs resolved for handle: no resolution methods succeeded"
89 )]
90 NoDIDsFound,
91
92 #[error(
94 "error-atproto-identity-resolve-3 Conflicting DIDs found for handle: DNS and HTTP resolution returned different results"
95 )]
96 ConflictingDIDsFound,
97
98 #[cfg(feature = "hickory-dns")]
100 #[error("error-atproto-identity-resolve-4 DNS resolution failed: {error:?}")]
101 DNSResolutionFailed {
102 error: hickory_resolver::ResolveError,
104 },
105
106 #[cfg(not(feature = "hickory-dns"))]
108 #[error("error-atproto-identity-resolve-4 DNS resolution failed")]
109 DNSResolutionFailed,
110
111 #[error("error-atproto-identity-resolve-5 HTTP resolution failed: {error:?}")]
113 HTTPResolutionFailed {
114 error: reqwest::Error,
116 },
117
118 #[error(
120 "error-atproto-identity-resolve-6 Invalid HTTP resolution response: expected DID format"
121 )]
122 InvalidHTTPResolutionResponse,
123
124 #[error("error-atproto-identity-resolve-7 Invalid input format: expected valid handle or DID")]
126 InvalidInput,
127
128 #[error("error-atproto-identity-resolve-8 Subject resolved to handle instead of DID")]
130 SubjectResolvedToHandle,
131}
132
133#[derive(Debug, Error)]
135pub enum PLCDIDError {
136 #[error("error-atproto-identity-plc-1 HTTP request failed: {url} {error}")]
138 HttpRequestFailed {
139 url: String,
141 error: reqwest::Error,
143 },
144
145 #[error("error-atproto-identity-plc-2 Failed to parse DID document: {url} {error}")]
147 DocumentParseFailed {
148 url: String,
150 error: reqwest::Error,
152 },
153
154 #[error("error-atproto-identity-plc-3 Invalid DID format: {details}")]
156 InvalidDidFormat {
157 details: String,
159 },
160
161 #[error("error-atproto-identity-plc-4 Invalid base32 encoding: {details}")]
163 InvalidBase32 {
164 details: String,
166 },
167
168 #[error("error-atproto-identity-plc-5 Invalid base64url encoding: {details}")]
170 InvalidBase64Url {
171 details: String,
173 },
174
175 #[error("error-atproto-identity-plc-6 Operation exceeds size limit: {size} bytes (max {max})")]
177 OperationTooLarge {
178 size: usize,
180 max: usize,
182 },
183
184 #[error("error-atproto-identity-plc-7 Invalid rotation keys: {details}")]
186 InvalidRotationKeys {
187 details: String,
189 },
190
191 #[error("error-atproto-identity-plc-8 Invalid verification methods: {details}")]
193 InvalidVerificationMethods {
194 details: String,
196 },
197
198 #[error("error-atproto-identity-plc-9 Invalid service endpoint: {details}")]
200 InvalidService {
201 details: String,
203 },
204
205 #[error("error-atproto-identity-plc-10 Too many entries in {field}: max {max}, got {actual}")]
207 TooManyEntries {
208 field: String,
210 max: usize,
212 actual: usize,
214 },
215
216 #[error("error-atproto-identity-plc-11 Duplicate entry in {field}: {value}")]
218 DuplicateEntry {
219 field: String,
221 value: String,
223 },
224
225 #[error("error-atproto-identity-plc-12 DAG-CBOR encoding failed: {details}")]
227 DagCborEncodeFailed {
228 details: String,
230 },
231
232 #[error("error-atproto-identity-plc-13 DAG-CBOR decoding failed: {details}")]
234 DagCborDecodeFailed {
235 details: String,
237 },
238
239 #[error("error-atproto-identity-plc-14 Signature verification failed")]
241 SignatureVerificationFailed,
242
243 #[error("error-atproto-identity-plc-15 Invalid CID: {details}")]
245 InvalidCid {
246 details: String,
248 },
249
250 #[error("error-atproto-identity-plc-16 Invalid operation type: {details}")]
252 InvalidOperationType {
253 details: String,
255 },
256
257 #[error("error-atproto-identity-plc-17 Missing required field: {field}")]
259 MissingField {
260 field: String,
262 },
263
264 #[error("error-atproto-identity-plc-18 Chain validation failed: {details}")]
266 ChainValidationFailed {
267 details: String,
269 },
270
271 #[error("error-atproto-identity-plc-19 Empty operation chain")]
273 EmptyChain,
274
275 #[error("error-atproto-identity-plc-20 First operation must be genesis")]
277 FirstOperationNotGenesis,
278
279 #[error("error-atproto-identity-plc-21 Invalid prev reference: {details}")]
281 InvalidPrev {
282 details: String,
284 },
285
286 #[error("error-atproto-identity-plc-22 Fork resolution error: {details}")]
288 ForkResolutionError {
289 details: String,
291 },
292
293 #[error("error-atproto-identity-plc-23 Invalid also-known-as URI: {details}")]
295 InvalidAlsoKnownAs {
296 details: String,
298 },
299
300 #[error("error-atproto-identity-plc-24 Invalid timestamp: {details}")]
302 InvalidTimestamp {
303 details: String,
305 },
306
307 #[error("error-atproto-identity-plc-25 Submission failed: {url} status {status}: {body}")]
309 SubmissionFailed {
310 url: String,
312 status: u16,
314 body: String,
316 },
317
318 #[error("error-atproto-identity-plc-26 Audit log fetch failed: {url} {error}")]
320 AuditLogFetchFailed {
321 url: String,
323 error: reqwest::Error,
325 },
326
327 #[error("error-atproto-identity-plc-27 Audit log parse failed: {url} {error}")]
329 AuditLogParseFailed {
330 url: String,
332 error: reqwest::Error,
334 },
335}
336
337#[derive(Debug, Error)]
339pub enum KeyError {
340 #[error("error-atproto-identity-key-1 Error decoding key: {error:?}")]
342 DecodeError {
343 error: multibase::Error,
345 },
346
347 #[error("error-atproto-identity-key-2 Signature parsing failed: {error:?}")]
349 SignatureError {
350 error: ecdsa::signature::Error,
352 },
353
354 #[error("error-atproto-identity-key-3 P-256 key operation failed: {error:?}")]
356 P256Error {
357 error: p256::ecdsa::Error,
359 },
360
361 #[error("error-atproto-identity-key-4 P-384 key operation failed: {error:?}")]
363 P384Error {
364 error: p384::ecdsa::Error,
366 },
367
368 #[error("error-atproto-identity-key-5 K-256 key operation failed: {error:?}")]
370 K256Error {
371 error: k256::ecdsa::Error,
373 },
374
375 #[error("error-atproto-identity-key-6 ECDSA operation failed: {error:?}")]
377 ECDSAError {
378 error: ecdsa::Error,
380 },
381
382 #[error("error-atproto-identity-key-7 Secret key operation failed: {error:?}")]
384 SecretKeyError {
385 error: ecdsa::elliptic_curve::Error,
387 },
388
389 #[error("error-atproto-identity-key-8 Private key required for signature")]
391 PrivateKeyRequiredForSignature,
392
393 #[error(
395 "error-atproto-identity-key-9 Public key generation not supported: generate private key instead"
396 )]
397 PublicKeyGenerationNotSupported,
398
399 #[error("error-atproto-identity-key-10 Unidentified key type: key data too short")]
401 UnidentifiedKeyType,
402
403 #[error("error-atproto-identity-key-11 Invalid multibase key type: {prefix:?}")]
405 InvalidMultibaseKeyType {
406 prefix: Vec<u8>,
408 },
409
410 #[error("error-atproto-identity-key-12 JWK format conversion failed: {error}")]
412 JWKConversionFailed {
413 error: String,
415 },
416
417 #[error("error-atproto-identity-key-13 Ed25519 key operation failed: {error}")]
419 Ed25519Error {
420 error: String,
422 },
423}
424
425#[derive(Debug, Error)]
427pub enum WebVHDIDError {
428 #[error("error-atproto-identity-webvh-1 Invalid DID format: missing 'did:webvh:' prefix")]
430 InvalidDIDPrefix,
431
432 #[error("error-atproto-identity-webvh-2 Invalid DID format: missing SCID component")]
434 MissingSCID,
435
436 #[error("error-atproto-identity-webvh-3 Invalid DID format: missing hostname component")]
438 MissingHostname,
439
440 #[error("error-atproto-identity-webvh-4 HTTP request failed: {url} {error}")]
442 HttpRequestFailed {
443 url: String,
445 error: reqwest::Error,
447 },
448
449 #[error("error-atproto-identity-webvh-5 Failed to parse log entry at line {line}: {details}")]
451 LogEntryParseFailed {
452 line: usize,
454 details: String,
456 },
457
458 #[error("error-atproto-identity-webvh-6 Empty log file")]
460 EmptyLog,
461
462 #[error("error-atproto-identity-webvh-7 Invalid version ID at entry {entry}: {details}")]
464 InvalidVersionId {
465 entry: usize,
467 details: String,
469 },
470
471 #[error(
473 "error-atproto-identity-webvh-8 Version time not monotonically increasing at entry {entry}"
474 )]
475 VersionTimeNotMonotonic {
476 entry: usize,
478 },
479
480 #[error("error-atproto-identity-webvh-9 SCID verification failed on genesis entry")]
482 SCIDVerificationFailed,
483
484 #[error("error-atproto-identity-webvh-10 Entry hash verification failed at entry {entry}")]
486 EntryHashVerificationFailed {
487 entry: usize,
489 },
490
491 #[error(
493 "error-atproto-identity-webvh-11 Proof verification failed at entry {entry}: {details}"
494 )]
495 ProofVerificationFailed {
496 entry: usize,
498 details: String,
500 },
501
502 #[error("error-atproto-identity-webvh-12 Pre-rotation key hash mismatch at entry {entry}")]
504 PreRotationKeyMismatch {
505 entry: usize,
507 },
508
509 #[error("error-atproto-identity-webvh-13 Invalid parameters at entry {entry}: {details}")]
511 InvalidParameters {
512 entry: usize,
514 details: String,
516 },
517
518 #[error(
520 "error-atproto-identity-webvh-14 Witness verification failed at entry {entry}: {details}"
521 )]
522 WitnessVerificationFailed {
523 entry: usize,
525 details: String,
527 },
528
529 #[error("error-atproto-identity-webvh-15 DID document extraction failed: {details}")]
531 DocumentExtractionFailed {
532 details: String,
534 },
535
536 #[error("error-atproto-identity-webvh-16 Invalid SCID format: {details}")]
538 InvalidSCIDFormat {
539 details: String,
541 },
542
543 #[error("error-atproto-identity-webvh-17 Deactivated DID: {did}")]
545 DeactivatedDID {
546 did: String,
548 },
549
550 #[error("error-atproto-identity-webvh-18 Version not found: {details}")]
552 VersionNotFound {
553 details: String,
555 },
556
557 #[error(
559 "error-atproto-identity-webvh-19 Version time is in the future at entry {entry}: {version_time}"
560 )]
561 VersionTimeInFuture {
562 entry: usize,
564 version_time: String,
566 },
567
568 #[error(
570 "error-atproto-identity-webvh-20 DIDDoc id mismatch at entry {entry}: expected '{expected}', found '{found}'"
571 )]
572 DIDDocIdMismatch {
573 entry: usize,
575 expected: String,
577 found: String,
579 },
580
581 #[error("error-atproto-identity-webvh-21 Unsupported hash algorithm in SCID: {details}")]
583 UnsupportedHashAlgorithm {
584 details: String,
586 },
587
588 #[error("error-atproto-identity-webvh-22 Unknown parameter at entry {entry}: {parameter}")]
590 UnknownParameter {
591 entry: usize,
593 parameter: String,
595 },
596
597 #[error("error-atproto-identity-webvh-23 Hostname normalization failed: {details}")]
599 HostnameNormalizationFailed {
600 details: String,
602 },
603
604 #[error("error-atproto-identity-webvh-24 Genesis creation failed: {details}")]
606 GenesisCreationFailed {
607 details: String,
609 },
610
611 #[error("error-atproto-identity-webvh-25 Update creation failed: {details}")]
613 UpdateCreationFailed {
614 details: String,
616 },
617
618 #[error("error-atproto-identity-webvh-26 Signing failed: {details}")]
620 SigningFailed {
621 details: String,
623 },
624}
625
626#[derive(Debug, Clone, Serialize, Deserialize)]
628#[serde(rename_all = "camelCase")]
629pub struct ResolutionError {
630 pub error: ResolutionErrorCode,
632 #[serde(skip_serializing_if = "Option::is_none")]
634 pub problem_details: Option<ProblemDetails>,
635}
636
637#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
639pub enum ResolutionErrorCode {
640 #[serde(rename = "notFound")]
642 NotFound,
643 #[serde(rename = "invalidDid")]
645 InvalidDid,
646}
647
648#[derive(Debug, Clone, Serialize, Deserialize)]
650pub struct ProblemDetails {
651 #[serde(skip_serializing_if = "Option::is_none")]
653 pub r#type: Option<String>,
654 pub title: String,
656 #[serde(skip_serializing_if = "Option::is_none")]
658 pub detail: Option<String>,
659}
660
661impl WebVHDIDError {
662 pub fn to_resolution_error(&self) -> ResolutionError {
664 match self {
665 WebVHDIDError::HttpRequestFailed { .. } | WebVHDIDError::EmptyLog => ResolutionError {
666 error: ResolutionErrorCode::NotFound,
667 problem_details: Some(ProblemDetails {
668 r#type: None,
669 title: "DID not found".to_string(),
670 detail: Some(self.to_string()),
671 }),
672 },
673 WebVHDIDError::InvalidDIDPrefix
674 | WebVHDIDError::MissingSCID
675 | WebVHDIDError::MissingHostname
676 | WebVHDIDError::InvalidSCIDFormat { .. } => ResolutionError {
677 error: ResolutionErrorCode::InvalidDid,
678 problem_details: Some(ProblemDetails {
679 r#type: None,
680 title: "Invalid DID format".to_string(),
681 detail: Some(self.to_string()),
682 }),
683 },
684 _ => ResolutionError {
685 error: ResolutionErrorCode::InvalidDid,
686 problem_details: Some(ProblemDetails {
687 r#type: None,
688 title: "DID verification failed".to_string(),
689 detail: Some(self.to_string()),
690 }),
691 },
692 }
693 }
694}
695
696#[derive(Debug, Error)]
698pub enum StorageError {
699 #[error(
701 "error-atproto-identity-storage-1 Cache lock acquisition failed for get operation: {details}"
702 )]
703 CacheLockFailedGet {
704 details: String,
706 },
707
708 #[error(
710 "error-atproto-identity-storage-2 Cache lock acquisition failed for store operation: {details}"
711 )]
712 CacheLockFailedStore {
713 details: String,
715 },
716
717 #[error(
719 "error-atproto-identity-storage-3 Cache lock acquisition failed for delete operation: {details}"
720 )]
721 CacheLockFailedDelete {
722 details: String,
724 },
725}