soma-som-core 0.1.0

Universal soma(som) structural primitives — Quad / Tree / Ring / Genesis / Fingerprint / TemporalLedger / CrossingRecord
Documentation
// SPDX-License-Identifier: LGPL-3.0-only
#![allow(missing_docs)]

//! Pluggable evidence validation + ring-cycle evidence tripwires.
//!
//! Two surfaces live here:
//!
//! 1. [`EvidenceValidator`] — the §13.1 trait the ring delegates the
//!    "is the evidence sufficient at this gate?" decision to. Application
//!    code supplies a concrete validator; the ring exposes [`AcceptAll`] as
//!    the no-op default.
//!
//! 2. [`verify_ring_cycle_evidence`] / [`verify_view_cycle_evidence`] —
//!    defense-in-depth tripwires that reject delegate calls bypassing ring
//!    mediation. The primary protection is structural (the engine does not
//!    expose the dispatch surface externally); these helpers are the second
//!    line.

use crate::extension::DelegationError;
use crate::quad::Tree;

// ── §13.1 Evidence validation trait ─────────────────────────────────────────

/// Pluggable evidence validation for ring gate transitions.
pub trait EvidenceValidator: Send + Sync {
    fn validate(&self, gate: &str, artifacts: &Tree) -> Result<(), Vec<String>>;
}

/// No-op default — accepts all evidence without inspection.
pub struct AcceptAll;

impl EvidenceValidator for AcceptAll {
    fn validate(&self, _gate: &str, _artifacts: &Tree) -> Result<(), Vec<String>> {
        Ok(())
    }
}

// ── Ring-cycle evidence tripwires ───────────────────────────────────────────

/// Verify that a dispatch input Tree carries ring-cycle evidence.
///
/// Returns `Ok(())` if `command.request_id` is present — injected by
/// `inject_command` during a legitimate ring cycle regardless of which
/// application vocabulary the ring uses. Returns `Err(DelegationError)` if
/// absent, indicating the delegate was called outside the ring, bypassing
/// BEFORE gates, CU authorization, and crossing attestation.
///
/// This is a defense-in-depth tripwire. The primary protection is structural:
/// `RingEngine` does not expose the dispatch surface externally.
///
/// Applications whose ring-cycle injection uses a different evidence key
/// should call [`verify_ring_cycle_evidence_with_key`] instead.
pub fn verify_ring_cycle_evidence(
    delegate_name: &str,
    input: &Tree,
) -> Result<(), DelegationError> {
    verify_ring_cycle_evidence_with_key(delegate_name, input, "command.request_id")
}

/// Verify that a dispatch input Tree carries ring-cycle evidence using a
/// caller-supplied marker key.
///
/// Identical to [`verify_ring_cycle_evidence`] but accepts any key the
/// application injects as its ring-cycle marker (e.g. `"command.operator"`,
/// `"command.session_id"`). Use this when the application's injection scheme
/// differs from the default `command.request_id` convention.
pub fn verify_ring_cycle_evidence_with_key(
    delegate_name: &str,
    input: &Tree,
    cycle_marker_key: &str,
) -> Result<(), DelegationError> {
    if input.get(cycle_marker_key).is_none() {
        return Err(DelegationError {
            source: delegate_name.to_string(),
            reason: format!(
                "dispatch called outside ring cycle — no ring-cycle evidence ({cycle_marker_key}) present"
            ),
        });
    }
    Ok(())
}

/// Verify that a view projection input Tree carries ring-cycle evidence.
///
/// Returns `Ok(())` if `view.id` is present (injected by `inject_view_intent`
/// during a legitimate ring cycle). Returns `Err(DelegationError)` if the
/// evidence is missing — indicating the delegate was called outside the ring.
///
/// Symmetric to [`verify_ring_cycle_evidence`] but keyed on the `view.*`
/// namespace. Implementors of [`crate::extension::ViewRing`] SHOULD call
/// this first.
pub fn verify_view_cycle_evidence(
    delegate_name: &str,
    input: &Tree,
) -> Result<(), DelegationError> {
    if input.get("view.id").is_none() {
        return Err(DelegationError {
            source: delegate_name.to_string(),
            reason: "project called outside view cycle — no ring-cycle evidence (view.id) present".into(),
        });
    }
    Ok(())
}