krabka_remote_storage/worm/error.rs
1//! Errors raised by the write-once (WORM) archive layer.
2
3use crate::worm::chain::WORM_CHAIN_RECORD_VERSION;
4
5/// A failure in the WORM archive layer.
6///
7/// The variants split into four groups: a broken or absent chain stamp, a
8/// manifest that does not encode or decode, a refusal that the archive policy
9/// demands, and an inability to reach the archive at all.
10///
11/// That last group is deliberately its own variant rather than a codec error.
12/// A verifier must be able to tell "I looked and the archive is broken" from
13/// "I could not look", because only the first is evidence of tampering. The
14/// grading in `krabka-worm-verify` rests on that distinction.
15#[derive(Debug, thiserror::Error)]
16#[non_exhaustive]
17pub enum WormError {
18 /// The archive could not be listed or read. Says nothing about the
19 /// archive's integrity — only that the store did not answer.
20 #[error("WORM archive access: {0}")]
21 Archive(String),
22 /// The broker did not stamp a chain position on the segment metadata
23 /// before the copy. A WORM backend refuses to write an unchained manifest.
24 #[error("segment metadata carries no WORM chain stamp")]
25 MissingChainStamp,
26 /// The chain record on the segment metadata is not decodable.
27 #[error("malformed WORM chain record: {0}")]
28 MalformedChainRecord(String),
29 /// The chain record has no `"version"`, or one this build does not read.
30 /// A record without one was written before krabka 1.0.
31 #[error(
32 "unsupported WORM chain record version {}: this build reads version {}{}",
33 found.as_deref().unwrap_or("(none)"),
34 WORM_CHAIN_RECORD_VERSION,
35 if found.is_none() {
36 "; the record predates krabka 1.0, whose data a 1.x broker does not read"
37 } else {
38 ""
39 }
40 )]
41 UnsupportedChainRecordVersion {
42 /// The `"version"` the record carries, as JSON text, or `None` when it
43 /// has none.
44 found: Option<String>,
45 },
46 /// The manifest does not encode to, or decode from, its JSON form.
47 #[error("WORM manifest codec error: {0}")]
48 Codec(String),
49 /// An object reached the manifest with no `SHA-256` digest recorded for it.
50 #[error("no digest recorded for object `{key}`")]
51 MissingDigest {
52 /// Object-store key of the object with no digest.
53 key: String,
54 },
55 /// A multipart WORM write completed without a version id to pin.
56 #[error("no version id recorded for multipart object `{key}`")]
57 MissingVersionId {
58 /// Object-store key whose completed version could not be identified.
59 key: String,
60 },
61 /// A delete was attempted against a write-once archive.
62 #[error("delete refused: `{key}` is in a write-once archive")]
63 DeleteRefused {
64 /// Object-store key the caller asked to delete.
65 key: String,
66 },
67 /// A remote fetch was attempted against a write-only archive.
68 #[error("read refused: this archive is configured write-only")]
69 ReadRefused,
70 /// The configured signing key is missing, unreadable, or not a valid
71 /// `PKCS#8` Ed25519 key.
72 #[error("WORM signing key: {0}")]
73 SigningKey(String),
74}