Skip to main content

exo_gatekeeper/
error.rs

1// Copyright 2026 Exochain Foundation
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at:
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15// SPDX-License-Identifier: Apache-2.0
16
17//! Gatekeeper-specific errors.
18
19use thiserror::Error;
20
21/// Top-level error type for the gatekeeper crate.
22#[derive(Debug, Error, Clone, PartialEq, Eq)]
23pub enum GatekeeperError {
24    #[error("kernel integrity check failed: expected {expected}, got {actual}")]
25    KernelIntegrityFailure { expected: String, actual: String },
26
27    #[error("invariant violation: {0}")]
28    InvariantViolation(String),
29
30    #[error("combinator reduction failed: {0}")]
31    CombinatorError(String),
32
33    #[error("holon error: {0}")]
34    HolonError(String),
35
36    #[error("MCP violation: {0}")]
37    McpViolation(String),
38
39    #[error("MCP typed signature payload encoding failed: {reason}")]
40    McpTypedSignatureEncodingFailed { reason: String },
41
42    #[error("TEE attestation failed: {0}")]
43    TeeError(String),
44
45    #[error("capability denied: {0}")]
46    CapabilityDenied(String),
47
48    #[error("authority resolver unavailable: {0}")]
49    AuthorityResolverUnavailable(String),
50
51    #[error("timeout after {0} ms")]
52    Timeout(u64),
53
54    #[error("checkpoint error: {0}")]
55    CheckpointError(String),
56
57    #[error("core error: {0}")]
58    Core(String),
59
60    #[error("MCP audit chain broken at index {index}")]
61    McpAuditChainBroken { index: usize },
62
63    #[error("MCP audit record invalid: {reason}")]
64    McpAuditInvalidRecord { reason: String },
65
66    #[error("MCP audit hash encoding failed: {reason}")]
67    McpAuditHashEncodingFailed { reason: String },
68}
69
70impl From<exo_core::ExoError> for GatekeeperError {
71    fn from(e: exo_core::ExoError) -> Self {
72        GatekeeperError::Core(e.to_string())
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[test]
81    fn display_all_variants() {
82        let cases: Vec<(GatekeeperError, &str)> = vec![
83            (
84                GatekeeperError::KernelIntegrityFailure {
85                    expected: "abc".into(),
86                    actual: "def".into(),
87                },
88                "abc",
89            ),
90            (GatekeeperError::InvariantViolation("bad".into()), "bad"),
91            (GatekeeperError::CombinatorError("fail".into()), "fail"),
92            (GatekeeperError::HolonError("holon".into()), "holon"),
93            (GatekeeperError::McpViolation("mcp".into()), "mcp"),
94            (
95                GatekeeperError::McpTypedSignatureEncodingFailed {
96                    reason: "canonical CBOR failed".into(),
97                },
98                "canonical CBOR failed",
99            ),
100            (GatekeeperError::TeeError("tee".into()), "tee"),
101            (GatekeeperError::CapabilityDenied("cap".into()), "cap"),
102            (
103                GatekeeperError::AuthorityResolverUnavailable("pool absent".into()),
104                "authority resolver unavailable",
105            ),
106            (GatekeeperError::Timeout(500), "500"),
107            (GatekeeperError::CheckpointError("ckpt".into()), "ckpt"),
108            (GatekeeperError::Core("core".into()), "core"),
109            (
110                GatekeeperError::McpAuditInvalidRecord {
111                    reason: "missing deterministic metadata".into(),
112                },
113                "missing deterministic metadata",
114            ),
115            (
116                GatekeeperError::McpAuditHashEncodingFailed {
117                    reason: "canonical CBOR failed".into(),
118                },
119                "canonical CBOR failed",
120            ),
121        ];
122        for (err, fragment) in cases {
123            assert!(err.to_string().contains(fragment), "{err}");
124        }
125    }
126
127    #[test]
128    fn from_exo_error() {
129        let exo = exo_core::ExoError::InvalidDid { value: "x".into() };
130        let gk = GatekeeperError::from(exo);
131        assert!(matches!(gk, GatekeeperError::Core(_)));
132    }
133}