tenuo 0.3.4

Task-scoped authorization for AI agents: signed warrants that constrain tools and arguments, narrow on delegation, and verify offline. Rust core of Tenuo.
Documentation
use super::authority::{PresentedAuthority, ReceivedAuthorization};
use super::call::Call;
use super::decision::Denial;
use super::guard::Guard;

/// Operator-side explanation. Never for caller-visible text.
pub struct Diagnostics<'a> {
    guard: &'a Guard,
    subject: Subject<'a>,
}

enum Subject<'a> {
    Holder(&'a PresentedAuthority),
    Received(&'a ReceivedAuthorization<'a>),
}

impl<'a> Diagnostics<'a> {
    pub(crate) fn holder(guard: &'a Guard, authority: &'a PresentedAuthority) -> Self {
        Self {
            guard,
            subject: Subject::Holder(authority),
        }
    }

    pub(crate) fn received(guard: &'a Guard, received: &'a ReceivedAuthorization<'a>) -> Self {
        Self {
            guard,
            subject: Subject::Received(received),
        }
    }

    /// Explain a denial in operator terms, naming the responsible field when there is one.
    ///
    /// Operator-side only. This string MUST NOT be returned to the caller that was denied;
    /// it deliberately discloses more than [`Denial::message`].
    pub fn explain_denial(&self, denial: &Denial) -> String {
        let leaf = self.leaf_id();
        match (denial.protocol_code(), denial.sdk_kind()) {
            (Some(code), _) => match denial.constraint_field() {
                Some(field) => format!(
                    "denied {} ({}) for leaf {} because field '{}' failed its constraint",
                    code.name(),
                    code.code(),
                    leaf,
                    field
                ),
                None => format!("denied {} ({}) for leaf {}", code.name(), code.code(), leaf),
            },
            (None, Some(kind)) => format!("denied {:?} for leaf {}", kind, leaf),
            (None, None) => format!("denied for leaf {}", leaf),
        }
    }

    /// Explain why this call would be denied, without performing an authorization.
    pub fn why_denied(&self, call: &Call<'_>) -> Option<String> {
        let attempt = super::guard::AuthorizationAttempt::new(call);
        let result = match self.subject {
            Subject::Holder(authority) => {
                self.guard.authorize_holder(authority, &attempt).map(|_| ())
            }
            Subject::Received(received) => {
                self.guard.authorize_received(received, call).map(|_| ())
            }
        };
        match result {
            Ok(_) => None,
            Err(denial) => Some(self.explain_denial(&denial)),
        }
    }

    /// Summarize the authority: leaf, depth, expiry, and claimed capabilities.
    pub fn explain_authority(&self) -> String {
        match self.subject {
            Subject::Holder(authority) => {
                let caps = authority.capabilities();
                format!(
                    "leaf {} depth {} expires {} holder {} capabilities {:?}",
                    authority.leaf().id(),
                    authority.chain().len(),
                    authority.expires_at(),
                    authority.holder().fingerprint(),
                    caps.names()
                )
            }
            Subject::Received(received) => format!(
                "received leaf {} depth {} approvals {}",
                received.leaf().id(),
                received.chain().len(),
                received.approvals().len()
            ),
        }
    }

    fn leaf_id(&self) -> String {
        match self.subject {
            Subject::Holder(authority) => authority.leaf().id().to_string(),
            Subject::Received(received) => received.leaf().id().to_string(),
        }
    }
}