1use std::{marker::PhantomData, net::SocketAddr, sync::Arc};
2
3use saddle_core::ErrorKind;
4use saddle_db::{
5 DatabaseConfig,
6 internal::{StartupManagedDatabaseFactory, StartupManagedDatabaseOwner},
7};
8use saddle_observability::ObserverConfig;
9use saddle_runtime::startup_assembly::{
10 ActualStartupOwners, BootstrapBatchSeal, BootstrapInstallAdapter, BootstrapOwnerView,
11 BootstrapTransaction, PreparedBootstrapBatch, PreparedBootstrapInstall, ReadyBootstrapInstall,
12 StartupBootstrapOwners, run_with_actual_startup_owners,
13};
14
15use crate::{
16 Result, SaddleError,
17 http1::{
18 bootstrap::{
19 GeneratedApplicationBootstrap, GeneratedBootstrapToken, GeneratedCompiledAdapter,
20 GeneratedContextFactory, ProductionBundleFacts, ProductionServiceBundle,
21 generated_bootstrap_type_identity,
22 },
23 production::{
24 BoundPreparedHttp1, GeneratedTransportBinding, PreparedHttp1, ProductionHttp1,
25 StagedHttp1, stage,
26 },
27 },
28};
29
30pub struct GeneratedApplicationSeal {
33 _private: (),
34}
35
36pub struct GeneratedApplicationParts<B> {
41 bootstrap: B,
42 runtime_preflight: saddle_runtime::capacity_leaf::VerifiedRuntimeLayoutPreflight,
43 termination: FacadeGeneratedTermination,
44 continuation: FacadeGeneratedContinuation,
45}
46
47pub type GeneratedBoundApplicationParts<A, C, const ROUTES: usize> =
48 GeneratedApplicationParts<GeneratedProductionBootstrap<A, C, ROUTES>>;
49
50#[derive(Clone, Copy, Debug, Eq, PartialEq)]
51pub enum GeneratedApplicationBindingError {
52 ForeignGeneration,
53 ForeignBootstrapType,
54 RouteCountMismatch,
55 MissingIdentity,
56 PreflightFailed,
57}
58
59pub struct FacadeBootstrapCoordinator {
62 config: SaddleConfig,
63 adapter_provenance: [u8; 32],
64}
65
66mod generated_bootstrap_sealed {
67 pub trait Sealed {}
68}
69
70trait GeneratedProductionBootstrapCapability:
73 generated_bootstrap_sealed::Sealed + Send + 'static
74{
75 fn run<R, H>(
76 self,
77 coordinator: FacadeBootstrapCoordinator,
78 actual: ActualStartupOwners<StartupManagedDatabaseFactory>,
79 continuation: saddle_admission::LateStartupContinuationOwner<FacadeGeneratedContinuation>,
80 authorization: PendingListenerAuthorization<R, H>,
81 candidate: crate::startup_candidate::PreparedStartupCandidateTransaction,
82 ) -> Result<()>
83 where
84 R: ApprovedBundleNonceReservation,
85 H: ListenerAuthorityProvider;
86}
87
88pub struct GeneratedProductionBootstrap<A, C, const ROUTES: usize>
104where
105 A: GeneratedCompiledAdapter,
106 C: GeneratedContextFactory<A::Context>,
107{
108 generation: u64,
109 type_identity: [u8; 32],
110 staged: StagedHttp1<A, C>,
111}
112
113impl<A, C, const ROUTES: usize> generated_bootstrap_sealed::Sealed
114 for GeneratedProductionBootstrap<A, C, ROUTES>
115where
116 A: GeneratedCompiledAdapter,
117 C: GeneratedContextFactory<A::Context>,
118{
119}
120
121impl<A, C, const ROUTES: usize> GeneratedProductionBootstrapCapability
122 for GeneratedProductionBootstrap<A, C, ROUTES>
123where
124 A: ProductionServiceBundle,
125 C: GeneratedContextFactory<A::Context>,
126{
127 fn run<R, H>(
128 self,
129 coordinator: FacadeBootstrapCoordinator,
130 actual: ActualStartupOwners<StartupManagedDatabaseFactory>,
131 continuation: saddle_admission::LateStartupContinuationOwner<FacadeGeneratedContinuation>,
132 authorization: PendingListenerAuthorization<R, H>,
133 candidate: crate::startup_candidate::PreparedStartupCandidateTransaction,
134 ) -> Result<()>
135 where
136 R: ApprovedBundleNonceReservation,
137 H: ListenerAuthorityProvider,
138 {
139 let _binding = (self.generation, self.type_identity);
140 run_with_actual_startup_owners(
141 actual,
142 FacadeContinuationAdapter {
143 provenance: coordinator.adapter_provenance,
144 },
145 continuation,
146 move |owners, runtime_profile| async move {
147 let transaction = BootstrapTransaction::new(owners);
148 let prepared = transaction
149 .prepare(ProductionInstall {
150 config: coordinator.config,
151 staged: self.staged,
152 runtime_profile,
153 candidate,
154 })
155 .map_err(|failure| {
156 failure.into_runner_failure(startup_error(
157 "saddle.production_bootstrap_prepare_failed",
158 ))
159 })?;
160 let prepared = prepared.prepare_observability().await?;
161 let mut prepared = prepared;
162 if prepared.prepare_install().await.is_err() {
163 return Err(
164 prepared.fail(startup_error("saddle.production_listener_prepare_failed"))
165 );
166 }
167 let prepared = prepared.verify_post_driver_binding().map_err(|failure| {
168 failure.into_runner_failure(startup_error(
169 "saddle.production_post_driver_binding_failed",
170 ))
171 })?;
172 let prepared = authorize_listener(
173 prepared,
174 authorization.binding,
175 authorization.reservation,
176 authorization.authority,
177 )
178 .map_err(|error| error.value.fail(error.error))?;
179 let prepared = prepared.finish_prepare_install();
180 Ok(prepared.commit())
181 },
182 )
183 }
184}
185
186struct FacadeContinuationAdapter {
187 provenance: [u8; 32],
188}
189
190impl saddle_admission::StartupContinuationAdapter for FacadeContinuationAdapter {
191 type ContinuationOwner =
192 saddle_admission::LateStartupContinuationOwner<FacadeGeneratedContinuation>;
193
194 fn adapter_provenance(&self) -> [u8; 32] {
195 self.provenance
196 }
197}
198
199struct ProductionInstall<A, C>
200where
201 A: GeneratedCompiledAdapter,
202 C: GeneratedContextFactory<A::Context>,
203{
204 config: SaddleConfig,
205 staged: StagedHttp1<A, C>,
206 runtime_profile: saddle_runtime::startup_assembly::VerifiedTransportRuntimeProfile,
207 candidate: crate::startup_candidate::PreparedStartupCandidateTransaction,
208}
209
210struct PreparedProductionInstall<A, C>
211where
212 A: GeneratedCompiledAdapter,
213 C: GeneratedContextFactory<A::Context>,
214{
215 config: SaddleConfig,
216 prepared: PreparedHttp1<A, C>,
217 listener: Option<tokio::net::TcpListener>,
218 candidate: crate::startup_candidate::PreparedStartupCandidateTransaction,
219}
220
221struct ReadyProductionInstall<A, C>
222where
223 A: GeneratedCompiledAdapter,
224 C: GeneratedContextFactory<A::Context>,
225{
226 config: SaddleConfig,
227 prepared: BoundPreparedHttp1<A, C>,
228 candidate: crate::startup_candidate::PreparedStartupCandidateTransaction,
229}
230
231impl<A, C, B> BootstrapInstallAdapter<StartupManagedDatabaseOwner, B> for ProductionInstall<A, C>
232where
233 A: GeneratedCompiledAdapter,
234 C: GeneratedContextFactory<A::Context>,
235 B: Send + 'static,
236{
237 type Prepared = PreparedProductionInstall<A, C>;
238 type Error = SaddleError;
239
240 fn prepare(
241 self,
242 _owners: BootstrapOwnerView<'_, StartupManagedDatabaseOwner, B>,
243 ) -> std::result::Result<Self::Prepared, Self::Error> {
244 Ok(PreparedProductionInstall {
245 config: self.config,
246 prepared: self.staged.prepare(self.runtime_profile)?,
247 listener: None,
248 candidate: self.candidate,
249 })
250 }
251}
252
253impl<A, C, B> PreparedBootstrapInstall<StartupManagedDatabaseOwner, B>
254 for PreparedProductionInstall<A, C>
255where
256 A: GeneratedCompiledAdapter,
257 C: GeneratedContextFactory<A::Context>,
258 B: Send + 'static,
259{
260 type Ready = ReadyProductionInstall<A, C>;
261 type Error = SaddleError;
262
263 fn component_names(&self) -> &'static [&'static str] {
264 &["observability", "database", "http1-production"]
265 }
266
267 async fn prepare_install(&mut self) -> std::result::Result<(), Self::Error> {
268 self.listener = Some(
269 tokio::net::TcpListener::bind(self.config.listen)
270 .await
271 .map_err(|_| startup_error("saddle.production_listener_bind_failed"))?,
272 );
273 Ok(())
274 }
275
276 fn into_ready(self) -> Self::Ready {
277 ReadyProductionInstall {
278 config: self.config,
279 prepared: self
280 .prepared
281 .bind_owned(self.listener.unwrap_or_else(|| std::process::abort())),
282 candidate: self.candidate,
283 }
284 }
285}
286
287impl<A, C, B> ReadyBootstrapInstall<StartupManagedDatabaseOwner, B> for ReadyProductionInstall<A, C>
288where
289 A: GeneratedCompiledAdapter,
290 C: GeneratedContextFactory<A::Context>,
291 B: Send + 'static,
292{
293 fn install(
294 self,
295 owners: StartupBootstrapOwners<StartupManagedDatabaseOwner, B>,
296 mut seal: BootstrapBatchSeal,
297 ) -> PreparedBootstrapBatch {
298 let (
299 database_owner,
300 db_domain,
301 tokio_domain,
302 allocation,
303 ledger,
304 _termination,
305 bound,
306 _transport_binding,
307 observability,
308 ) = owners.into_parts();
309 let (database_owner, database) = database_owner.bootstrap(|database| database.existing());
310 let publish = bound.into_publish_token();
311 let (listener, prepared) = self.prepared.into_parts();
312 let assembly = prepared.attach(database, ledger, tokio_domain, allocation, db_domain);
313 let server = ProductionHttp1::from_staged(
314 listener,
315 assembly,
316 publish,
317 seal.take_driver_finalizer(),
318 self.candidate.commit(),
319 );
320 let observability: Arc<dyn saddle_core::ComponentLifecycle> = match observability {
321 Some(saddle_runtime::startup_assembly::StartupObservabilityOwner::Started(owner)) => {
322 owner
323 }
324 Some(saddle_runtime::startup_assembly::StartupObservabilityOwner::Prepared(_)) => {
325 std::process::abort()
326 }
327 None => std::process::abort(),
328 };
329 let components: Vec<Arc<dyn saddle_core::ComponentLifecycle>> =
330 vec![observability, Arc::new(database_owner), Arc::new(server)];
331 let _application = self.config.application;
332 seal.install(components)
333 }
334}
335
336struct FacadeGeneratedContinuation {
337 common: [[u8; 32]; 3],
338 generation: u64,
339}
340
341impl saddle_admission::PreflightGeneratedStaticContinuationOwner for FacadeGeneratedContinuation {
342 fn build_identity(&self) -> [u8; 32] {
343 self.common[0]
344 }
345 fn artifact_identity(&self) -> [u8; 32] {
346 self.common[1]
347 }
348 fn route_set_identity(&self) -> [u8; 32] {
349 self.common[2]
350 }
351 fn owner_generation(&self) -> u64 {
352 self.generation
353 }
354}
355
356struct FacadeGeneratedTermination {
357 common: [[u8; 32]; 3],
358 generation: u64,
359 topology: [u64; 4],
360}
361
362fn facade_generated_identity(common: [[u8; 32]; 3], tag: u8) -> [u8; 32] {
363 let mut identity = common[0];
364 for (index, byte) in identity.iter_mut().enumerate() {
365 *byte ^= common[1][index].rotate_left(1) ^ common[2][index].rotate_left(2) ^ tag;
366 }
367 identity
368}
369
370impl saddle_admission::GeneratedTerminationTopologyWorkOwner for FacadeGeneratedTermination {
371 fn leaf_identity(&self) -> [u8; 32] {
372 facade_generated_identity(self.common, 0x71)
373 }
374 fn common_identities(&self) -> [[u8; 32]; 3] {
375 self.common
376 }
377 fn owner_generation(&self) -> u64 {
378 self.generation
379 }
380 fn termination_topology(&self) -> [u64; 4] {
381 self.topology
382 }
383 fn termination_topology_identity(&self) -> [u8; 32] {
384 facade_generated_identity(self.common, 0x72)
385 }
386 fn db_return_work_identity(&self) -> [u8; 32] {
387 saddle_db::internal::db_normal_return_work_proof().identity()
388 }
389 fn writer_work_identity(&self) -> [u8; 32] {
390 saddle_observability::file::writer_component_work_proof().identity()
391 }
392 fn runtime_work_identity(&self) -> [u8; 32] {
393 saddle_runtime::termination_service::runtime_component_work_proof().identity()
394 }
395}
396
397struct FacadeRuntimeLayouts<F, C, const OUTPUT: usize> {
398 common: [[u8; 32]; 3],
399 generation: u64,
400 routes: Box<[u64]>,
401 types: PhantomData<fn() -> (F, C)>,
402}
403
404impl<F, C, const OUTPUT: usize> saddle_runtime::capacity_leaf::RuntimeMonomorphizedLayoutSource
405 for FacadeRuntimeLayouts<F, C, OUTPUT>
406where
407 F: Send + 'static,
408 C: Send + 'static,
409{
410 fn common_identities(&self) -> [[u8; 32]; 3] {
411 self.common
412 }
413 fn owner_generation(&self) -> u64 {
414 self.generation
415 }
416 fn solver_schema_identity(&self) -> [u8; 32] {
417 saddle_admission::admission_capacity_contract_identities()[0]
418 }
419 fn admission_layout_identity(&self) -> [u8; 32] {
420 saddle_admission::admission_capacity_contract_identities()[2]
421 }
422 fn measure(
423 self,
424 seal: saddle_runtime::capacity_leaf::RuntimeLayoutSeal,
425 ) -> std::result::Result<
426 Box<[saddle_runtime::capacity_leaf::RuntimeRouteLayout]>,
427 saddle_runtime::capacity_leaf::RuntimeCapacityLeafError,
428 > {
429 self.routes
430 .into_iter()
431 .map(|route| {
432 seal.measure::<F, C, saddle_admission::ManagedResponse, [u8; OUTPUT]>(route)
433 })
434 .collect()
435 }
436}
437
438impl GeneratedApplicationSeal {
439 fn issue() -> Self {
440 Self { _private: () }
441 }
442
443 pub fn preflight_token(&self) -> GeneratedBootstrapToken {
446 GeneratedBootstrapToken::issue()
447 }
448
449 pub fn bind<E, BC, F, C, const BODY: usize, const OUTPUT: usize, const ROUTES: usize>(
452 self,
453 generated: GeneratedApplicationBootstrap<
454 saddle_service::internal::CompiledExecutionWithCapacityLeaf<E, BC, F, BODY, OUTPUT>,
455 C,
456 ROUTES,
457 >,
458 ) -> std::result::Result<
459 GeneratedBoundApplicationParts<
460 saddle_service::internal::CompiledExecutionWithCapacityLeaf<E, BC, F, BODY, OUTPUT>,
461 C,
462 ROUTES,
463 >,
464 GeneratedApplicationBindingError,
465 >
466 where
467 E: Fn(
468 usize,
469 BC,
470 saddle_admission::ManagedBytes,
471 saddle_service::internal::CompiledDbPermit,
472 saddle_admission::ManagedResponseBuilder,
473 ) -> F
474 + Send
475 + Sync
476 + 'static,
477 BC: Send + Unpin + 'static,
478 F: std::future::Future<
479 Output = std::result::Result<
480 saddle_admission::ManagedResponse,
481 saddle_service::internal::ExecutionError,
482 >,
483 > + Send
484 + 'static,
485 C: GeneratedContextFactory<BC>,
486 {
487 let ProductionBundleFacts {
488 common,
489 generation,
490 routes,
491 termination_topology: topology,
492 } = generated.production_bundle_facts();
493 let type_identity = generated_bootstrap_type_identity::<
494 saddle_service::internal::CompiledExecutionWithCapacityLeaf<E, BC, F, BODY, OUTPUT>,
495 C,
496 ROUTES,
497 >();
498 if generation == 0
499 || ROUTES == 0
500 || routes.len() != ROUTES
501 || [common[0], common[1], common[2], type_identity].contains(&[0; 32])
502 {
503 return Err(GeneratedApplicationBindingError::MissingIdentity);
504 }
505 let runtime_preflight = saddle_runtime::capacity_leaf::verify_runtime_layout_preflight(
506 saddle_runtime::capacity_leaf::runtime_capacity_component_proof(),
507 FacadeRuntimeLayouts::<F, BC, OUTPUT> {
508 common,
509 generation,
510 routes,
511 types: PhantomData,
512 },
513 )
514 .map_err(|_| GeneratedApplicationBindingError::PreflightFailed)?;
515 let staged = stage(
516 generated,
517 GeneratedTransportBinding {
518 generation,
519 build_identity: common[0],
520 route_set_identity: common[2],
521 static_layout_identity: type_identity,
522 },
523 )
524 .map_err(|_| GeneratedApplicationBindingError::PreflightFailed)?;
525 Ok(GeneratedApplicationParts {
526 bootstrap: GeneratedProductionBootstrap {
527 generation,
528 type_identity,
529 staged,
530 },
531 runtime_preflight,
532 termination: FacadeGeneratedTermination {
533 common,
534 generation,
535 topology,
536 },
537 continuation: FacadeGeneratedContinuation { common, generation },
538 })
539 }
540}
541
542mod generated_consumer_sealed {
543 pub trait Sealed {}
544}
545
546#[doc(hidden)]
550pub trait GeneratedApplicationConsumer: generated_consumer_sealed::Sealed {
551 fn consume<E, BC, F, C, const BODY: usize, const OUTPUT: usize, const ROUTES: usize>(
552 self,
553 parts: GeneratedBoundApplicationParts<
554 saddle_service::internal::CompiledExecutionWithCapacityLeaf<E, BC, F, BODY, OUTPUT>,
555 C,
556 ROUTES,
557 >,
558 ) -> Result<()>
559 where
560 E: Fn(
561 usize,
562 BC,
563 saddle_admission::ManagedBytes,
564 saddle_service::internal::CompiledDbPermit,
565 saddle_admission::ManagedResponseBuilder,
566 ) -> F
567 + Send
568 + Sync
569 + 'static,
570 BC: Send + Unpin + 'static,
571 F: std::future::Future<
572 Output = std::result::Result<
573 saddle_admission::ManagedResponse,
574 saddle_service::internal::ExecutionError,
575 >,
576 > + Send
577 + 'static,
578 C: GeneratedContextFactory<BC>;
579}
580
581pub trait GeneratedApplicationOwner: Send + 'static {
599 fn consume<K>(self, seal: GeneratedApplicationSeal, consumer: K) -> Result<()>
600 where
601 K: GeneratedApplicationConsumer;
602}
603
604pub struct FacadeDeploymentSeal {
606 _private: (),
607}
608
609pub struct FacadeDeploymentParts<C, E, D, F, S, U> {
610 calibration: C,
611 envelope: E,
612 db_service: D,
613 filesystem_service: F,
614 scheduler_service: S,
615 supervisor_service: U,
616 policy: saddle_runtime::resource_envelope::FiniteStartupPolicy,
617 adapter_provenance: [u8; 32],
618}
619
620pub type FacadeDeploymentOwnerParts<O> = FacadeDeploymentParts<
621 <O as FacadeDeploymentOwner>::Calibration,
622 <O as FacadeDeploymentOwner>::Envelope,
623 <O as FacadeDeploymentOwner>::DbService,
624 <O as FacadeDeploymentOwner>::FilesystemService,
625 <O as FacadeDeploymentOwner>::SchedulerService,
626 <O as FacadeDeploymentOwner>::SupervisorService,
627>;
628
629impl FacadeDeploymentSeal {
630 fn issue() -> Self {
631 Self { _private: () }
632 }
633
634 #[allow(clippy::too_many_arguments)]
635 pub fn bind<C, E, D, F, S, U>(
636 self,
637 calibration: C,
638 envelope: E,
639 db_service: D,
640 filesystem_service: F,
641 scheduler_service: S,
642 supervisor_service: U,
643 policy: saddle_runtime::resource_envelope::FiniteStartupPolicy,
644 adapter_provenance: [u8; 32],
645 ) -> FacadeDeploymentParts<C, E, D, F, S, U>
646 where
647 C: saddle_admission::RuntimeCapacityCalibrationSourceLeaf,
648 E: Into<saddle_runtime::resource_envelope::VerifiedResourceEnvelope>,
649 D: saddle_admission::VerifiedDbTerminationServiceProofOwner,
650 F: Send + 'static,
651 S: saddle_admission::VerifiedSchedulerTerminationServiceProofOwner,
652 U: saddle_admission::VerifiedSupervisorTerminationServiceProofOwner,
653 {
654 FacadeDeploymentParts {
655 calibration,
656 envelope,
657 db_service,
658 filesystem_service,
659 scheduler_service,
660 supervisor_service,
661 policy,
662 adapter_provenance,
663 }
664 }
665}
666
667pub trait FacadeDeploymentOwner: Send + 'static {
676 type Calibration: saddle_admission::RuntimeCapacityCalibrationSourceLeaf;
677 type Envelope: Into<saddle_runtime::resource_envelope::VerifiedResourceEnvelope>;
678 type DbService: saddle_admission::VerifiedDbTerminationServiceProofOwner;
679 type FilesystemService: Send + 'static;
680 type SchedulerService: saddle_admission::VerifiedSchedulerTerminationServiceProofOwner;
681 type SupervisorService: saddle_admission::VerifiedSupervisorTerminationServiceProofOwner;
682
683 fn split(self, seal: FacadeDeploymentSeal) -> FacadeDeploymentOwnerParts<Self>;
684}
685
686pub trait ApprovedExternalBundle: Sized {
694 type Reservation: ApprovedBundleNonceReservation;
695 type Authority: ListenerAuthorityProvider;
696
697 fn verify(
698 self,
699 seal: ApprovedExternalBundleSeal,
700 ) -> Result<ApprovedExternalBundleVerification<Self::Reservation, Self::Authority>>;
701}
702
703pub trait ApprovedBundleNonceReservation: Send + 'static {
707 fn commit(self) -> Result<()>;
708}
709
710pub trait ListenerAuthorityProvider: Sized {
713 fn verify(self, seal: ListenerAuthoritySeal) -> Result<VerifiedListenerAuthority>;
714}
715
716#[doc(hidden)]
719pub struct ApprovedExternalBundleSeal {
720 _private: (),
721}
722
723pub struct ListenerAuthoritySeal {
725 _private: (),
726}
727
728#[derive(Clone, Copy, Debug, Eq, PartialEq)]
729struct ExternalBundleBinding {
730 bundle: [u8; 32],
731 nonce: [u8; 32],
732 binary: [u8; 32],
733 deployment: [u8; 32],
734 calibration: [u8; 32],
735}
736
737pub struct ApprovedExternalBundleVerification<R, L> {
739 deployment: VerifiedLauncherDeployment,
740 binding: ExternalBundleBinding,
741 reservation: R,
742 authority: L,
743}
744
745pub struct ApprovedExternalBundleApplicabilityFailure {
748 signed_filesystem: saddle_observability::file::VerifiedSignedProviderFilesystemBundle,
749}
750
751impl ApprovedExternalBundleApplicabilityFailure {
752 pub fn into_signed_filesystem(
754 self,
755 ) -> saddle_observability::file::VerifiedSignedProviderFilesystemBundle {
756 self.signed_filesystem
757 }
758}
759
760pub struct VerifiedListenerAuthority {
762 binding: ExternalBundleBinding,
763}
764
765struct VerifiedFacadeDeployment {
766 calibration: saddle_runtime::capacity_leaf::VerifiedRuntimeBuildCalibrationOwner,
767 envelope: saddle_runtime::resource_envelope::VerifiedResourceEnvelope,
768 db_service: saddle_db::internal::DbReturnServiceProof,
769 filesystem_service: saddle_observability::file::PairedSignedProviderFilesystemEvidence,
770 scheduler_service: saddle_runtime::termination_service::VerifiedSchedulerDeploymentOwner,
771 supervisor_service: saddle_runtime::termination_service::VerifiedSupervisorServiceOwner,
772 policy: saddle_runtime::resource_envelope::FiniteStartupPolicy,
773 adapter_provenance: [u8; 32],
774}
775
776enum VerifiedLauncherDeployment {
777 Production(VerifiedFacadeDeployment),
778 GoldenC8(VerifiedGoldenC8Compose),
779}
780
781#[allow(dead_code)]
782struct VerifiedGoldenC8Compose {
783 applicability: saddle_core::VerifiedGoldenC8ApplicabilityOwner,
784 calibration: saddle_runtime::capacity_leaf::VerifiedRuntimeBuildCalibrationOwner,
785 envelope: saddle_runtime::resource_envelope::VerifiedResourceEnvelope,
786 db_service: saddle_db::internal::DbReturnServiceProof,
787 filesystem_service: saddle_observability::file::WriterTerminationProof,
788 scheduler_service: saddle_runtime::termination_service::VerifiedSchedulerDeploymentOwner,
789 supervisor_service: saddle_runtime::termination_service::VerifiedSupervisorServiceOwner,
790 policy: saddle_runtime::resource_envelope::FiniteStartupPolicy,
791 adapter_provenance: [u8; 32],
792}
793
794#[derive(Clone, Copy)]
795struct VerifiedDeploymentIdentitySet {
796 runtime_common: [[u8; 32]; 3],
797 calibration_common: [[u8; 32]; 3],
798 runtime_generation: u64,
799 calibration_generation: u64,
800 expected_provenance: [[u8; 32]; 9],
801 calibration_provenance: [[u8; 32]; 9],
802 envelope_build: [u8; 32],
803 envelope_gate: [u8; 32],
804 envelope_supervisor: [u8; 32],
805 environment: [u8; 32],
806 db_build: [u8; 32],
807 supervisor_build: [u8; 32],
808 db_gate: [u8; 32],
809 supervisor_gate: [u8; 32],
810 supervisor_identity: [u8; 32],
811 supervisor_environment: [u8; 32],
812 required_identities: [[u8; 32]; 13],
813}
814
815fn validate_verified_deployment_identities(facts: VerifiedDeploymentIdentitySet) -> Result<()> {
816 if facts.required_identities.contains(&[0; 32])
817 || facts.runtime_common.contains(&[0; 32])
818 || facts.runtime_common != facts.calibration_common
819 || facts.runtime_generation == 0
820 || facts.runtime_generation != facts.calibration_generation
821 || facts.expected_provenance != facts.calibration_provenance
822 || facts.envelope_build != facts.db_build
823 || facts.envelope_build != facts.supervisor_build
824 || facts.envelope_gate != facts.db_gate
825 || facts.envelope_gate != facts.supervisor_gate
826 || facts.envelope_supervisor != facts.supervisor_identity
827 || facts.environment != facts.supervisor_environment
828 {
829 return Err(startup_error("saddle.approved_bundle_identity_mismatch"));
830 }
831 Ok(())
832}
833
834fn preflight_verified_deployment_without_filesystem(
835 calibration: &saddle_runtime::capacity_leaf::VerifiedRuntimeBuildCalibrationOwner,
836 envelope: &saddle_runtime::resource_envelope::VerifiedResourceEnvelope,
837 db_service: &saddle_db::internal::DbReturnServiceProof,
838 scheduler_service: saddle_runtime::termination_service::VerifiedSchedulerServiceOwner,
839 supervisor_service: &saddle_runtime::termination_service::VerifiedSupervisorServiceOwner,
840 adapter_provenance: [u8; 32],
841 binding: ExternalBundleBinding,
842) -> Result<saddle_runtime::termination_service::VerifiedSchedulerDeploymentOwner> {
843 let calibration_common =
844 saddle_admission::RuntimeCapacityCalibrationSourceLeaf::common_identities(calibration);
845 let calibration_generation =
846 saddle_admission::RuntimeCapacityCalibrationSourceLeaf::owner_generation(calibration);
847 let calibration_provenance =
848 saddle_admission::RuntimeCapacityCalibrationSourceLeaf::calibration_provenance(calibration);
849 let envelope_build = saddle_admission::VerifiedStartupEnvelopeOwner::build_identity(envelope);
850 let envelope_gate = saddle_admission::VerifiedStartupEnvelopeOwner::gate_identity(envelope);
851 let envelope_resource =
852 saddle_admission::VerifiedStartupEnvelopeOwner::resource_attestation(envelope);
853 let envelope_supervisor =
854 saddle_admission::VerifiedStartupEnvelopeOwner::supervisor_attestation(envelope);
855 let environment = db_service.environment_identity();
856 validate_verified_deployment_identities(VerifiedDeploymentIdentitySet {
857 runtime_common: calibration_common,
858 calibration_common,
859 runtime_generation: calibration_generation,
860 calibration_generation,
861 expected_provenance: calibration_provenance,
862 calibration_provenance,
863 envelope_build,
864 envelope_gate,
865 envelope_supervisor,
866 environment,
867 db_build: db_service.build_identity(),
868 supervisor_build: supervisor_service.build_identity(),
869 db_gate: db_service.gate_identity(),
870 supervisor_gate: supervisor_service.gate_identity(),
871 supervisor_identity: supervisor_service.supervisor_identity(),
872 supervisor_environment: supervisor_service.environment_identity(),
873 required_identities: [
874 adapter_provenance,
875 binding.bundle,
876 binding.nonce,
877 binding.binary,
878 binding.deployment,
879 binding.calibration,
880 envelope_build,
881 envelope_gate,
882 envelope_resource,
883 envelope_supervisor,
884 environment,
885 db_service.service_attestation(),
886 supervisor_service.service_attestation(),
887 ],
888 })?;
889 saddle_runtime::termination_service::bind_scheduler_deployment_owner(
890 scheduler_service,
891 envelope,
892 )
893 .map_err(|_| startup_error("saddle.approved_bundle_identity_mismatch"))
894}
895
896impl ApprovedExternalBundleSeal {
897 #[doc(hidden)]
901 #[allow(clippy::too_many_arguments)]
902 pub fn bind_verified_for_golden_c8<R, L>(
903 self,
904 applicability: saddle_core::VerifiedGoldenC8ApplicabilityOwner,
905 calibration: saddle_runtime::capacity_leaf::VerifiedRuntimeBuildCalibrationOwner,
906 envelope: saddle_runtime::resource_envelope::VerifiedResourceEnvelope,
907 db_service: saddle_db::internal::DbReturnServiceProof,
908 filesystem_service: saddle_observability::file::WriterTerminationProof,
909 scheduler_service: saddle_runtime::termination_service::VerifiedSchedulerServiceOwner,
910 supervisor_service: saddle_runtime::termination_service::VerifiedSupervisorServiceOwner,
911 policy: saddle_runtime::resource_envelope::FiniteStartupPolicy,
912 adapter_provenance: [u8; 32],
913 bundle_identity: [u8; 32],
914 nonce_identity: [u8; 32],
915 binary_identity: [u8; 32],
916 deployment_identity: [u8; 32],
917 calibration_identity: [u8; 32],
918 reservation: R,
919 authority: L,
920 ) -> Result<ApprovedExternalBundleVerification<R, L>>
921 where
922 R: ApprovedBundleNonceReservation,
923 L: ListenerAuthorityProvider,
924 {
925 let binding = ExternalBundleBinding {
926 bundle: bundle_identity,
927 nonce: nonce_identity,
928 binary: binary_identity,
929 deployment: deployment_identity,
930 calibration: calibration_identity,
931 };
932 let scheduler_service = preflight_verified_deployment_without_filesystem(
933 &calibration,
934 &envelope,
935 &db_service,
936 scheduler_service,
937 &supervisor_service,
938 adapter_provenance,
939 binding,
940 )?;
941 if filesystem_service.environment_identity()
942 != saddle_db::internal::DbReturnServiceProof::environment_identity(&db_service)
943 || filesystem_service.build_identity()
944 != saddle_admission::VerifiedStartupEnvelopeOwner::build_identity(&envelope)
945 {
946 return Err(startup_error("saddle.golden_c8_applicability_foreign"));
947 }
948 Ok(ApprovedExternalBundleVerification {
949 deployment: VerifiedLauncherDeployment::GoldenC8(VerifiedGoldenC8Compose {
950 applicability,
951 calibration,
952 envelope,
953 db_service,
954 filesystem_service,
955 scheduler_service,
956 supervisor_service,
957 policy,
958 adapter_provenance,
959 }),
960 binding,
961 reservation,
962 authority,
963 })
964 }
965
966 #[allow(clippy::too_many_arguments)]
969 pub fn bind_verified_with_applicability<R, L>(
970 self,
971 signed_filesystem: saddle_observability::file::VerifiedSignedProviderFilesystemBundle,
972 application_identity: [u8; 32],
973 source_identity: [u8; 32],
974 artifact_identity: [u8; 32],
975 routes_identity: [u8; 32],
976 calibration: saddle_runtime::capacity_leaf::VerifiedRuntimeBuildCalibrationOwner,
977 envelope: saddle_runtime::resource_envelope::VerifiedResourceEnvelope,
978 db_service: saddle_db::internal::DbReturnServiceProof,
979 scheduler_service: saddle_runtime::termination_service::VerifiedSchedulerServiceOwner,
980 supervisor_service: saddle_runtime::termination_service::VerifiedSupervisorServiceOwner,
981 policy: saddle_runtime::resource_envelope::FiniteStartupPolicy,
982 adapter_provenance: [u8; 32],
983 bundle_identity: [u8; 32],
984 nonce_identity: [u8; 32],
985 binary_identity: [u8; 32],
986 deployment_identity: [u8; 32],
987 calibration_identity: [u8; 32],
988 reservation: R,
989 authority: L,
990 ) -> std::result::Result<
991 ApprovedExternalBundleVerification<R, L>,
992 ApprovedExternalBundleApplicabilityFailure,
993 >
994 where
995 R: ApprovedBundleNonceReservation,
996 L: ListenerAuthorityProvider,
997 {
998 let binding = ExternalBundleBinding {
999 bundle: bundle_identity,
1000 nonce: nonce_identity,
1001 binary: binary_identity,
1002 deployment: deployment_identity,
1003 calibration: calibration_identity,
1004 };
1005 let scheduler_service = match preflight_verified_deployment_without_filesystem(
1006 &calibration,
1007 &envelope,
1008 &db_service,
1009 scheduler_service,
1010 &supervisor_service,
1011 adapter_provenance,
1012 binding,
1013 ) {
1014 Ok(owner) => owner,
1015 Err(_) => {
1016 return Err(ApprovedExternalBundleApplicabilityFailure { signed_filesystem });
1017 }
1018 };
1019 let filesystem_service = signed_filesystem
1020 .verify_deployment_pair(
1021 bundle_identity,
1022 adapter_provenance,
1023 application_identity,
1024 binary_identity,
1025 saddle_admission::VerifiedStartupEnvelopeOwner::build_identity(&envelope),
1026 source_identity,
1027 artifact_identity,
1028 routes_identity,
1029 deployment_identity,
1030 saddle_db::internal::DbReturnServiceProof::environment_identity(&db_service),
1031 nonce_identity,
1032 calibration_identity,
1033 )
1034 .map_err(
1035 |signed_filesystem| ApprovedExternalBundleApplicabilityFailure {
1036 signed_filesystem,
1037 },
1038 )?;
1039 Ok(self
1040 .bind_verified_inner(
1041 calibration,
1042 envelope,
1043 db_service,
1044 filesystem_service,
1045 scheduler_service,
1046 supervisor_service,
1047 policy,
1048 adapter_provenance,
1049 bundle_identity,
1050 nonce_identity,
1051 binary_identity,
1052 deployment_identity,
1053 calibration_identity,
1054 reservation,
1055 authority,
1056 )
1057 .unwrap_or_else(|_| {
1058 unreachable!("signed filesystem issuer and component preflight are closed")
1059 }))
1060 }
1061
1062 #[allow(clippy::too_many_arguments)]
1066 #[cfg(test)]
1067 pub fn bind_verified<R, L>(
1068 self,
1069 calibration: saddle_runtime::capacity_leaf::VerifiedRuntimeBuildCalibrationOwner,
1070 envelope: saddle_runtime::resource_envelope::VerifiedResourceEnvelope,
1071 db_service: saddle_db::internal::DbReturnServiceProof,
1072 filesystem_service: saddle_observability::file::PairedSignedProviderFilesystemEvidence,
1073 scheduler_service: saddle_runtime::termination_service::VerifiedSchedulerServiceOwner,
1074 supervisor_service: saddle_runtime::termination_service::VerifiedSupervisorServiceOwner,
1075 policy: saddle_runtime::resource_envelope::FiniteStartupPolicy,
1076 adapter_provenance: [u8; 32],
1077 bundle_identity: [u8; 32],
1078 nonce_identity: [u8; 32],
1079 binary_identity: [u8; 32],
1080 deployment_identity: [u8; 32],
1081 calibration_identity: [u8; 32],
1082 reservation: R,
1083 authority: L,
1084 ) -> Result<ApprovedExternalBundleVerification<R, L>>
1085 where
1086 R: ApprovedBundleNonceReservation,
1087 L: ListenerAuthorityProvider,
1088 {
1089 let scheduler_service =
1090 saddle_runtime::termination_service::bind_scheduler_deployment_owner(
1091 scheduler_service,
1092 &envelope,
1093 )
1094 .map_err(|_| startup_error("saddle.approved_bundle_identity_mismatch"))?;
1095 self.bind_verified_inner(
1096 calibration,
1097 envelope,
1098 db_service,
1099 filesystem_service,
1100 scheduler_service,
1101 supervisor_service,
1102 policy,
1103 adapter_provenance,
1104 bundle_identity,
1105 nonce_identity,
1106 binary_identity,
1107 deployment_identity,
1108 calibration_identity,
1109 reservation,
1110 authority,
1111 )
1112 }
1113
1114 #[allow(clippy::too_many_arguments)]
1115 fn bind_verified_inner<R, L>(
1116 self,
1117 calibration: saddle_runtime::capacity_leaf::VerifiedRuntimeBuildCalibrationOwner,
1118 envelope: saddle_runtime::resource_envelope::VerifiedResourceEnvelope,
1119 db_service: saddle_db::internal::DbReturnServiceProof,
1120 filesystem_service: saddle_observability::file::PairedSignedProviderFilesystemEvidence,
1121 scheduler_service: saddle_runtime::termination_service::VerifiedSchedulerDeploymentOwner,
1122 supervisor_service: saddle_runtime::termination_service::VerifiedSupervisorServiceOwner,
1123 policy: saddle_runtime::resource_envelope::FiniteStartupPolicy,
1124 adapter_provenance: [u8; 32],
1125 bundle_identity: [u8; 32],
1126 nonce_identity: [u8; 32],
1127 binary_identity: [u8; 32],
1128 deployment_identity: [u8; 32],
1129 calibration_identity: [u8; 32],
1130 reservation: R,
1131 authority: L,
1132 ) -> Result<ApprovedExternalBundleVerification<R, L>>
1133 where
1134 R: ApprovedBundleNonceReservation,
1135 L: ListenerAuthorityProvider,
1136 {
1137 let calibration_common =
1138 saddle_admission::RuntimeCapacityCalibrationSourceLeaf::common_identities(&calibration);
1139 let calibration_generation =
1140 saddle_admission::RuntimeCapacityCalibrationSourceLeaf::owner_generation(&calibration);
1141 let envelope_build =
1142 saddle_admission::VerifiedStartupEnvelopeOwner::build_identity(&envelope);
1143 let envelope_gate =
1144 saddle_admission::VerifiedStartupEnvelopeOwner::gate_identity(&envelope);
1145 let envelope_resource =
1146 saddle_admission::VerifiedStartupEnvelopeOwner::resource_attestation(&envelope);
1147 let envelope_supervisor =
1148 saddle_admission::VerifiedStartupEnvelopeOwner::supervisor_attestation(&envelope);
1149 let environment = db_service.environment_identity();
1150 let binding = ExternalBundleBinding {
1151 bundle: bundle_identity,
1152 nonce: nonce_identity,
1153 binary: binary_identity,
1154 deployment: deployment_identity,
1155 calibration: calibration_identity,
1156 };
1157 let required_identities = [
1158 adapter_provenance,
1159 binding.bundle,
1160 binding.nonce,
1161 binding.binary,
1162 binding.deployment,
1163 binding.calibration,
1164 envelope_build,
1165 envelope_gate,
1166 envelope_resource,
1167 envelope_supervisor,
1168 environment,
1169 db_service.service_attestation(),
1170 supervisor_service.service_attestation(),
1171 ];
1172 validate_verified_deployment_identities(VerifiedDeploymentIdentitySet {
1173 runtime_common: calibration_common,
1174 calibration_common,
1175 runtime_generation: calibration_generation,
1176 calibration_generation,
1177 expected_provenance:
1178 saddle_admission::RuntimeCapacityCalibrationSourceLeaf::calibration_provenance(
1179 &calibration,
1180 ),
1181 calibration_provenance:
1182 saddle_admission::RuntimeCapacityCalibrationSourceLeaf::calibration_provenance(
1183 &calibration,
1184 ),
1185 envelope_build,
1186 envelope_gate,
1187 envelope_supervisor,
1188 environment,
1189 db_build: db_service.build_identity(),
1190 supervisor_build: supervisor_service.build_identity(),
1191 db_gate: db_service.gate_identity(),
1192 supervisor_gate: supervisor_service.gate_identity(),
1193 supervisor_identity: supervisor_service.supervisor_identity(),
1194 supervisor_environment: supervisor_service.environment_identity(),
1195 required_identities,
1196 })?;
1197 if !filesystem_service.matches_deployment(environment, envelope_build) {
1198 return Err(startup_error("saddle.approved_bundle_identity_mismatch"));
1199 }
1200 Ok(ApprovedExternalBundleVerification {
1201 deployment: VerifiedLauncherDeployment::Production(VerifiedFacadeDeployment {
1202 calibration,
1203 envelope,
1204 db_service,
1205 filesystem_service,
1206 scheduler_service,
1207 supervisor_service,
1208 policy,
1209 adapter_provenance,
1210 }),
1211 binding,
1212 reservation,
1213 authority,
1214 })
1215 }
1216}
1217
1218impl ListenerAuthoritySeal {
1219 pub fn bind_verified(
1222 self,
1223 bundle_identity: [u8; 32],
1224 nonce_identity: [u8; 32],
1225 binary_identity: [u8; 32],
1226 deployment_identity: [u8; 32],
1227 calibration_identity: [u8; 32],
1228 ) -> Result<VerifiedListenerAuthority> {
1229 let binding = ExternalBundleBinding {
1230 bundle: bundle_identity,
1231 nonce: nonce_identity,
1232 binary: binary_identity,
1233 deployment: deployment_identity,
1234 calibration: calibration_identity,
1235 };
1236 if [
1237 binding.bundle,
1238 binding.nonce,
1239 binding.binary,
1240 binding.deployment,
1241 binding.calibration,
1242 ]
1243 .contains(&[0; 32])
1244 {
1245 return Err(startup_error("saddle.listener_authority_missing_identity"));
1246 }
1247 Ok(VerifiedListenerAuthority { binding })
1248 }
1249}
1250
1251impl FacadeDeploymentOwner for VerifiedFacadeDeployment {
1252 type Calibration = saddle_runtime::capacity_leaf::VerifiedRuntimeBuildCalibrationOwner;
1253 type Envelope = saddle_runtime::resource_envelope::VerifiedResourceEnvelope;
1254 type DbService = saddle_db::internal::DbReturnServiceProof;
1255 type FilesystemService = saddle_observability::file::PairedSignedProviderFilesystemEvidence;
1256 type SchedulerService = saddle_runtime::termination_service::VerifiedSchedulerDeploymentOwner;
1257 type SupervisorService = saddle_runtime::termination_service::VerifiedSupervisorServiceOwner;
1258
1259 fn split(self, seal: FacadeDeploymentSeal) -> FacadeDeploymentOwnerParts<Self> {
1260 seal.bind(
1261 self.calibration,
1262 self.envelope,
1263 self.db_service,
1264 self.filesystem_service,
1265 self.scheduler_service,
1266 self.supervisor_service,
1267 self.policy,
1268 self.adapter_provenance,
1269 )
1270 }
1271}
1272
1273pub struct SaddleConfig {
1276 application: saddle_core::ApplicationId,
1277 database: Option<DatabaseConfig>,
1278 listen: SocketAddr,
1279 _observability: ObserverConfig,
1280}
1281
1282impl SaddleConfig {
1283 pub fn new(
1284 application: impl Into<saddle_core::ApplicationId>,
1285 database_url: impl Into<String>,
1286 listen: SocketAddr,
1287 ) -> Self {
1288 Self {
1289 application: application.into(),
1290 database: Some(DatabaseConfig::new(database_url)),
1291 listen,
1292 _observability: ObserverConfig::default(),
1293 }
1294 }
1295
1296 pub fn without_database(
1299 application: impl Into<saddle_core::ApplicationId>,
1300 listen: SocketAddr,
1301 ) -> Self {
1302 Self {
1303 application: application.into(),
1304 database: None,
1305 listen,
1306 _observability: ObserverConfig::default(),
1307 }
1308 }
1309}
1310
1311struct Saddle;
1313
1314pub struct ProductionLauncher {
1342 _private: (),
1343}
1344
1345#[doc(hidden)]
1348pub struct VerifiedProductionLauncher<R, L> {
1349 deployment: VerifiedLauncherDeployment,
1350 binding: ExternalBundleBinding,
1351 reservation: R,
1352 authority: L,
1353}
1354
1355impl ProductionLauncher {
1356 pub fn verify<B>(bundle: B) -> Result<VerifiedProductionLauncher<B::Reservation, B::Authority>>
1359 where
1360 B: ApprovedExternalBundle,
1361 {
1362 let verified = bundle.verify(ApprovedExternalBundleSeal { _private: () })?;
1363 Ok(VerifiedProductionLauncher {
1364 deployment: verified.deployment,
1365 binding: verified.binding,
1366 reservation: verified.reservation,
1367 authority: verified.authority,
1368 })
1369 }
1370}
1371
1372impl<R, L> VerifiedProductionLauncher<R, L>
1373where
1374 R: ApprovedBundleNonceReservation,
1375 L: ListenerAuthorityProvider,
1376{
1377 pub fn run<O>(self, config: SaddleConfig, generated: O) -> Result<()>
1380 where
1381 O: GeneratedApplicationOwner,
1382 {
1383 match self.deployment {
1384 VerifiedLauncherDeployment::Production(deployment) => Saddle::run_with_deployment(
1385 config,
1386 generated,
1387 deployment,
1388 PendingListenerAuthorization {
1389 binding: self.binding,
1390 reservation: self.reservation,
1391 authority: self.authority,
1392 },
1393 ),
1394 VerifiedLauncherDeployment::GoldenC8(compose) => Saddle::run_with_golden_c8(
1395 config,
1396 generated,
1397 compose,
1398 PendingListenerAuthorization {
1399 binding: self.binding,
1400 reservation: self.reservation,
1401 authority: self.authority,
1402 },
1403 ),
1404 }
1405 }
1406}
1407
1408struct GoldenC8GeneratedConsumer<R, L> {
1409 config: SaddleConfig,
1410 compose: VerifiedGoldenC8Compose,
1411 authorization: PendingListenerAuthorization<R, L>,
1412}
1413
1414impl<R, L> generated_consumer_sealed::Sealed for GoldenC8GeneratedConsumer<R, L>
1415where
1416 R: ApprovedBundleNonceReservation,
1417 L: ListenerAuthorityProvider,
1418{
1419}
1420
1421impl<R, L> GeneratedApplicationConsumer for GoldenC8GeneratedConsumer<R, L>
1422where
1423 R: ApprovedBundleNonceReservation,
1424 L: ListenerAuthorityProvider,
1425{
1426 fn consume<E, BC, F, C, const BODY: usize, const OUTPUT: usize, const ROUTES: usize>(
1427 self,
1428 parts: GeneratedBoundApplicationParts<
1429 saddle_service::internal::CompiledExecutionWithCapacityLeaf<E, BC, F, BODY, OUTPUT>,
1430 C,
1431 ROUTES,
1432 >,
1433 ) -> Result<()>
1434 where
1435 E: Fn(
1436 usize,
1437 BC,
1438 saddle_admission::ManagedBytes,
1439 saddle_service::internal::CompiledDbPermit,
1440 saddle_admission::ManagedResponseBuilder,
1441 ) -> F
1442 + Send
1443 + Sync
1444 + 'static,
1445 BC: Send + Unpin + 'static,
1446 F: std::future::Future<
1447 Output = std::result::Result<
1448 saddle_admission::ManagedResponse,
1449 saddle_service::internal::ExecutionError,
1450 >,
1451 > + Send
1452 + 'static,
1453 C: GeneratedContextFactory<BC>,
1454 {
1455 let approved = saddle_core::verify_golden_c8_startup_assembly_input(
1456 saddle_core::approved_golden_c8_startup_assembly_input(),
1457 )
1458 .map_err(|_| startup_error("saddle.golden_c8_startup_assembly_rejected"))?;
1459 let GeneratedApplicationParts {
1460 termination,
1461 continuation,
1462 bootstrap,
1463 runtime_preflight,
1464 } = parts;
1465 let continuation = (
1472 approved,
1473 continuation,
1474 bootstrap,
1475 runtime_preflight,
1476 termination,
1477 self.compose,
1478 self.config,
1479 );
1480 let PendingListenerAuthorization {
1481 binding,
1482 reservation,
1483 authority,
1484 } = self.authorization;
1485 let listener_preclosure = authorize_listener(continuation, binding, reservation, authority)
1486 .map_err(|failure| failure.error)?;
1487 let (
1488 approved,
1489 continuation,
1490 GeneratedProductionBootstrap {
1491 generation,
1492 type_identity,
1493 staged,
1494 },
1495 runtime_preflight,
1496 termination,
1497 compose,
1498 config,
1499 ) = listener_preclosure;
1500 let http_preclosure = (
1501 approved,
1502 continuation,
1503 generation,
1504 type_identity,
1505 staged,
1506 runtime_preflight,
1507 termination,
1508 compose,
1509 config,
1510 );
1511 let db_commit_preclosure = http_preclosure;
1517 let db_rollback_preclosure = db_commit_preclosure;
1522 let shutdown_preclosure = db_rollback_preclosure;
1523 let resource_zero_preclosure = shutdown_preclosure;
1528 let c8_acceptance_preclosure = resource_zero_preclosure;
1533 let _accepted = c8_acceptance_preclosure;
1538 Ok(())
1539 }
1540}
1541
1542#[doc(hidden)]
1543pub struct PendingListenerAuthorization<R, L> {
1544 binding: ExternalBundleBinding,
1545 reservation: R,
1546 authority: L,
1547}
1548
1549fn authorize_listener<T, R, L>(
1550 value: T,
1551 binding: ExternalBundleBinding,
1552 reservation: R,
1553 authority: L,
1554) -> std::result::Result<T, ListenerAuthorizationFailure<T>>
1555where
1556 R: ApprovedBundleNonceReservation,
1557 L: ListenerAuthorityProvider,
1558{
1559 let mut value = Some(value);
1560 let listener = authority
1561 .verify(ListenerAuthoritySeal { _private: () })
1562 .map_err(|error| ListenerAuthorizationFailure {
1563 value: value.take().expect("authorization value is present"),
1564 error,
1565 })?;
1566 if binding != listener.binding {
1567 return Err(ListenerAuthorizationFailure {
1568 value: value.take().expect("authorization value is present"),
1569 error: startup_error("saddle.listener_authority_binding_mismatch"),
1570 });
1571 }
1572 reservation
1573 .commit()
1574 .map_err(|error| ListenerAuthorizationFailure {
1575 value: value.take().expect("authorization value is present"),
1576 error,
1577 })?;
1578 Ok(value.take().expect("authorization value is present"))
1579}
1580
1581struct ListenerAuthorizationFailure<T> {
1582 value: T,
1583 error: SaddleError,
1584}
1585
1586struct FacadeGeneratedConsumer<D, R, L>
1587where
1588 D: FacadeDeploymentOwner<
1589 Calibration = saddle_runtime::capacity_leaf::VerifiedRuntimeBuildCalibrationOwner,
1590 FilesystemService = saddle_observability::file::PairedSignedProviderFilesystemEvidence,
1591 >,
1592{
1593 config: SaddleConfig,
1594 deployment: FacadeDeploymentOwnerParts<D>,
1595 authorization: PendingListenerAuthorization<R, L>,
1596}
1597
1598impl<D, R, L> generated_consumer_sealed::Sealed for FacadeGeneratedConsumer<D, R, L>
1599where
1600 D: FacadeDeploymentOwner<
1601 Calibration = saddle_runtime::capacity_leaf::VerifiedRuntimeBuildCalibrationOwner,
1602 FilesystemService = saddle_observability::file::PairedSignedProviderFilesystemEvidence,
1603 >,
1604 R: ApprovedBundleNonceReservation,
1605 L: ListenerAuthorityProvider,
1606{
1607}
1608
1609impl<D, R, L> GeneratedApplicationConsumer for FacadeGeneratedConsumer<D, R, L>
1610where
1611 D: FacadeDeploymentOwner<
1612 Calibration = saddle_runtime::capacity_leaf::VerifiedRuntimeBuildCalibrationOwner,
1613 FilesystemService = saddle_observability::file::PairedSignedProviderFilesystemEvidence,
1614 >,
1615 R: ApprovedBundleNonceReservation,
1616 L: ListenerAuthorityProvider,
1617{
1618 fn consume<E, BC, F, C, const BODY: usize, const OUTPUT: usize, const ROUTES: usize>(
1619 self,
1620 parts: GeneratedBoundApplicationParts<
1621 saddle_service::internal::CompiledExecutionWithCapacityLeaf<E, BC, F, BODY, OUTPUT>,
1622 C,
1623 ROUTES,
1624 >,
1625 ) -> Result<()>
1626 where
1627 E: Fn(
1628 usize,
1629 BC,
1630 saddle_admission::ManagedBytes,
1631 saddle_service::internal::CompiledDbPermit,
1632 saddle_admission::ManagedResponseBuilder,
1633 ) -> F
1634 + Send
1635 + Sync
1636 + 'static,
1637 BC: Send + Unpin + 'static,
1638 F: std::future::Future<
1639 Output = std::result::Result<
1640 saddle_admission::ManagedResponse,
1641 saddle_service::internal::ExecutionError,
1642 >,
1643 > + Send
1644 + 'static,
1645 C: GeneratedContextFactory<BC>,
1646 {
1647 let GeneratedApplicationParts {
1648 termination,
1649 continuation,
1650 bootstrap,
1651 runtime_preflight,
1652 } = parts;
1653 let candidate = crate::startup_candidate::prepare_production_candidate_transaction(
1654 bootstrap
1655 .staged
1656 .service_production_fact_input()
1657 .map_err(|_| startup_error("saddle.service_production_fact_failed"))?,
1658 )?;
1659 let FacadeDeploymentParts {
1660 calibration,
1661 envelope,
1662 db_service,
1663 filesystem_service,
1664 scheduler_service,
1665 supervisor_service,
1666 policy,
1667 adapter_provenance,
1668 } = self.deployment;
1669 let (runtime, _runtime_termination, calibration) =
1670 saddle_runtime::capacity_leaf::verify_runtime_capacity_leaf_from_preflight(
1671 runtime_preflight,
1672 calibration,
1673 )
1674 .map_err(|_| startup_error("saddle.runtime_layout_calibration_pair_failed"))?;
1675 let (staged, generated) = bootstrap
1676 .staged
1677 .compose_generated(runtime, calibration, termination)
1678 .map_err(|_| startup_error("saddle.generated_facts_composition_failed"))?;
1679 let bootstrap = GeneratedProductionBootstrap {
1680 staged,
1681 ..bootstrap
1682 };
1683 let (generated, continuation) =
1684 saddle_admission::seal_generated_startup_continuation(continuation, generated)
1685 .map_err(|_| startup_error("saddle.generated_continuation_foreign"))?
1686 .into_parts();
1687 let (filesystem_service, generated) = filesystem_service
1688 .pair_component_generation(generated)
1689 .map_err(|_| startup_error("saddle.component_generation_foreign"))?;
1690 let (pending, physical) =
1691 saddle_runtime::resource_envelope::create_runtime_startup_plan_with_filesystem_bundle(
1692 policy,
1693 adapter_provenance,
1694 envelope.into(),
1695 generated,
1696 db_service,
1697 filesystem_service,
1698 scheduler_service,
1699 supervisor_service,
1700 )
1701 .map_err(|failure| {
1702 let _ = failure.consume_for_application_error();
1703 startup_error("saddle.startup_plan_creation_failed")
1704 })?;
1705 let factory = StartupManagedDatabaseFactory::awaiting_started_observability(
1706 self.config.database.clone(),
1707 );
1708 let owners = saddle_runtime::startup_assembly::assemble_actual_startup_owners(
1709 pending,
1710 factory,
1711 saddle_runtime::startup_assembly::StartupObservabilityInput::Production(physical),
1712 )
1713 .map_err(|_| startup_error("saddle.startup_actual_owner_assembly_failed"))?;
1714 bootstrap.run(
1715 FacadeBootstrapCoordinator {
1716 config: self.config,
1717 adapter_provenance,
1718 },
1719 owners,
1720 continuation,
1721 self.authorization,
1722 candidate,
1723 )
1724 }
1725}
1726
1727impl Saddle {
1728 fn run_with_golden_c8<O, R, L>(
1729 config: SaddleConfig,
1730 generated: O,
1731 compose: VerifiedGoldenC8Compose,
1732 authorization: PendingListenerAuthorization<R, L>,
1733 ) -> Result<()>
1734 where
1735 O: GeneratedApplicationOwner,
1736 R: ApprovedBundleNonceReservation,
1737 L: ListenerAuthorityProvider,
1738 {
1739 generated.consume(
1740 GeneratedApplicationSeal::issue(),
1741 GoldenC8GeneratedConsumer {
1742 config,
1743 compose,
1744 authorization,
1745 },
1746 )
1747 }
1748
1749 fn run_with_deployment<O, D, R, L>(
1751 config: SaddleConfig,
1752 generated: O,
1753 deployment: D,
1754 authorization: PendingListenerAuthorization<R, L>,
1755 ) -> Result<()>
1756 where
1757 O: GeneratedApplicationOwner,
1758 D: FacadeDeploymentOwner<
1759 Calibration = saddle_runtime::capacity_leaf::VerifiedRuntimeBuildCalibrationOwner,
1760 FilesystemService = saddle_observability::file::PairedSignedProviderFilesystemEvidence,
1761 >,
1762 R: ApprovedBundleNonceReservation,
1763 L: ListenerAuthorityProvider,
1764 {
1765 let deployment = deployment.split(FacadeDeploymentSeal::issue());
1766 generated.consume(
1767 GeneratedApplicationSeal::issue(),
1768 FacadeGeneratedConsumer::<D, R, L> {
1769 config,
1770 deployment,
1771 authorization,
1772 },
1773 )
1774 }
1775}
1776
1777pub(crate) fn startup_error(code: &'static str) -> SaddleError {
1778 SaddleError::new(
1779 ErrorKind::Infrastructure,
1780 code,
1781 "Saddle application initialization failed",
1782 )
1783}
1784
1785#[cfg(test)]
1786mod generated_bootstrap_contract_tests {
1787 use super::*;
1788 use std::sync::{
1789 Arc,
1790 atomic::{AtomicBool, AtomicUsize, Ordering},
1791 };
1792
1793 static REJECTED_BUNDLES: AtomicUsize = AtomicUsize::new(0);
1794
1795 fn binding(seed: u8) -> ExternalBundleBinding {
1796 ExternalBundleBinding {
1797 bundle: [seed; 32],
1798 nonce: [seed.wrapping_add(1); 32],
1799 binary: [seed.wrapping_add(2); 32],
1800 deployment: [seed.wrapping_add(3); 32],
1801 calibration: [seed.wrapping_add(4); 32],
1802 }
1803 }
1804
1805 struct ReferenceAuthority(ExternalBundleBinding);
1806
1807 impl ListenerAuthorityProvider for ReferenceAuthority {
1808 fn verify(self, seal: ListenerAuthoritySeal) -> Result<VerifiedListenerAuthority> {
1809 seal.bind_verified(
1810 self.0.bundle,
1811 self.0.nonce,
1812 self.0.binary,
1813 self.0.deployment,
1814 self.0.calibration,
1815 )
1816 }
1817 }
1818
1819 struct CountingAuthority {
1820 binding: ExternalBundleBinding,
1821 calls: Arc<AtomicUsize>,
1822 }
1823
1824 impl ListenerAuthorityProvider for CountingAuthority {
1825 fn verify(self, seal: ListenerAuthoritySeal) -> Result<VerifiedListenerAuthority> {
1826 self.calls.fetch_add(1, Ordering::SeqCst);
1827 seal.bind_verified(
1828 self.binding.bundle,
1829 self.binding.nonce,
1830 self.binding.binary,
1831 self.binding.deployment,
1832 self.binding.calibration,
1833 )
1834 }
1835 }
1836
1837 struct CountingReservation {
1838 calls: Arc<AtomicUsize>,
1839 }
1840
1841 impl ApprovedBundleNonceReservation for CountingReservation {
1842 fn commit(self) -> Result<()> {
1843 self.calls.fetch_add(1, Ordering::SeqCst);
1844 Ok(())
1845 }
1846 }
1847
1848 struct RejectingAuthority {
1849 calls: Arc<AtomicUsize>,
1850 }
1851
1852 impl ListenerAuthorityProvider for RejectingAuthority {
1853 fn verify(self, _seal: ListenerAuthoritySeal) -> Result<VerifiedListenerAuthority> {
1854 self.calls.fetch_add(1, Ordering::SeqCst);
1855 Err(startup_error("test.listener_authority_rejected"))
1856 }
1857 }
1858
1859 struct RejectingReservation {
1860 calls: Arc<AtomicUsize>,
1861 }
1862
1863 impl ApprovedBundleNonceReservation for RejectingReservation {
1864 fn commit(self) -> Result<()> {
1865 self.calls.fetch_add(1, Ordering::SeqCst);
1866 Err(startup_error("test.bundle_nonce_commit_rejected"))
1867 }
1868 }
1869
1870 fn pending_counting_authorization(
1871 authority_calls: &Arc<AtomicUsize>,
1872 commit_calls: &Arc<AtomicUsize>,
1873 ) -> PendingListenerAuthorization<CountingReservation, CountingAuthority> {
1874 let binding = binding(40);
1875 PendingListenerAuthorization {
1876 binding,
1877 reservation: CountingReservation {
1878 calls: Arc::clone(commit_calls),
1879 },
1880 authority: CountingAuthority {
1881 binding,
1882 calls: Arc::clone(authority_calls),
1883 },
1884 }
1885 }
1886
1887 struct ReferenceReservation(Arc<AtomicBool>);
1888
1889 impl ApprovedBundleNonceReservation for ReferenceReservation {
1890 fn commit(self) -> Result<()> {
1891 self.0
1892 .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
1893 .map(|_| ())
1894 .map_err(|_| startup_error("test.bundle_nonce_replay"))
1895 }
1896 }
1897
1898 struct OwnerAggregate(Arc<AtomicUsize>);
1899
1900 impl Drop for OwnerAggregate {
1901 fn drop(&mut self) {
1902 self.0.fetch_add(1, Ordering::SeqCst);
1903 }
1904 }
1905
1906 struct RejectedBundle;
1907 struct RejectedReservation;
1908 struct RejectedAuthority;
1909
1910 impl ApprovedBundleNonceReservation for RejectedReservation {
1911 fn commit(self) -> Result<()> {
1912 panic!("rejected bundle cannot reserve a nonce")
1913 }
1914 }
1915
1916 impl ApprovedExternalBundle for RejectedBundle {
1917 type Reservation = RejectedReservation;
1918 type Authority = RejectedAuthority;
1919
1920 fn verify(
1921 self,
1922 _seal: ApprovedExternalBundleSeal,
1923 ) -> Result<ApprovedExternalBundleVerification<Self::Reservation, Self::Authority>>
1924 {
1925 REJECTED_BUNDLES.fetch_add(1, Ordering::SeqCst);
1926 Err(startup_error("test.bundle_rejected"))
1927 }
1928 }
1929
1930 impl ListenerAuthorityProvider for RejectedAuthority {
1931 fn verify(self, _seal: ListenerAuthoritySeal) -> Result<VerifiedListenerAuthority> {
1932 panic!("listener authority must not run after bundle rejection")
1933 }
1934 }
1935
1936 #[test]
1937 fn rejected_bundle_returns_before_any_launcher_exists() {
1938 let before = REJECTED_BUNDLES.load(Ordering::SeqCst);
1939 assert!(ProductionLauncher::verify(RejectedBundle).is_err());
1940 assert_eq!(REJECTED_BUNDLES.load(Ordering::SeqCst), before + 1);
1941 }
1942
1943 fn matching_deployment_identities() -> VerifiedDeploymentIdentitySet {
1944 VerifiedDeploymentIdentitySet {
1945 runtime_common: [[1; 32], [2; 32], [3; 32]],
1946 calibration_common: [[1; 32], [2; 32], [3; 32]],
1947 runtime_generation: 7,
1948 calibration_generation: 7,
1949 expected_provenance: [[4; 32]; 9],
1950 calibration_provenance: [[4; 32]; 9],
1951 envelope_build: [5; 32],
1952 envelope_gate: [6; 32],
1953 envelope_supervisor: [8; 32],
1954 environment: [9; 32],
1955 db_build: [5; 32],
1956 supervisor_build: [5; 32],
1957 db_gate: [6; 32],
1958 supervisor_gate: [6; 32],
1959 supervisor_identity: [8; 32],
1960 supervisor_environment: [9; 32],
1961 required_identities: [[10; 32]; 13],
1962 }
1963 }
1964
1965 #[test]
1966 fn matching_seven_owner_identities_reach_the_launcher_boundary() {
1967 assert!(validate_verified_deployment_identities(matching_deployment_identities()).is_ok());
1968 }
1969
1970 #[test]
1971 fn missing_foreign_drift_and_replay_generation_fail_before_launcher() {
1972 let mut missing = matching_deployment_identities();
1973 missing.required_identities[4] = [0; 32];
1974 assert!(validate_verified_deployment_identities(missing).is_err());
1975
1976 let mut foreign = matching_deployment_identities();
1977 foreign.supervisor_environment = [11; 32];
1978 assert!(validate_verified_deployment_identities(foreign).is_err());
1979
1980 let mut drift = matching_deployment_identities();
1981 drift.supervisor_gate = [12; 32];
1982 assert!(validate_verified_deployment_identities(drift).is_err());
1983
1984 let mut replay = matching_deployment_identities();
1985 replay.calibration_generation += 1;
1986 assert!(validate_verified_deployment_identities(replay).is_err());
1987 }
1988
1989 #[test]
1990 fn reference_authority_commits_matching_nonce_once() {
1991 let committed = Arc::new(AtomicBool::new(false));
1992 let expected = binding(10);
1993 assert!(
1994 authorize_listener(
1995 (),
1996 expected,
1997 ReferenceReservation(Arc::clone(&committed)),
1998 ReferenceAuthority(expected),
1999 )
2000 .is_ok()
2001 );
2002 assert!(committed.load(Ordering::SeqCst));
2003 assert!(
2004 authorize_listener(
2005 (),
2006 expected,
2007 ReferenceReservation(Arc::clone(&committed)),
2008 ReferenceAuthority(expected),
2009 )
2010 .is_err()
2011 );
2012 }
2013
2014 #[test]
2015 fn concurrent_nonce_commit_has_exactly_one_success() {
2016 let committed = Arc::new(AtomicBool::new(false));
2017 let expected = binding(20);
2018 let threads: Vec<_> = (0..8)
2019 .map(|_| {
2020 let committed = Arc::clone(&committed);
2021 std::thread::spawn(move || {
2022 authorize_listener(
2023 (),
2024 expected,
2025 ReferenceReservation(committed),
2026 ReferenceAuthority(expected),
2027 )
2028 .is_ok()
2029 })
2030 })
2031 .collect();
2032 assert_eq!(
2033 threads
2034 .into_iter()
2035 .map(|thread| thread.join().unwrap())
2036 .filter(|success| *success)
2037 .count(),
2038 1
2039 );
2040 }
2041
2042 #[test]
2043 fn authority_drift_drops_owner_aggregate_without_committing_nonce() {
2044 let committed = Arc::new(AtomicBool::new(false));
2045 let drops = Arc::new(AtomicUsize::new(0));
2046 let expected = binding(30);
2047 assert!(
2048 authorize_listener(
2049 OwnerAggregate(Arc::clone(&drops)),
2050 expected,
2051 ReferenceReservation(Arc::clone(&committed)),
2052 ReferenceAuthority(binding(31)),
2053 )
2054 .is_err()
2055 );
2056 assert_eq!(drops.load(Ordering::SeqCst), 1);
2057 assert!(!committed.load(Ordering::SeqCst));
2058 }
2059
2060 #[test]
2061 fn authority_failure_returns_prepared_owner_without_nonce_commit() {
2062 let authority_calls = Arc::new(AtomicUsize::new(0));
2063 let commit_calls = Arc::new(AtomicUsize::new(0));
2064 let drops = Arc::new(AtomicUsize::new(0));
2065 let result = authorize_listener(
2066 OwnerAggregate(Arc::clone(&drops)),
2067 binding(35),
2068 CountingReservation {
2069 calls: Arc::clone(&commit_calls),
2070 },
2071 RejectingAuthority {
2072 calls: Arc::clone(&authority_calls),
2073 },
2074 );
2075 assert!(result.is_err());
2076 drop(result);
2077 assert_eq!(authority_calls.load(Ordering::SeqCst), 1);
2078 assert_eq!(commit_calls.load(Ordering::SeqCst), 0);
2079 assert_eq!(drops.load(Ordering::SeqCst), 1);
2080 }
2081
2082 #[test]
2083 fn nonce_commit_failure_returns_prepared_owner_before_listener_install() {
2084 let authority_calls = Arc::new(AtomicUsize::new(0));
2085 let commit_calls = Arc::new(AtomicUsize::new(0));
2086 let drops = Arc::new(AtomicUsize::new(0));
2087 let expected = binding(36);
2088 let result = authorize_listener(
2089 OwnerAggregate(Arc::clone(&drops)),
2090 expected,
2091 RejectingReservation {
2092 calls: Arc::clone(&commit_calls),
2093 },
2094 CountingAuthority {
2095 binding: expected,
2096 calls: Arc::clone(&authority_calls),
2097 },
2098 );
2099 assert!(result.is_err());
2100 drop(result);
2101 assert_eq!(authority_calls.load(Ordering::SeqCst), 1);
2102 assert_eq!(commit_calls.load(Ordering::SeqCst), 1);
2103 assert_eq!(drops.load(Ordering::SeqCst), 1);
2104 }
2105
2106 #[test]
2107 fn composition_failure_discards_pending_authorization_without_side_effects() {
2108 let authority_calls = Arc::new(AtomicUsize::new(0));
2109 let commit_calls = Arc::new(AtomicUsize::new(0));
2110 let pending = pending_counting_authorization(&authority_calls, &commit_calls);
2111
2112 let composition: Result<()> = Err(startup_error("test.composition_failed"));
2113 assert!(composition.is_err());
2114 drop(pending);
2115
2116 assert_eq!(authority_calls.load(Ordering::SeqCst), 0);
2117 assert_eq!(commit_calls.load(Ordering::SeqCst), 0);
2118 }
2119
2120 #[test]
2121 fn late_prepare_failure_discards_pending_authorization_without_side_effects() {
2122 let authority_calls = Arc::new(AtomicUsize::new(0));
2123 let commit_calls = Arc::new(AtomicUsize::new(0));
2124 let pending = pending_counting_authorization(&authority_calls, &commit_calls);
2125
2126 let late_prepare: Result<()> = Err(startup_error("test.http1_prepare_failed"));
2127 assert!(late_prepare.is_err());
2128 drop(pending);
2129
2130 assert_eq!(authority_calls.load(Ordering::SeqCst), 0);
2131 assert_eq!(commit_calls.load(Ordering::SeqCst), 0);
2132 }
2133
2134 #[test]
2135 fn facade_issues_distinct_termination_and_continuation_proofs() {
2136 use saddle_admission::{
2137 GeneratedTerminationTopologyWorkOwner, PreflightGeneratedStaticContinuationOwner,
2138 };
2139
2140 let common = [[0xb2; 32], [0xa2; 32], [0xd2; 32]];
2141 let termination = FacadeGeneratedTermination {
2142 common,
2143 generation: 9,
2144 topology: [5, 1, 1, 3],
2145 };
2146 let continuation = FacadeGeneratedContinuation {
2147 common,
2148 generation: 9,
2149 };
2150 let identities = [
2151 termination.leaf_identity(),
2152 termination.termination_topology_identity(),
2153 termination.db_return_work_identity(),
2154 termination.writer_work_identity(),
2155 termination.runtime_work_identity(),
2156 ];
2157 assert!(!identities.contains(&[0; 32]));
2158 for (index, identity) in identities.iter().enumerate() {
2159 assert!(!identities[index + 1..].contains(identity));
2160 assert!(!common.contains(identity));
2161 }
2162 assert_eq!(termination.termination_topology(), [5, 1, 1, 3]);
2163 assert_eq!(continuation.build_identity(), common[0]);
2164 assert_eq!(continuation.artifact_identity(), common[1]);
2165 assert_eq!(continuation.route_set_identity(), common[2]);
2166 assert_eq!(continuation.owner_generation(), 9);
2167 }
2168}