a1-ai 2.8.0

A1 — The cryptographic identity and authorization layer that turns anonymous AI agents into accountable, verifiable entities. One Identity. Full Provenance.
Documentation
use thiserror::Error;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StorageErrorKind {
    Transient,
    Permanent,
}

#[derive(Debug, Clone)]
pub struct A1StorageError {
    pub kind: StorageErrorKind,
    pub message: String,
}

impl PartialEq for A1StorageError {
    fn eq(&self, other: &Self) -> bool {
        self.kind == other.kind
    }
}

impl Eq for A1StorageError {}

impl A1StorageError {
    pub fn transient(msg: impl Into<String>) -> Self {
        Self {
            kind: StorageErrorKind::Transient,
            message: msg.into(),
        }
    }

    pub fn permanent(msg: impl Into<String>) -> Self {
        Self {
            kind: StorageErrorKind::Permanent,
            message: msg.into(),
        }
    }

    pub fn is_transient(&self) -> bool {
        self.kind == StorageErrorKind::Transient
    }
}

impl std::fmt::Display for A1StorageError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let label = match self.kind {
            StorageErrorKind::Transient => "transient",
            StorageErrorKind::Permanent => "permanent",
        };
        write!(f, "{label} storage error: {}", self.message)
    }
}

impl std::error::Error for A1StorageError {}

#[derive(Debug, Error, PartialEq, Eq)]
#[non_exhaustive]
pub enum A1Error {
    #[error("delegation chain is empty")]
    EmptyChain,

    #[error("storage backend failure: {0}")]
    StorageFailure(A1StorageError),

    #[error("chain does not anchor to the declared principal")]
    RootMismatch,

    #[error("delegation linkage broken at hop {0}")]
    BrokenLinkage(usize),

    #[error("invalid signature at hop {0}")]
    InvalidSignature(usize),

    #[error("delegation at hop {0} not yet valid (issued_at={1}, now={2})")]
    NotYetValid(usize, u64, u64),

    #[error("delegation at hop {0} has expired (expiry={1}, now={2})")]
    Expired(usize, u64, u64),

    #[error("temporal violation at hop {0}: child expiry {1} exceeds parent expiry {2}")]
    TemporalViolation(usize, u64, u64),

    #[error("depth limit exceeded at hop {0} (limit={1})")]
    MaxDepthExceeded(usize, u8),

    #[error("sub-scope proof is structurally invalid")]
    InvalidSubScopeProof,

