1use thiserror::Error;
20
21#[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}