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 | DiagnosticCategory::Cancelled => {
147                SchemaAuthorityErrorCode::ResourceLimit
148            }
149            DiagnosticCategory::Integrity => SchemaAuthorityErrorCode::IntegrityMismatch,
150        };
151        Self {
152            code,
153            message: "a schema-authority contract is invalid",
154            contract: Some(Box::new(value)),
155            schema: None,
156        }
157    }
158}
159
160impl From<SchemaDiagnostics> for SchemaAuthorityError {
161    fn from(value: SchemaDiagnostics) -> Self {
162        Self {
163            code: SchemaAuthorityErrorCode::Schema,
164            message: "schema-authority reconstruction failed",
165            contract: None,
166            schema: Some(Box::new(value)),
167        }
168    }
169}
170
171impl From<DeltaError> for SchemaAuthorityError {
172    fn from(value: DeltaError) -> Self {
173        match value {
174            DeltaError::Contract(error) => error.into(),
175            DeltaError::Schema(error) => error.into(),
176        }
177    }
178}
179
180#[derive(Clone, Debug, Deserialize, Serialize)]
181#[serde(deny_unknown_fields)]
182struct SemanticProfileBindingWire {
183    fingerprint: Value,
184    id: SemanticProfileId,
185}
186
187impl SemanticProfileBindingWire {
188    fn from_binding(binding: &SemanticProfileBinding) -> Result<Self, SchemaAuthorityError> {
189        Ok(Self {
190            fingerprint: canonical_value(binding.fingerprint().as_fingerprint())?,
191            id: binding.id().clone(),
192        })
193    }
194
195    fn rebuild(self) -> Result<SemanticProfileBinding, SchemaAuthorityError> {
196        let trusted = SemanticProfileBinding::resolve(self.id.clone())?;
197        require_exact_value(
198            &self.fingerprint,
199            trusted.fingerprint().as_fingerprint(),
200            "semantic-profile fingerprint is stale",
201        )?;
202        Ok(trusted)
203    }
204}
205
206#[derive(Clone, Debug, Deserialize, Serialize)]
207#[serde(deny_unknown_fields)]
208struct ManagedScopeProfileBindingWire {
209    fingerprint: Value,
210    id: ManagedScopeProfileId,
211}
212
213#[derive(Clone, Debug, Deserialize, Serialize)]
214#[serde(deny_unknown_fields)]
215struct ManagedScopeBindingWire {
216    id: ManagedScopeId,
217    profile: ManagedScopeProfileBindingWire,
218}
219
220impl ManagedScopeBindingWire {
221    fn from_binding(binding: &ManagedScopeBinding) -> Result<Self, SchemaAuthorityError> {
222        Ok(Self {
223            id: binding.id().clone(),
224            profile: ManagedScopeProfileBindingWire {
225                fingerprint: canonical_value(binding.profile().fingerprint().as_fingerprint())?,
226                id: binding.profile().id().clone(),
227            },
228        })
229    }
230
231    fn rebuild(self) -> Result<ManagedScopeBinding, SchemaAuthorityError> {
232        let trusted = ManagedScopeBinding::exclusive(self.id.clone())?;
233        if self.profile.id != *trusted.profile().id() {
234            return Err(integrity_failure("managed-scope profile identity is stale"));
235        }
236        require_exact_value(
237            &self.profile.fingerprint,
238            trusted.profile().fingerprint().as_fingerprint(),
239            "managed-scope profile fingerprint is stale",
240        )?;
241        Ok(trusted)
242    }
243}
244
245#[derive(Clone, Debug, Deserialize, Serialize)]
246#[serde(deny_unknown_fields)]
247struct SchemaAuthorityContentWire {
248    authority_version: String,
249    codec_version: u16,
250    declared_identity: Value,
251    declared_schema: Value,
252    managed_scope: ManagedScopeBindingWire,
253    managed_state: Value,
254    required_capabilities: CapabilitySet,
255    schema_ir_version: u16,
256    semantic_profile: SemanticProfileBindingWire,
257    semantic_schema: Value,
258}
259
260#[derive(Clone, Debug, Deserialize, Serialize)]
261#[serde(deny_unknown_fields)]
262struct SchemaAuthorityWire {
263    authority_fingerprint: Value,
264    content: SchemaAuthorityContentWire,
265}
266
267/// Opaque source-free authority accepted only after complete reconstruction.
268#[derive(Clone, Debug)]
269pub struct VerifiedSchemaAuthority {
270    authority_fingerprint: Fingerprint,
271    canonical_bytes: Vec<u8>,
272    declared: DeclaredSchema,
273    managed_scope: ManagedScopeBinding,
274    managed_state: ManagedSchemaState,
275    required_capabilities: CapabilitySet,
276    resolved: ResolvedSchema,
277    semantic_profile: SemanticProfileBinding,
278}
279
280impl VerifiedSchemaAuthority {
281    /// Return the exact fingerprint of the authority content envelope.
282    #[must_use]
283    pub const fn authority_fingerprint(&self) -> &Fingerprint {
284        &self.authority_fingerprint
285    }
286
287    /// Return the constructor-validated direct declaration.
288    #[must_use]
289    pub const fn declared_schema(&self) -> &DeclaredSchema {
290        &self.declared
291    }
292
293    /// Return independently reconstructed effective schema semantics.
294    #[must_use]
295    pub const fn resolved_schema(&self) -> &ResolvedSchema {
296        &self.resolved
297    }
298
299    /// Return the registry-resolved semantic-profile identity and content binding.
300    #[must_use]
301    pub const fn semantic_profile(&self) -> &SemanticProfileBinding {
302        &self.semantic_profile
303    }
304
305    /// Return the registry-resolved durable managed-scope binding.
306    #[must_use]
307    pub const fn managed_scope(&self) -> &ManagedScopeBinding {
308        &self.managed_scope
309    }
310
311    /// Return the independently reconstructed managed schema state.
312    #[must_use]
313    pub const fn managed_state(&self) -> &ManagedSchemaState {
314        &self.managed_state
315    }
316
317    /// Return the exact additive capability requirements bound by this artifact.
318    #[must_use]
319    pub const fn required_capabilities(&self) -> &CapabilitySet {
320        &self.required_capabilities
321    }
322}
323
324/// Build and immediately reverify one source-free schema authority.
325///
326/// `required_capabilities` may add workspace/runtime requirements to the
327/// declaration's own requirements, but it may neither omit a declaration
328/// requirement nor claim a capability absent from `context`.
329pub fn build_schema_authority(
330    declared: &DeclaredSchema,
331    required_capabilities: &CapabilitySet,
332    context: &ManagedDeltaContext,
333) -> Result<VerifiedSchemaAuthority, SchemaAuthorityError> {
334    declared
335        .required_capabilities()
336        .ensure_supported_by(required_capabilities)?;
337    required_capabilities.ensure_supported_by(context.available_capabilities())?;
338
339    let semantic_profile = SemanticProfileBinding::resolve(context.semantic_profile().clone())?;
340    let managed_scope = ManagedScopeBinding::exclusive(context.scope_id().clone())
341        .map_err(SchemaAuthorityError::from)?;
342    let resolved = resolve_schema_with_capabilities(
343        declared,
344        context.semantic_profile(),
345        context.available_capabilities(),
346    )?;
347    let managed_state = managed_schema_state(declared, context)?;
348    let declared_bytes = encode_declared_schema(declared)?;
349    let content = SchemaAuthorityContentWire {
350        authority_version: TYPEBRIDGE_SCHEMA_AUTHORITY_V1.to_owned(),
351        codec_version: CodecVersion::V1.get(),
352        declared_identity: canonical_value(declared.declared_identity_fingerprint())?,
353        declared_schema: from_canonical_json(&declared_bytes)?,
354        managed_scope: ManagedScopeBindingWire::from_binding(&managed_scope)?,
355        managed_state: canonical_value(&managed_state)?,
356        required_capabilities: required_capabilities.clone(),
357        schema_ir_version: declared.format().get(),
358        semantic_profile: SemanticProfileBindingWire::from_binding(&semantic_profile)?,
359        semantic_schema: canonical_value(resolved.semantic_fingerprint())?,
360    };
361    let authority_fingerprint = compute_authority_fingerprint(&content)?;
362    let wire = SchemaAuthorityWire {
363        authority_fingerprint: canonical_value(&authority_fingerprint)?,
364        content,
365    };
366    let bytes = to_canonical_json(&wire)?;
367    decode_schema_authority(&bytes, context.available_capabilities())
368}
369
370/// Return exact canonical bytes retained by an already verified authority.
371#[must_use]
372pub fn encode_schema_authority(authority: &VerifiedSchemaAuthority) -> Vec<u8> {
373    authority.canonical_bytes.clone()
374}
375
376/// Decode canonical bytes and independently reconstruct every bound schema view.
377///
378/// The caller supplies only its available capability set. Scope and semantic
379/// profile come from the artifact and are accepted only through their frozen
380/// registries; no source workspace or repeated deployment configuration is
381/// consulted.
382pub fn decode_schema_authority(
383    bytes: &[u8],
384    available_capabilities: &CapabilitySet,
385) -> Result<VerifiedSchemaAuthority, SchemaAuthorityError> {
386    let wire: SchemaAuthorityWire = from_canonical_json(bytes)?;
387    if to_canonical_json(&wire)? != bytes {
388        return Err(SchemaAuthorityError::new(
389            SchemaAuthorityErrorCode::Contract,
390            "schema-authority bytes normalize after typed reconstruction",
391        ));
392    }
393    if wire.content.authority_version != TYPEBRIDGE_SCHEMA_AUTHORITY_V1
394        || wire.content.codec_version != CodecVersion::V1.get()
395        || wire.content.schema_ir_version != FormatVersion::V1.get()
396    {
397        return Err(SchemaAuthorityError::new(
398            SchemaAuthorityErrorCode::UnsupportedVersion,
399            "schema-authority, codec, or schema-IR version is unsupported",
400        ));
401    }
402
403    let authority_fingerprint = compute_authority_fingerprint(&wire.content)?;
404    require_exact_value(
405        &wire.authority_fingerprint,
406        &authority_fingerprint,
407        "schema-authority content fingerprint is stale",
408    )?;
409
410    let SchemaAuthorityContentWire {
411        authority_version: _,
412        codec_version: _,
413        declared_identity,
414        declared_schema,
415        managed_scope,
416        managed_state,
417        required_capabilities,
418        schema_ir_version: _,
419        semantic_profile,
420        semantic_schema,
421    } = wire.content;
422
423    required_capabilities.ensure_supported_by(available_capabilities)?;
424    let semantic_profile = semantic_profile.rebuild()?;
425    let managed_scope = managed_scope.rebuild()?;
426    let declared_bytes = to_canonical_json(&declared_schema)?;
427    let declared = decode_declared_schema(&declared_bytes)?;
428    declared
429        .required_capabilities()
430        .ensure_supported_by(&required_capabilities)?;
431    require_exact_value(
432        &declared_identity,
433        declared.declared_identity_fingerprint(),
434        "declared-schema identity fingerprint is stale",
435    )?;
436
437    let resolved =
438        resolve_schema_with_capabilities(&declared, semantic_profile.id(), available_capabilities)?;
439    require_exact_value(
440        &semantic_schema,
441        resolved.semantic_fingerprint(),
442        "global semantic-schema fingerprint is stale",
443    )?;
444
445    let managed_context = ManagedDeltaContext::new(
446        managed_scope.id().clone(),
447        semantic_profile.id().clone(),
448        available_capabilities.clone(),
449    );
450    let rebuilt_managed_state = managed_schema_state(&declared, &managed_context)?;
451    require_exact_value(
452        &managed_state,
453        &rebuilt_managed_state,
454        "managed schema state is stale",
455    )?;
456
457    Ok(VerifiedSchemaAuthority {
458        authority_fingerprint,
459        canonical_bytes: bytes.to_vec(),
460        declared,
461        managed_scope,
462        managed_state: rebuilt_managed_state,
463        required_capabilities,
464        resolved,
465        semantic_profile,
466    })
467}
468
469fn compute_authority_fingerprint(
470    content: &SchemaAuthorityContentWire,
471) -> Result<Fingerprint, SchemaAuthorityError> {
472    Ok(Fingerprint::compute(
473        FingerprintDomain::new(SCHEMA_AUTHORITY_FINGERPRINT_DOMAIN)?,
474        CanonicalizationVersion::new(SCHEMA_AUTHORITY_FINGERPRINT_CANONICALIZATION)?,
475        None,
476        &to_canonical_json(content)?,
477    ))
478}
479
480fn canonical_value<T: Serialize>(value: &T) -> Result<Value, SchemaAuthorityError> {
481    Ok(from_canonical_json(&to_canonical_json(value)?)?)
482}
483
484fn require_exact_value<T: Serialize>(
485    actual: &Value,
486    expected: &T,
487    message: &'static str,
488) -> Result<(), SchemaAuthorityError> {
489    if actual == &canonical_value(expected)? {
490        Ok(())
491    } else {
492        Err(integrity_failure(message))
493    }
494}
495
496fn integrity_failure(message: &'static str) -> SchemaAuthorityError {
497    SchemaAuthorityError::new(SchemaAuthorityErrorCode::IntegrityMismatch, message)
498}