Skip to main content

qualia_core_db/render/
barrier.rs

1//! Validate-before-render barrier (P2)
2//!
3//! A strict, fail-closed gate that must pass before geometry is accepted for rendering.
4//! Validates the relational SHACL rules of the manifest, checks the `.10d` whole-file CRC,
5//! decodes the provenance sidecar, asserts the immutable `source_digest`, ensures the
6//! presence of a licence (context not stripped), and optionally verifies any attached
7//! Verifiable Credentials against grounded issuers.
8
9use ed25519_dalek::{Signature, VerifyingKey};
10
11use crate::container_10d::header::Container10dHeader;
12use crate::container_10d::provenance_section::{decode_provenance_section, validate_provenance};
13use crate::container_10d::section::{parse_section_table, SectionType};
14use crate::container_10d::{decode_mesh_section, verify_whole_file_crc32c};
15use crate::crypto::verifiable_credential::{decode_credential, verify_grounded, VcError};
16use crate::indexing::QuinIndex;
17use crate::modalities::logic::geometry_asset_shacl::{
18    validate_geometry_manifest, GeometryAssetConfiguration, GeometryConstraintViolation,
19    GeometryManifestFacts,
20};
21use crate::render::assets::Mesh;
22
23/// Errors that can occur during the validate-before-render barrier.
24#[derive(Debug, PartialEq, Eq)]
25pub enum BarrierError {
26    /// The container failed CRC-32C or header validation.
27    ContainerIntegrity(String),
28    /// The manifest failed relational SHACL validation.
29    ManifestViolation(Vec<GeometryConstraintViolation>),
30    /// The provenance sidecar was missing, malformed, or failed the digest/licence gate.
31    ProvenanceFailed(String),
32    /// The attached verifiable credential failed signature/expiry or issuer grounding checks.
33    CredentialInvalid(VcError),
34    /// The VC payload was malformed (could not decode signature + credential bytes).
35    CredentialMalformed,
36    /// The container has no `QuantizedMesh` section.
37    NoMesh,
38}
39
40impl std::fmt::Display for BarrierError {
41    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42        match self {
43            Self::ContainerIntegrity(e) => write!(f, "Barrier: Container integrity: {e}"),
44            Self::ManifestViolation(vs) => {
45                write!(f, "Barrier: SHACL manifest violations: {vs:?}")
46            }
47            Self::ProvenanceFailed(e) => write!(f, "Barrier: Provenance gate failed: {e}"),
48            Self::CredentialInvalid(e) => write!(f, "Barrier: VC invalid: {e}"),
49            Self::CredentialMalformed => write!(f, "Barrier: VC payload malformed"),
50            Self::NoMesh => write!(f, "Barrier: No QuantizedMesh section found"),
51        }
52    }
53}
54impl std::error::Error for BarrierError {}
55
56/// The validate-before-render barrier.
57///
58/// 1. Relational SHACL validation.
59/// 2. `.10d` whole-file CRC verification.
60/// 3. Provenance extraction and `validate_provenance` (source_digest and licence checks).
61/// 4. If a VC is present, decodes it and verifies the signature using `verify_grounded`.
62/// 5. Finally decodes and returns the `Mesh`.
63pub fn validate_before_render(
64    container_bytes: &[u8],
65    manifest: &GeometryManifestFacts,
66    config: &GeometryAssetConfiguration,
67    index: &QuinIndex,
68    now: u32,
69    key_resolver: impl Fn(u64) -> Option<VerifyingKey>,
70) -> Result<Mesh, BarrierError> {
71    // 1. Relational SHACL validation
72    let violations = validate_geometry_manifest(manifest, config);
73    if !violations.is_empty() {
74        return Err(BarrierError::ManifestViolation(violations));
75    }
76
77    // 2. Container Integrity
78    let mut bytes_mut = container_bytes.to_vec();
79    verify_whole_file_crc32c(&mut bytes_mut)
80        .map_err(|e| BarrierError::ContainerIntegrity(e.to_string()))?;
81
82    let header = Container10dHeader::parse(&bytes_mut)
83        .map_err(|e| BarrierError::ContainerIntegrity(e.to_string()))?;
84
85    let descs = parse_section_table(&bytes_mut, &header)
86        .map_err(|e| BarrierError::ContainerIntegrity(format!("{e:?}")))?;
87
88    let mut mesh = None;
89    let mut provenance = None;
90
91    for desc in descs.iter() {
92        let st = SectionType::from_u8(desc.section_type);
93        if let Some(st) = st {
94            let off = desc.byte_offset as usize;
95            let len = desc.byte_length as usize;
96            let payload = &bytes_mut[off..off + len];
97
98            if st == SectionType::QuantizedMesh {
99                mesh = Some(payload);
100            } else if st == SectionType::ProvenanceSidecar {
101                provenance = Some(payload);
102            }
103        }
104    }
105
106    // 3. Provenance Extraction & Validation
107    let prov_payload = provenance
108        .ok_or_else(|| BarrierError::ProvenanceFailed("No provenance sidecar found".to_string()))?;
109
110    let view = decode_provenance_section(prov_payload)
111        .map_err(|e| BarrierError::ProvenanceFailed(e.to_string()))?;
112
113    validate_provenance(&view).map_err(|e| BarrierError::ProvenanceFailed(e.to_string()))?;
114
115    // 4. VC Verification
116    if let Some(vc_bytes) = view.vc() {
117        if vc_bytes.len() < 64 {
118            return Err(BarrierError::CredentialMalformed);
119        }
120        let (sig_bytes, cred_bytes) = vc_bytes.split_at(64);
121        let signature = Signature::from_bytes(sig_bytes.try_into().unwrap());
122        let credential =
123            decode_credential(cred_bytes).map_err(|_| BarrierError::CredentialMalformed)?;
124
125        let issuer_key = key_resolver(credential.issuer).ok_or_else(|| {
126            BarrierError::CredentialInvalid(VcError::InvalidSignature) // Key not found
127        })?;
128
129        verify_grounded(&credential, &issuer_key, &signature, now, index)
130            .map_err(BarrierError::CredentialInvalid)?;
131    }
132
133    // 5. Decode Mesh
134    let mesh_payload = mesh.ok_or(BarrierError::NoMesh)?;
135    decode_mesh_section(mesh_payload).map_err(|e| BarrierError::ContainerIntegrity(e.to_string()))
136}