Skip to main content

type_bridge_schema/
authority.rs

1//! Constructor-verified, source-free schema authority artifacts.
2
3use std::error::Error;
4use std::fmt;
5
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use type_bridge_contract::capability::{CapabilityId, CapabilitySet};
9use type_bridge_contract::codec::{
10    CodecVersion, FormatVersion, from_canonical_json, to_canonical_json,
11};
12use type_bridge_contract::diagnostic::{Diagnostic, DiagnosticCategory};
13use type_bridge_contract::fingerprint::{
14    CanonicalizationVersion, Fingerprint, FingerprintDomain, SemanticProfileId,
15};
16use type_bridge_contract::limits::MAX_CANONICAL_BYTES;
17use type_bridge_contract::managed_scope::{
18    ManagedScopeBinding, ManagedScopeId, ManagedScopeProfileId, SemanticProfileBinding,
19};
20use type_bridge_contract::migration::CONDITIONAL_RESOLUTION_CAPABILITY;
21use type_bridge_contract::migration_assertion_capability_vocabulary;
22use type_bridge_contract::query_plan::query_plan_v2_capability_vocabulary;
23use type_bridge_contract::schema::{
24    DeclaredSchema, ManagedSchemaState, SchemaDiagnostics, decode_declared_schema,
25    encode_declared_schema,
26};
27use type_bridge_contract::schema_delta::{
28    SCHEMA_REDEFINE_CAPABILITY, schema_transition_capability_vocabulary,
29};
30
31use crate::{
32    BUILTIN_SCHEMA_CAPABILITY_IDS, DeltaError, ManagedDeltaContext, ResolvedSchema,
33    managed_schema_state, resolve_schema_with_capabilities,
34};
35
36/// The first source-free compiled schema-authority envelope.
37pub const TYPEBRIDGE_SCHEMA_AUTHORITY_V1: &str = "typebridge.schema-authority/v1";
38/// Fingerprint domain for one exact canonical schema-authority content envelope.
39pub const SCHEMA_AUTHORITY_FINGERPRINT_DOMAIN: &str = "typebridge.schema.authority";
40/// Canonicalization identity for the first schema-authority content envelope.
41pub const SCHEMA_AUTHORITY_FINGERPRINT_CANONICALIZATION: &str = "typebridge.schema-authority/v1";
42/// Maximum canonical schema-authority size under the shared contract codec.
43pub const MAX_SCHEMA_AUTHORITY_BYTES: usize = MAX_CANONICAL_BYTES;
44
45/// Return every capability understood by generated packages and the generic server.
46///
47/// The authority may bind query, schema-resolution, and workspace migration
48/// requirements. Consumers use this closed vocabulary only to reconstruct and
49/// validate that envelope; execution surfaces still advertise their narrower
50/// operation-specific capabilities.
51#[must_use]
52pub fn schema_authority_capability_vocabulary() -> CapabilitySet {
53    let mut capabilities = query_plan_v2_capability_vocabulary();
54    for capability in schema_transition_capability_vocabulary()
55        .into_iter()
56        .chain(migration_assertion_capability_vocabulary())
57    {
58        capabilities.insert(capability);
59    }
60    for capability in BUILTIN_SCHEMA_CAPABILITY_IDS {
61        capabilities.insert(
62            CapabilityId::new(*capability).expect("built-in schema capability ID is canonical"),
63        );
64    }
65    for capability in [
66        SCHEMA_REDEFINE_CAPABILITY,
67        CONDITIONAL_RESOLUTION_CAPABILITY,
68    ] {
69        capabilities.insert(
70            CapabilityId::new(capability).expect("static authority capability ID is canonical"),
71        );
72    }
73    capabilities
74}
75
76/// Stable high-level classification for schema-authority failures.
77#[derive(Clone, Copy, Debug, Eq, PartialEq)]
78pub enum SchemaAuthorityErrorCode {
79    /// Canonical bytes or a nested contract value are malformed.
80    Contract,
81    /// Declared-schema construction or semantic resolution failed.
82    Schema,
83    /// The authority, codec, or schema-IR version is unsupported.
84    UnsupportedVersion,
85    /// A required capability is absent from the available capability set.
86    UnsupportedCapability,
87    /// A canonical structural ceiling was exceeded.
88    ResourceLimit,
89    /// A bound fingerprint, scope, profile, or state is stale.
90    IntegrityMismatch,
91}
92
93/// A fail-closed schema-authority failure retaining nested diagnostics.
94#[derive(Clone, Debug, Eq, PartialEq)]
95pub struct SchemaAuthorityError {
96    code: SchemaAuthorityErrorCode,
97    message: &'static str,
98    contract: Option<Box<Diagnostic>>,
99    schema: Option<Box<SchemaDiagnostics>>,
100}
101
102impl SchemaAuthorityError {
103    fn new(code: SchemaAuthorityErrorCode, message: &'static str) -> Self {
104        Self {
105            code,
106            message,
107            contract: None,
108            schema: None,
109        }
110    }
111
112    /// Return the stable high-level failure classification.
113    #[must_use]
114    pub const fn code(&self) -> SchemaAuthorityErrorCode {
115        self.code
116    }
117
118    /// Return the nested contract diagnostic, when one caused the failure.
119    #[must_use]
120    pub fn contract(&self) -> Option<&Diagnostic> {
121        self.contract.as_deref()
122    }
123
124    /// Return nested schema diagnostics, when reconstruction or resolution failed.
125    #[must_use]
126    pub fn schema(&self) -> Option<&SchemaDiagnostics> {
127        self.schema.as_deref()
128    }
129}
130
131impl fmt::Display for SchemaAuthorityError {
132    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
133        formatter.write_str(self.message)
134    }
135}
136
137impl Error for SchemaAuthorityError {}
138
139impl From<Diagnostic> for SchemaAuthorityError {
140    fn from(value: Diagnostic) -> Self {
141        let code = match value.category() {
142            DiagnosticCategory::InvalidContract => SchemaAuthorityErrorCode::Contract,
143            DiagnosticCategory::UnsupportedCapability => {
144                SchemaAuthorityErrorCode::UnsupportedCapability
145            }
146            DiagnosticCategory::ResourceLimit => SchemaAuthorityErrorCode::ResourceLimit,
147            DiagnosticCategory::Integrity => SchemaAuthorityErrorCode::IntegrityMismatch,
148        };
149        Self {
150            code,
151            message: "a schema-authority contract is invalid",
152            contract: Some(Box::new(value)),
153            schema: None,
154        }
155    }
156}
157
158impl From<SchemaDiagnostics> for SchemaAuthorityError {
159    fn from(value: SchemaDiagnostics) -> Self {
160        Self {
161            code: SchemaAuthorityErrorCode::Schema,
162            message: "schema-authority reconstruction failed",
163            contract: None,
164            schema: Some(Box::new(value)),
165        }
166    }
167}
168
169impl From<DeltaError> for SchemaAuthorityError {
170    fn from(value: DeltaError) -> Self {
171        match value {
172            DeltaError::Contract(error) => error.into(),
173            DeltaError::Schema(error) => error.into(),
174        }
175    }
176}
177
178#[derive(Clone, Debug, Deserialize, Serialize)]
179#[serde(deny_unknown_fields)]
180struct SemanticProfileBindingWire {
181    fingerprint: Value,
182    id: SemanticProfileId,
183}
184
185impl SemanticProfileBindingWire {
186    fn from_binding(binding: &SemanticProfileBinding) -> Result<Self, SchemaAuthorityError> {
187        Ok(Self {
188            fingerprint: canonical_value(binding.fingerprint().as_fingerprint())?,
189            id: binding.id().clone(),
190        })
191    }
192
193    fn rebuild(self) -> Result<SemanticProfileBinding, SchemaAuthorityError> {
194        let trusted = SemanticProfileBinding::resolve(self.id.clone())?;
195        require_exact_value(
196            &self.fingerprint,
197            trusted.fingerprint().as_fingerprint(),
198            "semantic-profile fingerprint is stale",
199        )?;
200        Ok(trusted)
201    }
202}
203
204#[derive(Clone, Debug, Deserialize, Serialize)]
205#[serde(deny_unknown_fields)]
206struct ManagedScopeProfileBindingWire {
207    fingerprint: Value,
208    id: ManagedScopeProfileId,
209}
210
211#[derive(Clone, Debug, Deserialize, Serialize)]
212#[serde(deny_unknown_fields)]
213struct ManagedScopeBindingWire {
214    id: ManagedScopeId,
215    profile: ManagedScopeProfileBindingWire,
216}
217
218impl ManagedScopeBindingWire {
219    fn from_binding(binding: &ManagedScopeBinding) -> Result<Self, SchemaAuthorityError> {
220        Ok(Self {
221            id: binding.id().clone(),
222            profile: ManagedScopeProfileBindingWire {
223                fingerprint: canonical_value(binding.profile().fingerprint().as_fingerprint())?,
224                id: binding.profile().id().clone(),
225            },
226        })
227    }
228
229    fn rebuild(self) -> Result<ManagedScopeBinding, SchemaAuthorityError> {
230        let trusted = ManagedScopeBinding::exclusive(self.id.clone())?;
231        if self.profile.id != *trusted.profile().id() {
232            return Err(integrity_failure("managed-scope profile identity is stale"));
233        }
234        require_exact_value(
235            &self.profile.fingerprint,
236            trusted.profile().fingerprint().as_fingerprint(),
237            "managed-scope profile fingerprint is stale",
238        )?;
239        Ok(trusted)
240    }
241}
242
243#[derive(Clone, Debug, Deserialize, Serialize)]
244#[serde(deny_unknown_fields)]
245struct SchemaAuthorityContentWire {
246    authority_version: String,
247    codec_version: u16,
248    declared_identity: Value,
249    declared_schema: Value,
250    managed_scope: ManagedScopeBindingWire,
251    managed_state: Value,
252    required_capabilities: CapabilitySet,
253    schema_ir_version: u16,
254    semantic_profile: SemanticProfileBindingWire,
255    semantic_schema: Value,
256}
257
258#[derive(Clone, Debug, Deserialize, Serialize)]
259#[serde(deny_unknown_fields)]
260struct SchemaAuthorityWire {
261    authority_fingerprint: Value,
262    content: SchemaAuthorityContentWire,
263}
264
265/// Opaque source-free authority accepted only after complete reconstruction.
266#[derive(Clone, Debug)]
267pub struct VerifiedSchemaAuthority {
268    authority_fingerprint: Fingerprint,
269    canonical_bytes: Vec<u8>,
270    declared: DeclaredSchema,
271    managed_scope: ManagedScopeBinding,
272    managed_state: ManagedSchemaState,
273    required_capabilities: CapabilitySet,
274    resolved: ResolvedSchema,
275    semantic_profile: SemanticProfileBinding,
276}
277
278impl VerifiedSchemaAuthority {
279    /// Return the exact fingerprint of the authority content envelope.
280    #[must_use]
281    pub const fn authority_fingerprint(&self) -> &Fingerprint {
282        &self.authority_fingerprint
283    }
284
285    /// Return the constructor-validated direct declaration.
286    #[must_use]
287    pub const fn declared_schema(&self) -> &DeclaredSchema {
288        &self.declared
289    }
290
291    /// Return independently reconstructed effective schema semantics.
292    #[must_use]
293    pub const fn resolved_schema(&self) -> &ResolvedSchema {
294        &self.resolved
295    }
296
297    /// Return the registry-resolved semantic-profile identity and content binding.
298    #[must_use]
299    pub const fn semantic_profile(&self) -> &SemanticProfileBinding {
300        &self.semantic_profile
301    }
302
303    /// Return the registry-resolved durable managed-scope binding.
304    #[must_use]
305    pub const fn managed_scope(&self) -> &ManagedScopeBinding {
306        &self.managed_scope
307    }
308
309    /// Return the independently reconstructed managed schema state.
310    #[must_use]
311    pub const fn managed_state(&self) -> &ManagedSchemaState {
312        &self.managed_state
313    }
314
315    /// Return the exact additive capability requirements bound by this artifact.
316    #[must_use]
317    pub const fn required_capabilities(&self) -> &CapabilitySet {
318        &self.required_capabilities
319    }
320}
321
322/// Build and immediately reverify one source-free schema authority.
323///
324/// `required_capabilities` may add workspace/runtime requirements to the
325/// declaration's own requirements, but it may neither omit a declaration
326/// requirement nor claim a capability absent from `context`.
327pub fn build_schema_authority(
328    declared: &DeclaredSchema,
329    required_capabilities: &CapabilitySet,
330    context: &ManagedDeltaContext,
331) -> Result<VerifiedSchemaAuthority, SchemaAuthorityError> {
332    declared
333        .required_capabilities()
334        .ensure_supported_by(required_capabilities)?;
335    required_capabilities.ensure_supported_by(context.available_capabilities())?;
336
337    let semantic_profile = SemanticProfileBinding::resolve(context.semantic_profile().clone())?;
338    let managed_scope = ManagedScopeBinding::exclusive(context.scope_id().clone())
339        .map_err(SchemaAuthorityError::from)?;
340    let resolved = resolve_schema_with_capabilities(
341        declared,
342        context.semantic_profile(),
343        context.available_capabilities(),
344    )?;
345    let managed_state = managed_schema_state(declared, context)?;
346    let declared_bytes = encode_declared_schema(declared)?;
347    let content = SchemaAuthorityContentWire {
348        authority_version: TYPEBRIDGE_SCHEMA_AUTHORITY_V1.to_owned(),
349        codec_version: CodecVersion::V1.get(),
350        declared_identity: canonical_value(declared.declared_identity_fingerprint())?,
351        declared_schema: from_canonical_json(&declared_bytes)?,
352        managed_scope: ManagedScopeBindingWire::from_binding(&managed_scope)?,
353        managed_state: canonical_value(&managed_state)?,
354        required_capabilities: required_capabilities.clone(),
355        schema_ir_version: declared.format().get(),
356        semantic_profile: SemanticProfileBindingWire::from_binding(&semantic_profile)?,
357        semantic_schema: canonical_value(resolved.semantic_fingerprint())?,
358    };
359    let authority_fingerprint = compute_authority_fingerprint(&content)?;
360    let wire = SchemaAuthorityWire {
361        authority_fingerprint: canonical_value(&authority_fingerprint)?,
362        content,
363    };
364    let bytes = to_canonical_json(&wire)?;
365    decode_schema_authority(&bytes, context.available_capabilities())
366}
367
368/// Return exact canonical bytes retained by an already verified authority.
369#[must_use]
370pub fn encode_schema_authority(authority: &VerifiedSchemaAuthority) -> Vec<u8> {
371    authority.canonical_bytes.clone()
372}
373
374/// Decode canonical bytes and independently reconstruct every bound schema view.
375///
376/// The caller supplies only its available capability set. Scope and semantic
377/// profile come from the artifact and are accepted only through their frozen
378/// registries; no source workspace or repeated deployment configuration is
379/// consulted.
380pub fn decode_schema_authority(
381    bytes: &[u8],
382    available_capabilities: &CapabilitySet,
383) -> Result<VerifiedSchemaAuthority, SchemaAuthorityError> {
384    let wire: SchemaAuthorityWire = from_canonical_json(bytes)?;
385    if to_canonical_json(&wire)? != bytes {
386        return Err(SchemaAuthorityError::new(
387            SchemaAuthorityErrorCode::Contract,
388            "schema-authority bytes normalize after typed reconstruction",
389        ));
390    }
391    if wire.content.authority_version != TYPEBRIDGE_SCHEMA_AUTHORITY_V1
392        || wire.content.codec_version != CodecVersion::V1.get()
393        || wire.content.schema_ir_version != FormatVersion::V1.get()
394    {
395        return Err(SchemaAuthorityError::new(
396            SchemaAuthorityErrorCode::UnsupportedVersion,
397            "schema-authority, codec, or schema-IR version is unsupported",
398        ));
399    }
400
401    let authority_fingerprint = compute_authority_fingerprint(&wire.content)?;
402    require_exact_value(
403        &wire.authority_fingerprint,
404        &authority_fingerprint,
405        "schema-authority content fingerprint is stale",
406    )?;
407
408    let SchemaAuthorityContentWire {
409        authority_version: _,
410        codec_version: _,
411        declared_identity,
412        declared_schema,
413        managed_scope,
414        managed_state,
415        required_capabilities,
416        schema_ir_version: _,
417        semantic_profile,
418        semantic_schema,
419    } = wire.content;
420
421    required_capabilities.ensure_supported_by(available_capabilities)?;
422    let semantic_profile = semantic_profile.rebuild()?;
423    let managed_scope = managed_scope.rebuild()?;
424    let declared_bytes = to_canonical_json(&declared_schema)?;
425    let declared = decode_declared_schema(&declared_bytes)?;
426    declared
427        .required_capabilities()
428        .ensure_supported_by(&required_capabilities)?;
429    require_exact_value(
430        &declared_identity,
431        declared.declared_identity_fingerprint(),
432        "declared-schema identity fingerprint is stale",
433    )?;
434
435    let resolved =
436        resolve_schema_with_capabilities(&declared, semantic_profile.id(), available_capabilities)?;
437    require_exact_value(
438        &semantic_schema,
439        resolved.semantic_fingerprint(),
440        "global semantic-schema fingerprint is stale",
441    )?;
442
443    let managed_context = ManagedDeltaContext::new(
444        managed_scope.id().clone(),
445        semantic_profile.id().clone(),
446        available_capabilities.clone(),
447    );
448    let rebuilt_managed_state = managed_schema_state(&declared, &managed_context)?;
449    require_exact_value(
450        &managed_state,
451        &rebuilt_managed_state,
452        "managed schema state is stale",
453    )?;
454
455    Ok(VerifiedSchemaAuthority {
456        authority_fingerprint,
457        canonical_bytes: bytes.to_vec(),
458        declared,
459        managed_scope,
460        managed_state: rebuilt_managed_state,
461        required_capabilities,
462        resolved,
463        semantic_profile,
464    })
465}
466
467fn compute_authority_fingerprint(
468    content: &SchemaAuthorityContentWire,
469) -> Result<Fingerprint, SchemaAuthorityError> {
470    Ok(Fingerprint::compute(
471        FingerprintDomain::new(SCHEMA_AUTHORITY_FINGERPRINT_DOMAIN)?,
472        CanonicalizationVersion::new(SCHEMA_AUTHORITY_FINGERPRINT_CANONICALIZATION)?,
473        None,
474        &to_canonical_json(content)?,
475    ))
476}
477
478fn canonical_value<T: Serialize>(value: &T) -> Result<Value, SchemaAuthorityError> {
479    Ok(from_canonical_json(&to_canonical_json(value)?)?)
480}
481
482fn require_exact_value<T: Serialize>(
483    actual: &Value,
484    expected: &T,
485    message: &'static str,
486) -> Result<(), SchemaAuthorityError> {
487    if actual == &canonical_value(expected)? {
488        Ok(())
489    } else {
490        Err(integrity_failure(message))
491    }
492}
493
494fn integrity_failure(message: &'static str) -> SchemaAuthorityError {
495    SchemaAuthorityError::new(SchemaAuthorityErrorCode::IntegrityMismatch, message)
496}