Skip to main content

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}