    #[error(
        "scope escalation at hop {0}: delegated scope is not within the delegator's authorization"
    )]
    ScopeEscalation(usize),

    #[error("executing agent is not the terminal delegate")]
    UnauthorizedLeaf,

    #[error("execution intent is not within the terminal scope")]
    ScopeViolation,

    #[error("nonce has already been consumed")]
    NonceReplay,

    #[error("delegation certificate has been revoked")]
    Revoked,

    #[error("intent is not present in this tree")]
    IntentNotFound,

    #[error("intent tree requires at least one intent")]
    EmptyTree,

    #[error("wire format error: {0}")]
    WireFormatError(String),

    #[error("unsupported certificate version: expected {expected}, got {got}")]
    UnsupportedVersion { expected: u8, got: u8 },

    #[error("policy violation: {0}")]
    PolicyViolation(String),

    #[error("batch authorization failed at index {index}: {reason}")]
    BatchItemFailed { index: usize, reason: String },

    #[error("MAC verification failed")]
    MacVerificationFailed,

    #[error("namespace mismatch: chain namespace is '{chain}', authorization requested for '{requested}'")]
    NamespaceMismatch { chain: String, requested: String },

    #[error("rate limit exceeded for key")]
    RateLimitExceeded,

    #[error("storage health check failed: {0}")]
    StorageUnhealthy(String),

    #[error("passport capability narrowing violation: requested scope exceeds granted passport capabilities")]
    PassportNarrowingViolation,

    #[error("unsupported signature algorithm tag: {0}")]
    UnsupportedAlgorithm(u8),

    #[error("signature algorithm mismatch: expected {expected}, found {found}")]
    AlgorithmMismatch {
        expected: &'static str,
        found: &'static str,
    },

    #[error("hybrid signature invalid: {component} component failed verification")]
    HybridSignatureInvalid { component: &'static str },

    #[error("post-quantum signature component is missing for algorithm {0}")]
    PqSignatureMissing(&'static str),

    #[error("invalid hybrid key length for {algorithm}: expected {expected} bytes, found {found}")]
    InvalidHybridKeyLength {
        algorithm: &'static str,
        expected: usize,
        found: usize,
    },
}

impl A1Error {
    pub fn as_storage_error(&self) -> Option<&A1StorageError> {
        if let Self::StorageFailure(e) = self {
            Some(e)
        } else {
            None
        }
    }

    pub fn is_transient_storage_failure(&self) -> bool {
        self.as_storage_error().is_some_and(|e| e.is_transient())
    }

    pub fn error_code(&self) -> &'static str {
        match self {
            Self::Expired(..) => "CERT_EXPIRED",
            Self::Revoked => "CERT_REVOKED",
            Self::NonceReplay => "NONCE_REPLAY",
            Self::ScopeViolation => "SCOPE_VIOLATION",
            Self::ScopeEscalation(_) => "SCOPE_ESCALATION",
            Self::InvalidSignature(_) => "INVALID_SIGNATURE",
            Self::BrokenLinkage(_) => "CHAIN_BROKEN_LINKAGE",
            Self::MaxDepthExceeded(..) => "CHAIN_DEPTH_EXCEEDED",
            Self::PolicyViolation(_) => "POLICY_VIOLATION",
            Self::StorageFailure(_) => "STORAGE_ERROR",
            Self::BatchItemFailed { .. } => "BATCH_ITEM_FAILED",
            Self::MacVerificationFailed => "MAC_VERIFICATION_FAILED",
            Self::NamespaceMismatch { .. } => "NAMESPACE_MISMATCH",
            Self::RateLimitExceeded => "RATE_LIMIT_EXCEEDED",
            Self::StorageUnhealthy(_) => "STORAGE_UNHEALTHY",
            Self::PassportNarrowingViolation => "PASSPORT_NARROWING_VIOLATION",
            Self::UnsupportedAlgorithm(_) => "UNSUPPORTED_ALGORITHM",
            Self::AlgorithmMismatch { .. } => "ALGORITHM_MISMATCH",
            Self::HybridSignatureInvalid { .. } => "HYBRID_SIGNATURE_INVALID",
            Self::PqSignatureMissing(_) => "PQ_SIGNATURE_MISSING",
            Self::InvalidHybridKeyLength { .. } => "INVALID_HYBRID_KEY_LENGTH",
            _ => "AUTHORIZATION_FAILED",
        }
    }

    pub fn http_status(&self) -> u16 {
        match self {
            Self::StorageFailure(e) if e.is_transient() => 503,
            Self::StorageFailure(_) => 500,
            Self::StorageUnhealthy(_) => 503,
            Self::RateLimitExceeded => 429,
            Self::EmptyChain | Self::WireFormatError(_) | Self::UnsupportedVersion { .. } => 400,
            Self::Revoked | Self::Expired(..) | Self::NotYetValid(..) | Self::NonceReplay => 401,
            Self::ScopeViolation | Self::ScopeEscalation(_) | Self::UnauthorizedLeaf => 403,
            Self::InvalidSignature(_) | Self::RootMismatch | Self::BrokenLinkage(_) => 403,
            Self::PolicyViolation(_) | Self::NamespaceMismatch { .. } => 403,
            Self::MacVerificationFailed => 401,
            Self::PassportNarrowingViolation => 403,
            Self::UnsupportedAlgorithm(_) => 400,
            Self::AlgorithmMismatch { .. } => 400,
            Self::HybridSignatureInvalid { .. } => 403,
            Self::PqSignatureMissing(_) => 400,
            Self::InvalidHybridKeyLength { .. } => 400,
            _ => 403,
        }
    }
}