1use core::marker::PhantomData;
4use std::sync::Arc;
5
6use type_bridge_contract::projection::{ProjectionConfig, RuntimeProjection};
7use type_bridge_contract::schema::encode_declared_schema;
8use type_bridge_contract::sdk_diagnostic::{
9 SdkExecutionDiagnostic, SdkProjectionEvidenceSlotPresence,
10};
11use type_bridge_schema::{
12 MAX_SCHEMA_AUTHORITY_BYTES, VerifiedSchemaAuthority, decode_schema_authority,
13 schema_authority_capability_vocabulary,
14};
15use type_bridge_schema_codegen::RustEmitter;
16
17use crate::__codegen::{
18 CompleteModel, EncodedCreate, GroupedQueryValue, HydratedRow, HydrationCapability,
19 IntoEncodedCreate, IntoEncodedReference, IntoEncodedScalar, IntoEncodedStruct,
20 IntoHydratedSnapshot, MaterializeCreate, MaterializeModel, MaterializeReference,
21 MaterializeStruct, Model, StructValue, ValidationError, ValidationPath,
22};
23use crate::CanonicalCodecOptions;
24use crate::canonical_codec::{
25 CapturedCanonicalCodecControl, input_error as canonical_input_error,
26 output_error as canonical_output_error,
27};
28use crate::error::{Error, ModelValidationPhase, Result};
29
30#[doc(hidden)]
31pub mod sealed {
32 pub trait Sealed {}
33}
34
35pub trait Schema: sealed::Sealed + Send + Sync + 'static {}
37
38#[derive(Clone, Copy, Debug, Eq, PartialEq)]
40pub struct Unbound;
41
42impl sealed::Sealed for Unbound {}
43impl Schema for Unbound {}
44
45fn canonical_contract_error(error: type_bridge_contract::diagnostic::Diagnostic) -> Error {
46 let code = error.code().as_str().to_owned();
47 Error::model_validation(
48 ModelValidationPhase::Input,
49 code,
50 Vec::new(),
51 "canonical projected-record contract rejected the value",
52 Some(Box::new(error)),
53 )
54}
55
56fn canonical_codec_error(error: type_bridge_orm::ProjectedCodecError) -> Error {
57 if matches!(
58 &error,
59 type_bridge_orm::ProjectedCodecError::Contract(diagnostic)
60 if diagnostic.code().as_str() == "projected_codec_declared_schema_mismatch"
61 ) {
62 return Error::from_sdk_execution(
63 SdkExecutionDiagnostic::projected_record_schema_mismatch(),
64 ModelValidationPhase::Input,
65 );
66 }
67 Error::model_validation(
68 ModelValidationPhase::Input,
69 "canonical_projected_codec_failure",
70 Vec::new(),
71 "installed projection rejected canonical record materialization",
72 Some(Box::new(error)),
73 )
74}
75
76#[doc(hidden)]
78pub struct GeneratedProjectionValidator {
79 installed: Arc<type_bridge_orm::InstalledRuntimeProjection>,
80}
81
82impl GeneratedProjectionValidator {
83 pub fn validate_create(&self, encoded: &EncodedCreate) -> Result<(), ValidationError> {
85 crate::projected_codec::validate_encoded_create(encoded, &self.installed)
86 .map_err(generated_validation_error)
87 }
88
89 pub fn validate_hydration(&self, row: &HydratedRow) -> Result<(), ValidationError> {
91 crate::projected_codec::validate_hydrated_row(row, &self.installed)
92 .map_err(generated_validation_error)
93 }
94}
95
96fn generated_validation_error(error: Error) -> ValidationError {
97 let code = error
98 .code()
99 .expect("generated projected validation always returns a classified model error")
100 .to_owned();
101 let path = error
102 .path()
103 .map(generated_validation_path)
104 .unwrap_or_default();
105 ValidationError::new(path, code)
106}
107
108fn generated_validation_path(segments: &[String]) -> String {
109 let mut path = String::new();
110 for segment in segments {
111 if segment.starts_with('[') {
112 path.push_str(segment);
113 } else {
114 if !path.is_empty() {
115 path.push('.');
116 }
117 path.push_str(segment);
118 }
119 }
120 path
121}
122
123#[derive(Clone, Copy, Debug, Eq, PartialEq)]
125pub struct SchemaPackage<S: Schema> {
126 semantic_fingerprint_json: &'static str,
127 projection_fingerprint_json: &'static str,
128 runtime_projection_json: &'static str,
129 declared_schema_json: Option<&'static str>,
130 schema_authority_json: Option<&'static str>,
131 managed_scope_id: Option<&'static str>,
132 semantic_profile_id: Option<&'static str>,
133 marker: PhantomData<fn() -> S>,
134}
135
136impl<S: Schema> SchemaPackage<S> {
137 pub fn encode_attribute<T>(&self, value: T) -> Result<Vec<u8>>
139 where
140 T: Model<Schema = S> + IntoEncodedScalar,
141 {
142 self.encode_attribute_controlled(value, &CanonicalCodecOptions::default())
143 }
144
145 pub fn encode_attribute_controlled<T>(
147 &self,
148 value: T,
149 options: &CanonicalCodecOptions,
150 ) -> Result<Vec<u8>>
151 where
152 T: Model<Schema = S> + IntoEncodedScalar,
153 {
154 let control = CapturedCanonicalCodecControl::capture(options, false)?;
155 control.check()?;
156 let installed = self.verify_and_install()?;
157 let projected = crate::projected_codec::project_attribute_inferred(&value, &installed)?;
158 let record = type_bridge_orm::record_from_attribute(&installed, &projected)
159 .map_err(canonical_codec_error)?;
160 control.check_decoded_weight(record.decoded_weight())?;
161 control.check()?;
162 let bytes = record
163 .encode_with_limits(control.output_limits())
164 .map_err(canonical_output_error)?;
165 control.check()?;
166 Ok(bytes)
167 }
168
169 pub fn decode_attribute<T>(&self, bytes: &[u8]) -> Result<T>
171 where
172 T: Model<Schema = S> + GroupedQueryValue,
173 {
174 self.decode_attribute_controlled(bytes, &CanonicalCodecOptions::default())
175 }
176
177 pub fn decode_attribute_controlled<T>(
179 &self,
180 bytes: &[u8],
181 options: &CanonicalCodecOptions,
182 ) -> Result<T>
183 where
184 T: Model<Schema = S> + GroupedQueryValue,
185 {
186 let control = CapturedCanonicalCodecControl::capture(options, false)?;
187 control.check()?;
188 let installed = self.verify_and_install()?;
189 let record = type_bridge_contract::projected_record::ProjectedRecord::decode_with_limits(
190 bytes,
191 control.input_limits(),
192 )
193 .map_err(canonical_input_error)?;
194 control.check_decoded_weight(record.decoded_weight())?;
195 control.check()?;
196 let value = type_bridge_orm::materialize_record(&installed, &record)
197 .map_err(canonical_codec_error)?;
198 let type_bridge_orm::ProjectedCodecValue::Attribute(value) = value else {
199 return Err(Error::model_validation(
200 ModelValidationPhase::Input,
201 "canonical_record_kind_mismatch",
202 Vec::new(),
203 "canonical record is not a generated attribute value",
204 None,
205 ));
206 };
207 let expected = type_bridge_contract::codec::from_canonical_json::<
208 type_bridge_contract::id::TypeId,
209 >(T::TYPE_ID_JSON.as_bytes())
210 .map_err(canonical_contract_error)?;
211 if value.attribute_type() != &expected {
212 return Err(Error::model_validation(
213 ModelValidationPhase::Input,
214 "canonical_attribute_type_mismatch",
215 Vec::new(),
216 "canonical attribute record does not match the requested generated type",
217 None,
218 ));
219 }
220 let scalar = crate::projected_codec::projected_to_decoded_attribute(&value)?;
221 let decoded = T::from_group_scalar(scalar).map_err(|error| {
222 Error::model_validation(
223 ModelValidationPhase::Input,
224 "canonical_attribute_materialization_failed",
225 Vec::new(),
226 "canonical attribute could not materialize as the requested generated type",
227 Some(Box::new(error)),
228 )
229 })?;
230 control.check()?;
231 Ok(decoded)
232 }
233
234 #[doc(hidden)]
236 #[must_use]
237 pub const fn new(
238 semantic_fingerprint_json: &'static str,
239 projection_fingerprint_json: &'static str,
240 runtime_projection_json: &'static str,
241 ) -> Self {
242 Self {
243 semantic_fingerprint_json,
244 projection_fingerprint_json,
245 runtime_projection_json,
246 declared_schema_json: None,
247 schema_authority_json: None,
248 managed_scope_id: None,
249 semantic_profile_id: None,
250 marker: PhantomData,
251 }
252 }
253
254 #[doc(hidden)]
257 #[must_use]
258 pub const fn new_with_declared(
259 semantic_fingerprint_json: &'static str,
260 projection_fingerprint_json: &'static str,
261 runtime_projection_json: &'static str,
262 declared_schema_json: &'static str,
263 ) -> Self {
264 Self {
265 semantic_fingerprint_json,
266 projection_fingerprint_json,
267 runtime_projection_json,
268 declared_schema_json: Some(declared_schema_json),
269 schema_authority_json: None,
270 managed_scope_id: None,
271 semantic_profile_id: None,
272 marker: PhantomData,
273 }
274 }
275
276 #[doc(hidden)]
279 #[must_use]
280 pub const fn new_with_authority(
281 semantic_fingerprint_json: &'static str,
282 projection_fingerprint_json: &'static str,
283 runtime_projection_json: &'static str,
284 schema_authority_json: &'static str,
285 declared_schema_json: &'static str,
286 managed_scope_id: &'static str,
287 semantic_profile_id: &'static str,
288 ) -> Self {
289 Self {
290 semantic_fingerprint_json,
291 projection_fingerprint_json,
292 runtime_projection_json,
293 declared_schema_json: Some(declared_schema_json),
294 schema_authority_json: Some(schema_authority_json),
295 managed_scope_id: Some(managed_scope_id),
296 semantic_profile_id: Some(semantic_profile_id),
297 marker: PhantomData,
298 }
299 }
300
301 pub fn encode_create<T>(&self, value: T) -> Result<Vec<u8>>
303 where
304 T: IntoEncodedCreate,
305 {
306 self.encode_create_controlled(value, &CanonicalCodecOptions::default())
307 }
308
309 pub fn encode_create_controlled<T>(
311 &self,
312 value: T,
313 options: &CanonicalCodecOptions,
314 ) -> Result<Vec<u8>>
315 where
316 T: IntoEncodedCreate,
317 {
318 let control = CapturedCanonicalCodecControl::capture(options, false)?;
319 control.check()?;
320 let installed = self.verify_and_install()?;
321 let projected = crate::projected_codec::project_create_inferred(value, &installed)?;
322 let record = type_bridge_orm::record_from_create(&installed, &projected)
323 .map_err(canonical_codec_error)?;
324 control.check_decoded_weight(record.decoded_weight())?;
325 control.check()?;
326 let bytes = record
327 .encode_with_limits(control.output_limits())
328 .map_err(canonical_output_error)?;
329 control.check()?;
330 Ok(bytes)
331 }
332
333 pub fn decode_create<T>(&self, bytes: &[u8]) -> Result<T>
335 where
336 T: MaterializeCreate<Schema = S>,
337 {
338 self.decode_create_controlled(bytes, &CanonicalCodecOptions::default())
339 }
340
341 pub fn decode_create_controlled<T>(
343 &self,
344 bytes: &[u8],
345 options: &CanonicalCodecOptions,
346 ) -> Result<T>
347 where
348 T: MaterializeCreate<Schema = S>,
349 {
350 let control = CapturedCanonicalCodecControl::capture(options, false)?;
351 control.check()?;
352 let installed = self.verify_and_install()?;
353 let record = type_bridge_contract::projected_record::ProjectedRecord::decode_with_limits(
354 bytes,
355 control.input_limits(),
356 )
357 .map_err(canonical_input_error)?;
358 control.check_decoded_weight(record.decoded_weight())?;
359 control.check()?;
360 let value = type_bridge_orm::materialize_record(&installed, &record)
361 .map_err(canonical_codec_error)?;
362 let type_bridge_orm::ProjectedCodecValue::Create(value) = value else {
363 return Err(Error::model_validation(
364 ModelValidationPhase::Input,
365 "canonical_record_kind_mismatch",
366 Vec::new(),
367 "canonical record is not a generated create payload",
368 None,
369 ));
370 };
371 let decoded = crate::projected_codec::projected_to_decoded_create(&value, &installed)?;
372 let decoded =
373 T::materialize_create(&decoded, &ValidationPath::root()).map_err(|error| {
374 Error::model_validation(
375 ModelValidationPhase::Input,
376 "canonical_create_materialization_failed",
377 Vec::new(),
378 "canonical create could not materialize as the requested generated type",
379 Some(Box::new(error)),
380 )
381 })?;
382 control.check()?;
383 Ok(decoded)
384 }
385
386 pub fn encode_reference<T>(&self, value: T) -> Result<Vec<u8>>
388 where
389 T: IntoEncodedReference,
390 {
391 self.encode_reference_controlled(value, &CanonicalCodecOptions::default())
392 }
393
394 pub fn encode_reference_controlled<T>(
396 &self,
397 value: T,
398 options: &CanonicalCodecOptions,
399 ) -> Result<Vec<u8>>
400 where
401 T: IntoEncodedReference,
402 {
403 let control = CapturedCanonicalCodecControl::capture(options, false)?;
404 control.check()?;
405 let installed = self.verify_and_install()?;
406 let projected = crate::projected_codec::project_reference_inferred(value, &installed)?;
407 let record = type_bridge_orm::record_from_reference(&installed, &projected)
408 .map_err(canonical_codec_error)?;
409 control.check_decoded_weight(record.decoded_weight())?;
410 control.check()?;
411 let bytes = record
412 .encode_with_limits(control.output_limits())
413 .map_err(canonical_output_error)?;
414 control.check()?;
415 Ok(bytes)
416 }
417
418 pub fn decode_reference<T>(&self, bytes: &[u8]) -> Result<T>
420 where
421 T: MaterializeReference<Schema = S>,
422 {
423 self.decode_reference_controlled(bytes, &CanonicalCodecOptions::default())
424 }
425
426 pub fn decode_reference_controlled<T>(
428 &self,
429 bytes: &[u8],
430 options: &CanonicalCodecOptions,
431 ) -> Result<T>
432 where
433 T: MaterializeReference<Schema = S>,
434 {
435 let control = CapturedCanonicalCodecControl::capture(options, false)?;
436 control.check()?;
437 let installed = self.verify_and_install()?;
438 let record = type_bridge_contract::projected_record::ProjectedRecord::decode_with_limits(
439 bytes,
440 control.input_limits(),
441 )
442 .map_err(canonical_input_error)?;
443 control.check_decoded_weight(record.decoded_weight())?;
444 control.check()?;
445 let value = type_bridge_orm::materialize_record(&installed, &record)
446 .map_err(canonical_codec_error)?;
447 let type_bridge_orm::ProjectedCodecValue::Reference(value) = value else {
448 return Err(Error::model_validation(
449 ModelValidationPhase::Input,
450 "canonical_record_kind_mismatch",
451 Vec::new(),
452 "canonical record is not a generated reference",
453 None,
454 ));
455 };
456 let decoded = crate::projected_codec::projected_to_hydrated_player(&value, &installed)?;
457 let decoded =
458 T::materialize_reference(&decoded, &ValidationPath::root()).map_err(|error| {
459 Error::model_validation(
460 ModelValidationPhase::Input,
461 "canonical_reference_materialization_failed",
462 Vec::new(),
463 "canonical reference could not materialize as the requested generated type",
464 Some(Box::new(error)),
465 )
466 })?;
467 control.check()?;
468 Ok(decoded)
469 }
470
471 pub fn encode_snapshot<T>(&self, value: T) -> Result<Vec<u8>>
473 where
474 T: IntoHydratedSnapshot + Model<Schema = S>,
475 {
476 self.encode_snapshot_controlled(value, &CanonicalCodecOptions::default())
477 }
478
479 pub fn encode_snapshot_controlled<T>(
481 &self,
482 value: T,
483 options: &CanonicalCodecOptions,
484 ) -> Result<Vec<u8>>
485 where
486 T: IntoHydratedSnapshot + Model<Schema = S>,
487 {
488 let control = CapturedCanonicalCodecControl::capture(options, false)?;
489 control.check()?;
490 let installed = self.verify_and_install()?;
491 let projected = crate::projected_codec::project_snapshot_inferred(value, &installed)?;
492 let record = type_bridge_orm::record_from_snapshot(&installed, &projected)
493 .map_err(canonical_codec_error)?;
494 control.check_decoded_weight(record.decoded_weight())?;
495 control.check()?;
496 let bytes = record
497 .encode_with_limits(control.output_limits())
498 .map_err(canonical_output_error)?;
499 control.check()?;
500 Ok(bytes)
501 }
502
503 pub fn decode_snapshot<T>(&self, bytes: &[u8]) -> Result<T>
505 where
506 T: CompleteModel<Schema = S> + MaterializeModel,
507 {
508 self.decode_snapshot_controlled(bytes, &CanonicalCodecOptions::default())
509 }
510
511 pub fn decode_snapshot_controlled<T>(
513 &self,
514 bytes: &[u8],
515 options: &CanonicalCodecOptions,
516 ) -> Result<T>
517 where
518 T: CompleteModel<Schema = S> + MaterializeModel,
519 {
520 let control = CapturedCanonicalCodecControl::capture(options, false)?;
521 control.check()?;
522 let installed = self.verify_and_install()?;
523 let record = type_bridge_contract::projected_record::ProjectedRecord::decode_with_limits(
524 bytes,
525 control.input_limits(),
526 )
527 .map_err(canonical_input_error)?;
528 control.check_decoded_weight(record.decoded_weight())?;
529 control.check()?;
530 let value = type_bridge_orm::materialize_record(&installed, &record)
531 .map_err(canonical_codec_error)?;
532 let type_bridge_orm::ProjectedCodecValue::Snapshot(value) = value else {
533 return Err(Error::model_validation(
534 ModelValidationPhase::Hydration,
535 "canonical_record_kind_mismatch",
536 Vec::new(),
537 "canonical record is not a generated hydrated snapshot",
538 None,
539 ));
540 };
541 let mut row = crate::projected_codec::projected_to_hydrated_row(&value, &installed)?;
542 row.mark_detached_snapshot();
543 let decoded = T::materialize(&row, &HydrationCapability::new()).map_err(|error| {
544 crate::entity_codec::map_validation_error(error, ModelValidationPhase::Hydration)
545 })?;
546 control.check()?;
547 Ok(decoded)
548 }
549
550 pub fn verify(&self) -> Result<()> {
553 let _ = self.verify_and_install_with_authority()?;
554 Ok(())
555 }
556
557 #[cfg(feature = "typedb")]
563 pub async fn connect(
564 self,
565 policy: type_bridge_orm::DirectConnectionPolicy,
566 ) -> Result<crate::session::Database<S>> {
567 self.connect_with_cancellation(policy, type_bridge_orm::AnswerCancellation::default())
568 .await
569 }
570
571 #[cfg(feature = "typedb")]
574 pub async fn connect_with_cancellation(
575 self,
576 policy: type_bridge_orm::DirectConnectionPolicy,
577 cancellation: type_bridge_orm::AnswerCancellation,
578 ) -> Result<crate::session::Database<S>> {
579 let (installed, authority) = self.verify_and_install_with_authority()?;
580 let match_registry = crate::session::build_match_registry(&installed)?;
581 let inner =
582 type_bridge_orm::Database::connect_direct(installed.as_ref(), &policy, cancellation)
583 .await
584 .map_err(Error::from_direct_connection)?;
585 Ok(crate::session::Database::from_bound_parts(
586 inner,
587 installed,
588 match_registry,
589 authority.map(|authority| authority.managed_scope().id().clone()),
590 ))
591 }
592
593 #[doc(hidden)]
597 pub fn generated_projection_validator(
598 &self,
599 ) -> std::result::Result<GeneratedProjectionValidator, ValidationError> {
600 self.verify_and_install()
601 .map(|installed| GeneratedProjectionValidator { installed })
602 .map_err(|_| {
603 ValidationError::new("projection_evidence", "projection_evidence_mismatch")
604 })
605 }
606
607 pub fn encode_struct<T>(&self, value: T) -> Result<Vec<u8>>
609 where
610 T: IntoEncodedStruct + StructValue<Schema = S>,
611 {
612 self.encode_struct_controlled(value, &CanonicalCodecOptions::default())
613 }
614
615 pub fn encode_struct_controlled<T>(
617 &self,
618 value: T,
619 options: &CanonicalCodecOptions,
620 ) -> Result<Vec<u8>>
621 where
622 T: IntoEncodedStruct + StructValue<Schema = S>,
623 {
624 let control = CapturedCanonicalCodecControl::capture(options, false)?;
625 control.check()?;
626 let installed = self.verify_and_install()?;
627 let projected = crate::projected_codec::project_struct_inferred(value, &installed)?;
628 let record = type_bridge_orm::record_from_struct(&installed, &projected)
629 .map_err(canonical_codec_error)?;
630 control.check_decoded_weight(record.decoded_weight())?;
631 control.check()?;
632 let bytes = record
633 .encode_with_limits(control.output_limits())
634 .map_err(canonical_output_error)?;
635 control.check()?;
636 Ok(bytes)
637 }
638
639 pub fn decode_struct<T>(&self, bytes: &[u8]) -> Result<T>
641 where
642 T: MaterializeStruct + StructValue<Schema = S>,
643 {
644 self.decode_struct_controlled(bytes, &CanonicalCodecOptions::default())
645 }
646
647 pub fn decode_struct_controlled<T>(
649 &self,
650 bytes: &[u8],
651 options: &CanonicalCodecOptions,
652 ) -> Result<T>
653 where
654 T: MaterializeStruct + StructValue<Schema = S>,
655 {
656 let control = CapturedCanonicalCodecControl::capture(options, false)?;
657 control.check()?;
658 let installed = self.verify_and_install()?;
659 let record = type_bridge_contract::projected_record::ProjectedRecord::decode_with_limits(
660 bytes,
661 control.input_limits(),
662 )
663 .map_err(canonical_input_error)?;
664 control.check_decoded_weight(record.decoded_weight())?;
665 control.check()?;
666 let value = type_bridge_orm::materialize_record(&installed, &record)
667 .map_err(canonical_codec_error)?;
668 let type_bridge_orm::ProjectedCodecValue::Struct(value) = value else {
669 return Err(Error::model_validation(
670 ModelValidationPhase::Input,
671 "canonical_record_kind_mismatch",
672 Vec::new(),
673 "canonical record is not a generated struct",
674 None,
675 ));
676 };
677 let decoded = crate::projected_codec::projected_to_decoded_struct(&value, &installed)?;
678 let decoded =
679 T::materialize_struct(&decoded, &ValidationPath::root()).map_err(|error| {
680 Error::model_validation(
681 ModelValidationPhase::Input,
682 "canonical_struct_materialization_failed",
683 Vec::new(),
684 "canonical struct could not materialize as the requested generated type",
685 Some(Box::new(error)),
686 )
687 })?;
688 control.check()?;
689 Ok(decoded)
690 }
691
692 pub fn encode_archive<I, B>(&self, records: I) -> Result<Vec<u8>>
694 where
695 I: IntoIterator<Item = B>,
696 B: AsRef<[u8]>,
697 {
698 self.encode_archive_controlled(records, &CanonicalCodecOptions::default())
699 }
700
701 pub fn encode_archive_controlled<I, B>(
703 &self,
704 records: I,
705 options: &CanonicalCodecOptions,
706 ) -> Result<Vec<u8>>
707 where
708 I: IntoIterator<Item = B>,
709 B: AsRef<[u8]>,
710 {
711 let control = CapturedCanonicalCodecControl::capture(options, true)?;
712 control.check()?;
713 let installed = self.verify_and_install()?;
714 let mut verified = Vec::new();
715 for bytes in records {
716 control.check_record_count(verified.len().saturating_add(1))?;
717 control.check()?;
718 let record =
719 type_bridge_contract::projected_record::ProjectedRecord::decode_with_limits(
720 bytes.as_ref(),
721 control.input_limits(),
722 )
723 .map_err(canonical_input_error)?;
724 let _ = type_bridge_orm::materialize_record(&installed, &record)
725 .map_err(canonical_codec_error)?;
726 verified.push(record);
727 }
728 let archive = type_bridge_contract::projected_record::ProjectedArchive::try_new(verified)
729 .map_err(canonical_contract_error)?;
730 control.check_decoded_weight(archive.decoded_weight())?;
731 control.check()?;
732 let bytes = archive
733 .encode_with_limits(control.output_limits())
734 .map_err(canonical_output_error)?;
735 control.check()?;
736 Ok(bytes)
737 }
738
739 pub fn decode_archive(&self, bytes: &[u8]) -> Result<Vec<Vec<u8>>> {
741 self.decode_archive_controlled(bytes, &CanonicalCodecOptions::default())
742 }
743
744 pub fn decode_archive_controlled(
746 &self,
747 bytes: &[u8],
748 options: &CanonicalCodecOptions,
749 ) -> Result<Vec<Vec<u8>>> {
750 let control = CapturedCanonicalCodecControl::capture(options, true)?;
751 control.check()?;
752 let installed = self.verify_and_install()?;
753 let archive = type_bridge_contract::projected_record::ProjectedArchive::decode_with_limits(
754 bytes,
755 control.input_limits(),
756 )
757 .map_err(canonical_input_error)?;
758 control.check_record_count(archive.records().len())?;
759 control.check_decoded_weight(archive.decoded_weight())?;
760 let mut records = Vec::with_capacity(archive.records().len());
761 let mut output_bytes = 0_usize;
762 for record in archive.records() {
763 control.check()?;
764 let _ = type_bridge_orm::materialize_record(&installed, record)
765 .map_err(canonical_codec_error)?;
766 let encoded = record
767 .encode_with_limits(control.output_limits())
768 .map_err(canonical_output_error)?;
769 output_bytes = output_bytes.saturating_add(encoded.len());
770 control.check_output_bytes(output_bytes)?;
771 records.push(encoded);
772 }
773 control.check()?;
774 Ok(records)
775 }
776
777 #[doc(hidden)]
779 #[must_use]
780 pub const fn semantic_fingerprint_json(&self) -> &'static str {
781 self.semantic_fingerprint_json
782 }
783
784 #[doc(hidden)]
786 #[must_use]
787 pub const fn projection_fingerprint_json(&self) -> &'static str {
788 self.projection_fingerprint_json
789 }
790
791 #[doc(hidden)]
793 #[must_use]
794 pub const fn runtime_projection_json(&self) -> &'static str {
795 self.runtime_projection_json
796 }
797
798 pub(crate) const fn declared_schema_json(&self) -> Option<&'static str> {
799 self.declared_schema_json
800 }
801
802 pub(crate) fn verify_and_install(
805 &self,
806 ) -> Result<Arc<type_bridge_orm::InstalledRuntimeProjection>> {
807 self.verify_and_install_with_authority()
808 .map(|(projection, _authority)| projection)
809 }
810
811 pub(crate) fn verify_and_install_with_authority(
812 &self,
813 ) -> Result<(
814 Arc<type_bridge_orm::InstalledRuntimeProjection>,
815 Option<VerifiedSchemaAuthority>,
816 )> {
817 let authority_backed = self.schema_authority_json.is_some();
818 let semantic_fingerprint_presence = if self.semantic_fingerprint_json.is_empty() {
823 SdkProjectionEvidenceSlotPresence::Absent
824 } else {
825 SdkProjectionEvidenceSlotPresence::Present
826 };
827 let authority = self.verify_embedded_authority().map_err(|error| {
828 classify_admission_error(authority_backed, semantic_fingerprint_presence, error)
829 })?;
830 let mut projection = type_bridge_orm::InstalledRuntimeProjection::from_verified_rust_json(
831 self.runtime_projection_json.as_bytes(),
832 self.semantic_fingerprint_json.as_bytes(),
833 self.projection_fingerprint_json.as_bytes(),
834 )
835 .map_err(|err| {
836 classify_admission_error(
837 authority_backed,
838 semantic_fingerprint_presence,
839 Error::SchemaVerification {
840 message: err.to_string(),
841 source: Some(Box::new(err)),
842 },
843 )
844 })?;
845 if let Some(authority) = authority.as_ref() {
846 projection = projection.with_declared_schema_identity(
847 authority
848 .resolved_schema()
849 .declared_identity_fingerprint()
850 .clone(),
851 );
852 }
853 let successor =
854 authority_backed || projection_uses_ordered_collections(projection.projection());
855 if authority.as_ref().is_some_and(|authority| {
856 authority.resolved_schema().semantic_fingerprint()
857 != projection.projection().semantic_fingerprint()
858 }) {
859 return Err(classify_admission_error(
860 successor,
861 semantic_fingerprint_presence,
862 authority_error(
863 "generated schema authority does not match the installed runtime projection",
864 ),
865 ));
866 }
867 verify_rust_projection_evidence(&projection, authority.as_ref()).map_err(|error| {
868 classify_admission_error(successor, semantic_fingerprint_presence, error)
869 })?;
870 Ok((Arc::new(projection), authority))
871 }
872
873 fn verify_embedded_authority(&self) -> Result<Option<VerifiedSchemaAuthority>> {
874 let parts = (
875 self.schema_authority_json,
876 self.declared_schema_json,
877 self.managed_scope_id,
878 self.semantic_profile_id,
879 );
880 let (Some(envelope), Some(declared), Some(scope), Some(profile)) = parts else {
881 if parts.0.is_none() && parts.2.is_none() && parts.3.is_none() {
882 return Ok(None);
883 }
884 return Err(authority_error(
885 "generated schema package contains incomplete compiled authority evidence",
886 ));
887 };
888 if envelope.len() > MAX_SCHEMA_AUTHORITY_BYTES {
889 return Err(authority_error(
890 "generated schema authority exceeds the canonical byte ceiling",
891 ));
892 }
893 let authority = decode_schema_authority(
894 envelope.as_bytes(),
895 &schema_authority_capability_vocabulary(),
896 )
897 .map_err(|error| Error::SchemaVerification {
898 message: format!(
899 "generated schema package contains invalid compiled authority ({:?})",
900 error.code()
901 ),
902 source: Some(Box::new(error)),
903 })?;
904 let reconstructed_declared =
905 encode_declared_schema(authority.declared_schema()).map_err(|error| {
906 Error::SchemaVerification {
907 message: "generated schema authority declaration cannot be reconstructed"
908 .into(),
909 source: Some(Box::new(error)),
910 }
911 })?;
912 if reconstructed_declared != declared.as_bytes()
913 || authority.managed_scope().id().as_str() != scope
914 || authority.semantic_profile().id().as_str() != profile
915 {
916 return Err(authority_error(
917 "generated schema authority disagrees with its extracted query evidence",
918 ));
919 }
920 Ok(Some(authority))
921 }
922}
923
924fn verify_rust_projection_evidence(
925 installed: &type_bridge_orm::InstalledRuntimeProjection,
926 authority: Option<&VerifiedSchemaAuthority>,
927) -> Result<()> {
928 let projection = installed.projection();
929 let emitter = RustEmitter::new();
930 if projection.config().rust_naming_policy() != ProjectionConfig::rust().rust_naming_policy()
931 || projection.config().rust_create_policy() != ProjectionConfig::rust().rust_create_policy()
932 {
933 return Err(authority_error(
934 "generated Rust schema package does not match the exact shipped projection configuration",
935 ));
936 }
937 if let Some(authority) = authority {
938 return type_bridge_schema_codegen::verify_projection_evidence(authority, projection)
939 .map_err(|error| Error::SchemaVerification {
940 message: "generated Rust schema package does not match compiled schema authority and exact shipped emitter evidence"
941 .into(),
942 source: Some(Box::new(error)),
943 });
944 }
945
946 if projection_uses_ordered_collections(projection) {
947 return Err(authority_error(
948 "ordered Rust schema packages require compiled schema authority",
949 ));
950 }
951 let resources = emitter
952 .code_resources()
953 .map_err(|error| Error::SchemaVerification {
954 message: "legacy Rust schema package resource evidence cannot be reconstructed".into(),
955 source: Some(Box::new(error)),
956 })?;
957 if projection.generator_handlers() != emitter.generator_handlers()
958 || projection.code_resources() != resources
959 {
960 return Err(authority_error(
961 "legacy Rust schema package does not match the exact shipped handler and resource evidence",
962 ));
963 }
964
965 Ok(())
966}
967
968fn projection_uses_ordered_collections(projection: &RuntimeProjection) -> bool {
969 projection.models().values().any(|model| {
970 model
971 .query_tokens()
972 .fields()
973 .values()
974 .any(|field| !field.multiplicity().collection_mode().is_unordered())
975 || model
976 .query_tokens()
977 .roles()
978 .values()
979 .any(|role| !role.multiplicity().collection_mode().is_unordered())
980 })
981}
982
983fn authority_error(message: &'static str) -> Error {
984 Error::SchemaVerification {
985 message: message.into(),
986 source: None,
987 }
988}
989
990fn projection_evidence_error(presence: SdkProjectionEvidenceSlotPresence) -> Error {
991 Error::projection_evidence_rejection(presence)
992}
993
994fn classify_admission_error(
995 successor: bool,
996 semantic_fingerprint_presence: SdkProjectionEvidenceSlotPresence,
997 error: Error,
998) -> Error {
999 if successor {
1000 projection_evidence_error(semantic_fingerprint_presence)
1001 } else {
1002 error
1003 }
1004}
1005
1006#[cfg(test)]
1007mod tests {
1008 use super::*;
1009 use serde_json::Value;
1010 use type_bridge_contract::codec::to_canonical_json;
1011 use type_bridge_contract::fingerprint::{
1012 CanonicalizationVersion, Fingerprint, FingerprintDomain, SemanticProfileId,
1013 };
1014 use type_bridge_contract::managed_scope::ManagedScopeId;
1015 use type_bridge_contract::projection::{BindingTarget, ProjectionConfig};
1016 use type_bridge_contract::schema::{DocumentId, encode_declared_schema};
1017 use type_bridge_contract::sdk_diagnostic::{
1018 SdkDiagnosticCategory, SdkDiagnosticDetailValue, SdkDiagnosticPathSegment,
1019 SdkExecutionDiagnostic,
1020 };
1021 use type_bridge_schema::{
1022 ManagedDeltaContext, SCHEMA_AUTHORITY_FINGERPRINT_CANONICALIZATION,
1023 SCHEMA_AUTHORITY_FINGERPRINT_DOMAIN, SchemaDocumentSet, build_schema_authority,
1024 encode_schema_authority, normalize_documents, project, resolve,
1025 };
1026 use type_bridge_schema_codegen::{PythonEmitter, RustEmitter};
1027 use type_bridge_schema_migration::{
1028 MigrationHistoryGraph, VerifiedMigrationHistoryBundle,
1029 encode_verified_migration_history_bundle,
1030 };
1031
1032 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
1033 struct TestSchema;
1034 impl sealed::Sealed for TestSchema {}
1035 impl Schema for TestSchema {}
1036
1037 fn leak(bytes: Vec<u8>) -> &'static str {
1038 Box::leak(String::from_utf8(bytes).unwrap().into_boxed_str())
1039 }
1040
1041 fn generated_package(source: &str, scope: &str) -> SchemaPackage<TestSchema> {
1042 package_with_evidence(source, scope, None, None)
1043 }
1044
1045 fn package_with_evidence(
1046 source: &str,
1047 scope: &str,
1048 handlers: Option<Vec<type_bridge_contract::projection::ProjectionHandler>>,
1049 resources: Option<Vec<type_bridge_contract::projection::CodeResourceDigest>>,
1050 ) -> SchemaPackage<TestSchema> {
1051 let documents =
1052 SchemaDocumentSet::parse([(DocumentId::new("authority-test.yaml").unwrap(), source)])
1053 .unwrap();
1054 let declared = normalize_documents(&documents).unwrap();
1055 let profile = SemanticProfileId::new("typedb-3.12.1/v1").unwrap();
1056 let resolved = resolve(&declared, &profile).unwrap();
1057 let authority = build_schema_authority(
1058 &declared,
1059 declared.required_capabilities(),
1060 &ManagedDeltaContext::new(
1061 ManagedScopeId::new(scope).unwrap(),
1062 profile,
1063 schema_authority_capability_vocabulary(),
1064 ),
1065 )
1066 .unwrap();
1067 let emitter = RustEmitter::new();
1068 let handlers = handlers.unwrap_or_else(|| emitter.generator_handlers_for(&resolved));
1069 let resources = resources.unwrap_or_else(|| emitter.code_resources_for(&resolved).unwrap());
1070 let projection = project(
1071 &resolved,
1072 BindingTarget::Rust,
1073 &ProjectionConfig::rust(),
1074 &handlers,
1075 &resources,
1076 )
1077 .unwrap();
1078 SchemaPackage::new_with_authority(
1079 leak(to_canonical_json(projection.semantic_fingerprint()).unwrap()),
1080 leak(to_canonical_json(projection.projection_fingerprint()).unwrap()),
1081 leak(to_canonical_json(&projection).unwrap()),
1082 leak(encode_schema_authority(&authority)),
1083 leak(encode_declared_schema(&declared).unwrap()),
1084 Box::leak(scope.to_owned().into_boxed_str()),
1085 "typedb-3.12.1/v1",
1086 )
1087 }
1088
1089 fn without_authority(package: SchemaPackage<TestSchema>) -> SchemaPackage<TestSchema> {
1090 SchemaPackage::new(
1091 package.semantic_fingerprint_json,
1092 package.projection_fingerprint_json,
1093 package.runtime_projection_json,
1094 )
1095 }
1096
1097 fn with_runtime_projection(
1098 package: SchemaPackage<TestSchema>,
1099 runtime_projection_json: &'static str,
1100 ) -> SchemaPackage<TestSchema> {
1101 SchemaPackage::new_with_authority(
1102 package.semantic_fingerprint_json,
1103 package.projection_fingerprint_json,
1104 runtime_projection_json,
1105 package.schema_authority_json.unwrap(),
1106 package.declared_schema_json.unwrap(),
1107 package.managed_scope_id.unwrap(),
1108 package.semantic_profile_id.unwrap(),
1109 )
1110 }
1111
1112 fn with_envelope(
1113 package: SchemaPackage<TestSchema>,
1114 envelope: &'static str,
1115 ) -> SchemaPackage<TestSchema> {
1116 SchemaPackage::new_with_authority(
1117 package.semantic_fingerprint_json,
1118 package.projection_fingerprint_json,
1119 package.runtime_projection_json,
1120 envelope,
1121 package.declared_schema_json.unwrap(),
1122 package.managed_scope_id.unwrap(),
1123 package.semantic_profile_id.unwrap(),
1124 )
1125 }
1126
1127 fn with_semantic_fingerprint(
1128 package: SchemaPackage<TestSchema>,
1129 semantic_fingerprint_json: &'static str,
1130 ) -> SchemaPackage<TestSchema> {
1131 SchemaPackage::new_with_authority(
1132 semantic_fingerprint_json,
1133 package.projection_fingerprint_json,
1134 package.runtime_projection_json,
1135 package.schema_authority_json.unwrap(),
1136 package.declared_schema_json.unwrap(),
1137 package.managed_scope_id.unwrap(),
1138 package.semantic_profile_id.unwrap(),
1139 )
1140 }
1141
1142 fn canonical(value: &Value) -> &'static str {
1143 leak(to_canonical_json(value).unwrap())
1144 }
1145
1146 #[test]
1147 fn authority_backed_package_opens_and_fences_generated_migration_catalogs_offline() {
1148 let package = generated_package(
1149 "format: typebridge.schema/v2\nentities:\n person: {}\n",
1150 "rust-migration-catalog",
1151 );
1152 let graph = MigrationHistoryGraph::from_verified(std::iter::empty::<
1153 type_bridge_schema_migration::VerifiedSchemaMigrationManifest,
1154 >())
1155 .unwrap();
1156 let bundle = VerifiedMigrationHistoryBundle::from_graph(&graph).unwrap();
1157 let bytes = encode_verified_migration_history_bundle(&bundle).unwrap();
1158 let catalog = package
1159 .open_migration_catalog(&bytes)
1160 .expect("verified generated bundle opens without provider I/O");
1161 assert!(catalog.is_empty());
1162 assert_eq!(catalog.fingerprint(), bundle.fingerprint());
1163 let preview = catalog
1164 .preview_apply(Vec::new(), None)
1165 .expect("empty generated catalog previews without provider I/O");
1166 assert!(preview.is_apply());
1167 assert!(preview.is_empty());
1168 assert!(!preview.execution_authorized());
1169 let approvals = preview
1170 .approval_builder()
1171 .finish()
1172 .expect("empty exact approval set freezes");
1173 assert!(approvals.is_empty());
1174 let executable = preview
1175 .authorize(&approvals)
1176 .expect("fresh plan is authorized from the exact preview owner");
1177 assert!(executable.execution_authorized());
1178 let foreign_preview = catalog.preview_apply(Vec::new(), None).unwrap();
1179 let mismatch = foreign_preview
1180 .authorize(&approvals)
1181 .expect_err("approval owner mismatch rejects");
1182 assert!(
1183 mismatch
1184 .message()
1185 .contains("migration_approval_plan_mismatch")
1186 );
1187
1188 let mut tampered: Value = serde_json::from_slice(&bytes).unwrap();
1189 tampered["format"] = Value::String("foreign.history/v1".to_owned());
1190 let tampered = to_canonical_json(&tampered).unwrap();
1191 let error = package
1192 .open_migration_catalog(&tampered)
1193 .expect_err("foreign bundle rejects before provider I/O");
1194 assert!(matches!(error, Error::SchemaVerification { .. }));
1195 }
1196
1197 fn resign(value: &mut Value) {
1198 let content = to_canonical_json(&value["content"]).unwrap();
1199 let fingerprint = Fingerprint::compute(
1200 FingerprintDomain::new(SCHEMA_AUTHORITY_FINGERPRINT_DOMAIN).unwrap(),
1201 CanonicalizationVersion::new(SCHEMA_AUTHORITY_FINGERPRINT_CANONICALIZATION).unwrap(),
1202 None,
1203 &content,
1204 );
1205 value["authority_fingerprint"] = serde_json::to_value(fingerprint).unwrap();
1206 }
1207
1208 fn assert_projection_evidence_mismatch(error: &Error) {
1209 assert_eq!(error.category(), crate::ErrorCategory::Integrity);
1210 assert_eq!(error.model_validation_phase(), None);
1211 assert_eq!(error.code(), Some("projection_evidence_mismatch"));
1212 assert_eq!(
1213 error.path().expect("evidence mismatch has a stable path"),
1214 ["projection_evidence"],
1215 );
1216 assert_eq!(
1217 error.message(),
1218 "Generated projection evidence does not match the verified schema package",
1219 );
1220 assert!(matches!(
1221 error
1222 .diagnostic_path()
1223 .expect("evidence mismatch retains its typed path"),
1224 [crate::ErrorPathSegment::Argument(name)] if name == "projection_evidence"
1225 ));
1226 assert!(
1227 error
1228 .details()
1229 .expect("evidence mismatch retains typed details")
1230 .is_empty()
1231 );
1232
1233 let diagnostic = std::error::Error::source(error)
1234 .and_then(|source| source.downcast_ref::<SdkExecutionDiagnostic>())
1235 .expect("the public Rust error retains the common SDK diagnostic");
1236 assert_eq!(diagnostic.category(), SdkDiagnosticCategory::Integrity);
1237 assert!(matches!(
1238 diagnostic.path(),
1239 [SdkDiagnosticPathSegment::Argument(name)]
1240 if name.as_str() == "projection_evidence"
1241 ));
1242 assert!(diagnostic.details().is_empty());
1243 }
1244
1245 fn assert_missing_semantic_fingerprint(error: &Error) {
1246 assert_eq!(error.category(), crate::ErrorCategory::Integrity);
1247 assert_eq!(error.model_validation_phase(), None);
1248 assert_eq!(error.code(), Some("projection_evidence_mismatch"));
1249 assert_eq!(
1250 error.path().expect("missing evidence has a stable path"),
1251 ["projection_evidence", "[0]", "semantic_schema_fingerprint",],
1252 );
1253 assert!(matches!(
1254 error
1255 .diagnostic_path()
1256 .expect("missing evidence retains its typed path"),
1257 [
1258 crate::ErrorPathSegment::Argument(argument),
1259 crate::ErrorPathSegment::Index(0),
1260 crate::ErrorPathSegment::ContractIdentity(identity),
1261 ] if argument == "projection_evidence"
1262 && identity == "semantic_schema_fingerprint"
1263 ));
1264 assert_eq!(
1265 error
1266 .details()
1267 .expect("missing evidence retains typed details"),
1268 &std::collections::BTreeMap::from([
1269 (
1270 "actual_occurrence_count".to_owned(),
1271 crate::ErrorDetail::Long(0),
1272 ),
1273 (
1274 "expected_occurrence_count".to_owned(),
1275 crate::ErrorDetail::Long(1),
1276 ),
1277 (
1278 "foreign_package".to_owned(),
1279 crate::ErrorDetail::Boolean(false),
1280 ),
1281 ]),
1282 );
1283
1284 let diagnostic = std::error::Error::source(error)
1285 .and_then(|source| source.downcast_ref::<SdkExecutionDiagnostic>())
1286 .expect("the Rust error retains the exact common SDK diagnostic");
1287 assert!(matches!(
1288 diagnostic.path(),
1289 [
1290 SdkDiagnosticPathSegment::Argument(argument),
1291 SdkDiagnosticPathSegment::Index(0),
1292 SdkDiagnosticPathSegment::ContractIdentity(identity),
1293 ] if argument.as_str() == "projection_evidence"
1294 && identity.as_str() == "semantic_schema_fingerprint"
1295 ));
1296 assert_eq!(
1297 diagnostic
1298 .details()
1299 .iter()
1300 .map(|(name, value)| (name.as_str(), value))
1301 .collect::<Vec<_>>(),
1302 vec![
1303 (
1304 "actual_occurrence_count",
1305 &SdkDiagnosticDetailValue::Count(0),
1306 ),
1307 (
1308 "expected_occurrence_count",
1309 &SdkDiagnosticDetailValue::Count(1),
1310 ),
1311 ("foreign_package", &SdkDiagnosticDetailValue::Boolean(false),),
1312 ],
1313 );
1314 }
1315
1316 #[test]
1317 fn compiled_authority_is_fully_verified_and_bound_to_projection() {
1318 let package = generated_package(
1319 "format: typebridge.schema/v2\nentities:\n person: {}\n",
1320 "rust-authority-test",
1321 );
1322 let (projection, authority) = package.verify_and_install_with_authority().unwrap();
1323 let authority = authority.expect("generated package has compiled authority");
1324 assert_eq!(
1325 projection.projection().semantic_fingerprint(),
1326 authority.resolved_schema().semantic_fingerprint(),
1327 );
1328
1329 let foreign = generated_package(
1330 "format: typebridge.schema/v2\nentities:\n organization: {}\n",
1331 "rust-authority-test",
1332 );
1333 let mismatched: SchemaPackage<TestSchema> = SchemaPackage::new_with_authority(
1334 package.semantic_fingerprint_json,
1335 package.projection_fingerprint_json,
1336 package.runtime_projection_json,
1337 foreign.schema_authority_json.unwrap(),
1338 foreign.declared_schema_json.unwrap(),
1339 foreign.managed_scope_id.unwrap(),
1340 foreign.semantic_profile_id.unwrap(),
1341 );
1342 let error = mismatched
1343 .verify()
1344 .expect_err("foreign semantic authority must not bind to the projection");
1345 assert_projection_evidence_mismatch(&error);
1346 }
1347
1348 #[test]
1349 fn ordered_package_requires_exact_successor_handler_and_compiled_authority() {
1350 const ORDERED: &str = "format: typebridge.schema/v2\nattributes:\n tag: { value: string }\nentities:\n person:\n owns:\n tag:\n card: 1\n ordered: true\n distinct: true\n";
1351
1352 let valid = generated_package(ORDERED, "rust-ordered-evidence");
1353 let installed = valid.verify_and_install().unwrap();
1354 assert_eq!(
1355 installed.projection().generator_handlers(),
1356 [type_bridge_contract::projection::ProjectionHandler::rust_v2()],
1357 );
1358 let resource_ids = installed
1359 .projection()
1360 .code_resources()
1361 .iter()
1362 .map(|resource| resource.id().as_str())
1363 .collect::<Vec<_>>();
1364 assert_eq!(
1365 resource_ids,
1366 [
1367 "typebridge.generator.rust.cargo-toml",
1368 "typebridge.generator.rust.runtime-source",
1369 ],
1370 );
1371 assert_ne!(
1372 installed.projection().code_resources(),
1373 RustEmitter::new().code_resources().unwrap(),
1374 "the ordered package must carry the successor runtime-resource digest",
1375 );
1376
1377 let legacy = package_with_evidence(
1378 ORDERED,
1379 "rust-ordered-evidence",
1380 Some(vec![
1381 type_bridge_contract::projection::ProjectionHandler::rust_v1(),
1382 ]),
1383 Some(RustEmitter::new().code_resources().unwrap()),
1384 );
1385 let error = legacy
1386 .verify()
1387 .expect_err("an ordered package cannot claim the legacy Rust ledger");
1388 assert_projection_evidence_mismatch(&error);
1389
1390 let authorityless = without_authority(valid);
1391 let error = authorityless
1392 .verify()
1393 .expect_err("ordered packages require reconstructable schema authority");
1394 assert_projection_evidence_mismatch(&error);
1395
1396 let diagnostic = authorityless
1397 .generated_projection_validator()
1398 .err()
1399 .expect("ordered successor validation must reject the detached package");
1400 assert_eq!(diagnostic.code(), "projection_evidence_mismatch");
1401 assert_eq!(diagnostic.field(), "projection_evidence");
1402 }
1403
1404 #[test]
1405 fn package_rejects_missing_forged_and_foreign_resource_evidence() {
1406 const UNORDERED: &str = "format: typebridge.schema/v2\nentities:\n person: {}\n";
1407 const ORDERED: &str = "format: typebridge.schema/v2\nattributes:\n tag: { value: string }\nentities:\n person:\n owns:\n tag:\n card: 1\n ordered: true\n";
1408
1409 let missing = package_with_evidence(
1410 ORDERED,
1411 "rust-resource-evidence",
1412 Some(vec![
1413 type_bridge_contract::projection::ProjectionHandler::rust_v2(),
1414 ]),
1415 Some(Vec::new()),
1416 );
1417 let error = missing
1418 .verify()
1419 .expect_err("ordered resource evidence is mandatory");
1420 assert_projection_evidence_mismatch(&error);
1421
1422 let emitter = RustEmitter::new();
1423 let documents =
1424 SchemaDocumentSet::parse([(DocumentId::new("forged-resource.yaml").unwrap(), ORDERED)])
1425 .unwrap();
1426 let declared = normalize_documents(&documents).unwrap();
1427 let profile = SemanticProfileId::new("typedb-3.12.1/v1").unwrap();
1428 let resolved = resolve(&declared, &profile).unwrap();
1429 let mut forged_resources = emitter.code_resources_for(&resolved).unwrap();
1430 let first_id = forged_resources[0].id().as_str().to_owned();
1431 forged_resources[0] = type_bridge_contract::projection::CodeResourceDigest::from_bytes(
1432 first_id,
1433 b"forged Rust emitter resource",
1434 )
1435 .unwrap();
1436 let forged = package_with_evidence(
1437 ORDERED,
1438 "rust-resource-evidence",
1439 Some(emitter.generator_handlers_for(&resolved)),
1440 Some(forged_resources),
1441 );
1442 let error = forged
1443 .verify()
1444 .expect_err("self-consistent forged resource evidence must reject");
1445 assert_projection_evidence_mismatch(&error);
1446
1447 let foreign_resources = type_bridge_schema_codegen::PythonEmitter::new()
1448 .code_resources_for(&resolved)
1449 .unwrap();
1450 let foreign = package_with_evidence(
1451 ORDERED,
1452 "rust-resource-evidence",
1453 Some(emitter.generator_handlers_for(&resolved)),
1454 Some(foreign_resources),
1455 );
1456 let error = foreign
1457 .verify()
1458 .expect_err("foreign binding resource evidence must reject");
1459 assert_projection_evidence_mismatch(&error);
1460
1461 let valid_legacy = generated_package(UNORDERED, "rust-resource-evidence");
1462 let installed_legacy = valid_legacy.verify_and_install().unwrap();
1463 assert_eq!(
1464 installed_legacy.projection().generator_handlers(),
1465 [type_bridge_contract::projection::ProjectionHandler::rust_v1()],
1466 );
1467 assert_eq!(
1468 installed_legacy.projection().code_resources(),
1469 RustEmitter::new().code_resources().unwrap(),
1470 );
1471 assert!(without_authority(valid_legacy).verify().is_ok());
1472 }
1473
1474 #[test]
1475 fn successor_admission_classifies_only_absent_detached_semantic_fingerprint() {
1476 let package = generated_package(
1477 "format: typebridge.schema/v2\nattributes:\n tag: { value: string }\nentities:\n person:\n owns:\n tag:\n card: 1\n ordered: true\n",
1478 "rust-missing-semantic-evidence",
1479 );
1480 let malformed = with_semantic_fingerprint(package, "{")
1481 .verify()
1482 .expect_err("nonempty malformed semantic evidence must reject");
1483 assert_projection_evidence_mismatch(&malformed);
1484
1485 let error = with_semantic_fingerprint(package, "")
1486 .verify()
1487 .expect_err("an absent detached semantic fingerprint must reject");
1488 assert_missing_semantic_fingerprint(&error);
1489 }
1490
1491 #[test]
1492 fn package_rejects_stale_and_reordered_evidence() {
1493 const UNORDERED: &str = "format: typebridge.schema/v2\nentities:\n person: {}\n";
1494 const ORDERED: &str = "format: typebridge.schema/v2\nattributes:\n tag: { value: string }\nentities:\n person:\n owns:\n tag:\n card: 1\n ordered: true\n";
1495
1496 let emitter = RustEmitter::new();
1497 let ordered_documents =
1498 SchemaDocumentSet::parse([(DocumentId::new("stale-resource.yaml").unwrap(), ORDERED)])
1499 .unwrap();
1500 let ordered_declared = normalize_documents(&ordered_documents).unwrap();
1501 let profile = SemanticProfileId::new("typedb-3.12.1/v1").unwrap();
1502 let ordered_resolved = resolve(&ordered_declared, &profile).unwrap();
1503 let stale_successor = package_with_evidence(
1504 UNORDERED,
1505 "rust-stale-evidence",
1506 Some(emitter.generator_handlers_for(&ordered_resolved)),
1507 Some(emitter.code_resources_for(&ordered_resolved).unwrap()),
1508 );
1509 let error = stale_successor
1510 .verify()
1511 .expect_err("an unordered package cannot claim successor evidence");
1512 assert_projection_evidence_mismatch(&error);
1513
1514 let ordered = generated_package(ORDERED, "rust-reordered-evidence");
1515 let mut runtime: Value = serde_json::from_str(ordered.runtime_projection_json).unwrap();
1516 runtime["code_resources"]
1517 .as_array_mut()
1518 .expect("runtime projection has a resource ledger")
1519 .swap(0, 1);
1520 let reordered = with_runtime_projection(ordered, canonical(&runtime));
1521 let error = reordered
1522 .verify()
1523 .expect_err("resource ledger wire order is canonical and cannot be changed");
1524 assert_projection_evidence_mismatch(&error);
1525 }
1526
1527 #[test]
1528 fn authority_backed_admission_normalizes_malformed_extra_and_duplicate_evidence() {
1529 const ORDERED: &str = "format: typebridge.schema/v2\nattributes:\n tag: { value: string }\nentities:\n person:\n owns:\n tag:\n card: 1\n ordered: true\n";
1530 let package = generated_package(ORDERED, "rust-malformed-evidence");
1531
1532 let malformed = with_envelope(package, "{")
1533 .verify()
1534 .expect_err("malformed authority evidence must reject");
1535 assert_projection_evidence_mismatch(&malformed);
1536
1537 let runtime: Value = serde_json::from_str(package.runtime_projection_json).unwrap();
1538 let resource = runtime["code_resources"]
1539 .as_array()
1540 .and_then(|resources| resources.first())
1541 .cloned()
1542 .expect("successor projection carries resource evidence");
1543
1544 let mut duplicate_runtime = runtime.clone();
1545 duplicate_runtime["code_resources"]
1546 .as_array_mut()
1547 .unwrap()
1548 .push(resource.clone());
1549 let duplicate = with_runtime_projection(package, canonical(&duplicate_runtime))
1550 .verify()
1551 .expect_err("duplicate resource evidence must reject");
1552 assert_projection_evidence_mismatch(&duplicate);
1553
1554 let mut extra_resource = resource;
1555 extra_resource["id"] = Value::String("typebridge.generator.rust.zzz-extra".into());
1556 let mut extra_runtime = runtime;
1557 extra_runtime["code_resources"]
1558 .as_array_mut()
1559 .unwrap()
1560 .push(extra_resource);
1561 let extra = with_runtime_projection(package, canonical(&extra_runtime))
1562 .verify()
1563 .expect_err("extra resource evidence must reject");
1564 assert_projection_evidence_mismatch(&extra);
1565 }
1566
1567 #[test]
1568 fn compiled_authority_rejects_missing_and_stale_outer_fingerprints() {
1569 let package = generated_package(
1570 "format: typebridge.schema/v2\nentities:\n person: {}\n",
1571 "rust-authority-test",
1572 );
1573 let original: Value = serde_json::from_str(package.schema_authority_json.unwrap()).unwrap();
1574
1575 let mut missing = original.clone();
1576 missing
1577 .as_object_mut()
1578 .unwrap()
1579 .remove("authority_fingerprint");
1580 let error = with_envelope(package, canonical(&missing))
1581 .verify()
1582 .expect_err("missing authority fingerprint must reject");
1583 assert_projection_evidence_mismatch(&error);
1584
1585 let mut stale = original;
1586 stale["authority_fingerprint"]["digest"] = "0".repeat(64).into();
1587 let error = with_envelope(package, canonical(&stale))
1588 .verify()
1589 .expect_err("stale authority fingerprint must reject");
1590 assert_projection_evidence_mismatch(&error);
1591 }
1592
1593 #[test]
1594 fn compiled_authority_reconstructs_managed_state_and_rejects_unsupported_claims() {
1595 let package = generated_package(
1596 "format: typebridge.schema/v2\nentities:\n person: {}\n",
1597 "rust-authority-test",
1598 );
1599 let original: Value = serde_json::from_str(package.schema_authority_json.unwrap()).unwrap();
1600
1601 let mut managed = original.clone();
1602 managed["content"]["managed_state"]["declared_identity"]["digest"] = "0".repeat(64).into();
1603 resign(&mut managed);
1604 let error = with_envelope(package, canonical(&managed))
1605 .verify()
1606 .expect_err("detached managed-state evidence must reject");
1607 assert_projection_evidence_mismatch(&error);
1608
1609 let mut capabilities = original.clone();
1610 capabilities["content"]["required_capabilities"] =
1611 Value::Array(vec![Value::String("unsupported.runtime".into())]);
1612 resign(&mut capabilities);
1613 let error = with_envelope(package, canonical(&capabilities))
1614 .verify()
1615 .expect_err("unsupported artifact capability must fail closed");
1616 assert_projection_evidence_mismatch(&error);
1617
1618 let mut version = original;
1619 version["content"]["authority_version"] =
1620 Value::String("typebridge.schema-authority/v2".into());
1621 resign(&mut version);
1622 let error = with_envelope(package, canonical(&version))
1623 .verify()
1624 .expect_err("unsupported artifact version must fail closed");
1625 assert_projection_evidence_mismatch(&error);
1626 }
1627
1628 #[test]
1629 fn compiled_authority_rejects_oversize_and_detached_evidence() {
1630 let package = generated_package(
1631 "format: typebridge.schema/v2\nentities:\n person: {}\n",
1632 "rust-authority-test",
1633 );
1634 let oversized = Box::leak(" ".repeat(MAX_SCHEMA_AUTHORITY_BYTES + 1).into_boxed_str());
1635 let error = with_envelope(package, oversized)
1636 .verify()
1637 .expect_err("oversize authority must fail before parsing");
1638 assert_projection_evidence_mismatch(&error);
1639
1640 let detached: SchemaPackage<TestSchema> = SchemaPackage::new_with_authority(
1641 package.semantic_fingerprint_json,
1642 package.projection_fingerprint_json,
1643 package.runtime_projection_json,
1644 package.schema_authority_json.unwrap(),
1645 package.declared_schema_json.unwrap(),
1646 "other-scope",
1647 package.semantic_profile_id.unwrap(),
1648 );
1649 let error = detached
1650 .verify()
1651 .expect_err("detached scope must not override compiled authority");
1652 assert_projection_evidence_mismatch(&error);
1653 }
1654
1655 #[test]
1656 fn schema_package_fingerprint_verification() {
1657 let documents = SchemaDocumentSet::parse([(
1658 DocumentId::new("test.yaml").unwrap(),
1659 "format: typebridge.schema/v2\nentities:\n person: {}\n",
1660 )])
1661 .unwrap();
1662 let declared = normalize_documents(&documents).unwrap();
1663 let profile = SemanticProfileId::new("typedb-3.12.1/v1").unwrap();
1664 let resolved = resolve(&declared, &profile).unwrap();
1665 let emitter = RustEmitter::new();
1666 let resources = emitter.code_resources().unwrap();
1667 let projection = project(
1668 &resolved,
1669 BindingTarget::Rust,
1670 &ProjectionConfig::rust(),
1671 &emitter.generator_handlers(),
1672 &resources,
1673 )
1674 .unwrap();
1675
1676 let semantic_json =
1677 String::from_utf8(to_canonical_json(projection.semantic_fingerprint()).unwrap())
1678 .unwrap();
1679 let projection_json =
1680 String::from_utf8(to_canonical_json(projection.projection_fingerprint()).unwrap())
1681 .unwrap();
1682 let runtime_json = String::from_utf8(to_canonical_json(&projection).unwrap()).unwrap();
1683
1684 let semantic_ref: &'static str = Box::leak(semantic_json.into_boxed_str());
1685 let projection_ref: &'static str = Box::leak(projection_json.into_boxed_str());
1686 let runtime_ref: &'static str = Box::leak(runtime_json.into_boxed_str());
1687
1688 let valid_package: SchemaPackage<TestSchema> =
1689 SchemaPackage::new(semantic_ref, projection_ref, runtime_ref);
1690 assert!(valid_package.verify().is_ok());
1691
1692 let tampered_package: SchemaPackage<TestSchema> =
1693 SchemaPackage::new(semantic_ref, r#""rust/v1-tampered""#, runtime_ref);
1694 match tampered_package.verify() {
1695 Err(err) => {
1696 use std::error::Error as _;
1697 assert_eq!(err.category(), crate::ErrorCategory::Schema);
1698 assert_eq!(err.code(), None);
1699 assert_eq!(err.path(), None);
1700 assert!(err.source().is_some());
1701 }
1702 Ok(_) => panic!("tampered schema package must fail verification"),
1703 }
1704 }
1705
1706 #[test]
1707 fn rejects_non_rust_target_projection() {
1708 let documents = SchemaDocumentSet::parse([(
1709 DocumentId::new("test.yaml").unwrap(),
1710 "format: typebridge.schema/v2\nentities:\n person: {}\n",
1711 )])
1712 .unwrap();
1713 let declared = normalize_documents(&documents).unwrap();
1714 let profile = SemanticProfileId::new("typedb-3.12.1/v1").unwrap();
1715 let resolved = resolve(&declared, &profile).unwrap();
1716 let py_emitter = PythonEmitter::new();
1717 let py_resources = py_emitter.code_resources().unwrap();
1718 let py_projection = project(
1719 &resolved,
1720 BindingTarget::Python,
1721 &ProjectionConfig::python(),
1722 &py_emitter.generator_handlers(),
1723 &py_resources,
1724 )
1725 .unwrap();
1726
1727 let semantic_json =
1728 String::from_utf8(to_canonical_json(py_projection.semantic_fingerprint()).unwrap())
1729 .unwrap();
1730 let projection_json =
1731 String::from_utf8(to_canonical_json(py_projection.projection_fingerprint()).unwrap())
1732 .unwrap();
1733 let runtime_json = String::from_utf8(to_canonical_json(&py_projection).unwrap()).unwrap();
1734
1735 let semantic_ref: &'static str = Box::leak(semantic_json.into_boxed_str());
1736 let projection_ref: &'static str = Box::leak(projection_json.into_boxed_str());
1737 let runtime_ref: &'static str = Box::leak(runtime_json.into_boxed_str());
1738
1739 let py_package: SchemaPackage<TestSchema> =
1740 SchemaPackage::new(semantic_ref, projection_ref, runtime_ref);
1741 let err = py_package.verify().unwrap_err();
1742 assert_eq!(err.category(), crate::ErrorCategory::Schema);
1743 assert_eq!(err.code(), None);
1744 assert_eq!(err.path(), None);
1745 assert!(err.to_string().contains("target mismatch") || err.to_string().contains("Rust"));
1746 }
1747}