impl<'a> Rpkiv1PublicationPointRunner<'a> { pub(crate) fn ccr_accumulator_snapshot(&self) -> Option { self.ccr_accumulator .as_ref() .and_then(|accumulator| accumulator.lock().ok().map(|guard| guard.clone())) } pub(crate) fn append_ccr_manifest_projection( &self, projection: &CcrManifestProjection, ) -> Result<(), String> { if let Some(accumulator) = self.ccr_accumulator.as_ref() { accumulator .lock() .map_err(|_| "lock CCR accumulator failed".to_string())? .append_manifest_projection(projection)?; } Ok(()) } pub(crate) fn stage_fresh_publication_point_after_repo_ready( &self, ca: &CaInstanceHandle, repo_sync_ok: bool, repo_sync_err: Option<&str>, ) -> Result { let snapshot_prepare_started = std::time::Instant::now(); let issuer_ca_der = ca_certificate_der_for_validation(ca, self.store, self.timing.as_ref()) .map_err(|detail| FreshPublicationPointStageError { warnings: Vec::new(), error: ManifestFreshError::IssuerCaLoadFailed { detail }, snapshot_prepare_ms: snapshot_prepare_started.elapsed().as_millis() as u64, })?; let issuer_ca_der: Arc<[u8]> = Arc::from(issuer_ca_der.as_ref()); let identity = ResourceCertificate::decode_der(&issuer_ca_der) .map(|cert| { crate::repository::storage::CaIdentity::from_spki( ca.anchor_namespace, &cert.tbs.subject_public_key_info, ) }) .map_err(|e| FreshPublicationPointStageError { warnings: Vec::new(), error: ManifestFreshError::IssuerCaLoadFailed { detail: e.to_string(), }, snapshot_prepare_ms: snapshot_prepare_started.elapsed().as_millis() as u64, })?; let rename_warnings: Vec = crate::validation::manifest::manifest_rename_warning( self.store, &identity, &ca.manifest_sia_uris, &ca.manifest_rsync_uri, ) .map_err(|error| FreshPublicationPointStageError { warnings: Vec::new(), error, snapshot_prepare_ms: snapshot_prepare_started.elapsed().as_millis() as u64, })? .into_iter() .collect(); for warning in &rename_warnings { crate::logging::emit( crate::logging::Level::Warn, "manifest_filename_changed", || serde_json::json!({"manifest_rsync_uri": ca.manifest_rsync_uri, "message": warning.message, "candidate_accepted": null}), ); } let fresh_publication_point = { let _manifest_total = self .timing .as_ref() .map(|t| t.span_phase("manifest_processing_total")); process_manifest_publication_point_fresh_after_repo_sync_with_timing( self.store, &ca.anchor_namespace, &ca.manifest_rsync_uri, &ca.publication_point_rsync_uri, self.current_repo_index.as_ref(), issuer_ca_der.as_ref(), ca.ca_certificate_rsync_uri.as_deref(), self.validation_time, repo_sync_ok, repo_sync_err, ) }; let snapshot_prepare_ms = snapshot_prepare_started.elapsed().as_millis() as u64; let (fresh_point, snapshot_prepare_timing) = fresh_publication_point.map_err(|error| FreshPublicationPointStageError { warnings: rename_warnings .iter() .cloned() .map(|mut warning| { warning .message .push_str(&format!(" candidate_accepted=false reason={error}")); warning }) .collect(), error, snapshot_prepare_ms, })?; if let Some(timing) = self.timing.as_ref() { timing.record_phase_nanos( "fresh_snapshot_prepare_total", snapshot_prepare_ms.saturating_mul(1_000_000), ); timing.record_phase_nanos( "fresh_snapshot_manifest_load_total", snapshot_prepare_timing .manifest_load_ms .saturating_mul(1_000_000), ); timing.record_phase_nanos( "fresh_snapshot_manifest_decode_total", snapshot_prepare_timing .manifest_decode_ms .saturating_mul(1_000_000), ); timing.record_phase_nanos( "fresh_snapshot_anti_rollback_total", snapshot_prepare_timing .anti_rollback_guard_ms .saturating_mul(1_000_000), ); timing.record_phase_nanos( "fresh_snapshot_manifest_entries_total", snapshot_prepare_timing .manifest_entries_ms .saturating_mul(1_000_000), ); timing.record_phase_nanos( "fresh_snapshot_pack_files_total", snapshot_prepare_timing .pack_files_ms .saturating_mul(1_000_000), ); timing.record_phase_nanos( "fresh_snapshot_ee_path_validate_total", snapshot_prepare_timing .ee_path_validate_ms .saturating_mul(1_000_000), ); timing.record_count("fresh_publication_points", 1); timing.record_count( "fresh_manifest_files_total", snapshot_prepare_timing.manifest_file_count as u64, ); } self.record_publication_point_step_ms( &ca.manifest_rsync_uri, "fresh_snapshot_prepare", snapshot_prepare_ms, ); self.record_publication_point_step_ms( &ca.manifest_rsync_uri, "fresh_snapshot_manifest_load", snapshot_prepare_timing.manifest_load_ms, ); self.record_publication_point_step_ms( &ca.manifest_rsync_uri, "fresh_snapshot_manifest_decode", snapshot_prepare_timing.manifest_decode_ms, ); self.record_publication_point_step_ms( &ca.manifest_rsync_uri, "fresh_snapshot_anti_rollback", snapshot_prepare_timing.anti_rollback_guard_ms, ); self.record_publication_point_step_ms( &ca.manifest_rsync_uri, "fresh_snapshot_manifest_entries", snapshot_prepare_timing.manifest_entries_ms, ); self.record_publication_point_step_ms( &ca.manifest_rsync_uri, "fresh_snapshot_pack_files", snapshot_prepare_timing.pack_files_ms, ); self.record_publication_point_step_ms( &ca.manifest_rsync_uri, "fresh_snapshot_ee_path_validate", snapshot_prepare_timing.ee_path_validate_ms, ); let child_discovery_started = std::time::Instant::now(); let out = { let _child_disc_total = self .timing .as_ref() .map(|t| t.span_phase("child_discovery_total")); discover_children_from_fresh_snapshot_with_audit_with_issuer_der( ca, issuer_ca_der.as_ref(), &fresh_point, self.validation_time, self.timing.as_ref(), self.policy, ) }; let (discovered_children, child_audits, discovered_router_keys, mut warnings) = match out { Ok(out) => (out.children, out.audits, out.router_keys, Vec::new()), Err(e) => ( Vec::new(), Vec::new(), Vec::new(), vec![ Warning::new(format!("child CA discovery failed: {e}")) .with_category(crate::output::report::WarningCategory::Certificate) .with_rfc_refs(&[RfcRef("RFC 6487 ยง7.2")]) .with_context(&ca.manifest_rsync_uri), ], ), }; let child_discovery_ms = child_discovery_started.elapsed().as_millis() as u64; if let Some(timing) = self.timing.as_ref() { timing.record_phase_nanos( "fresh_child_discovery_total", child_discovery_ms.saturating_mul(1_000_000), ); timing.record_count( "fresh_children_discovered", discovered_children.len() as u64, ); timing.record_count("fresh_child_audits", child_audits.len() as u64); timing.record_count( "fresh_router_keys_discovered", discovered_router_keys.len() as u64, ); } self.record_publication_point_step_ms( &ca.manifest_rsync_uri, "fresh_child_discovery", child_discovery_ms, ); warnings.extend(rename_warnings); Ok(FreshPublicationPointStage { fresh_point, issuer_ca_der, snapshot_prepare_timing, snapshot_prepare_ms, discovered_children, child_audits, discovered_router_keys, child_discovery_ms, warnings, }) } pub(crate) fn finalize_fresh_publication_point_from_reducer( &self, ca: &CaInstanceHandle, fresh_point: &FreshValidatedPublicationPoint, mut warnings: Vec, objects: crate::validation::objects::ObjectsOutput, child_audits: Vec, discovered_children: Vec, repo_sync_source: Option<&str>, repo_sync_phase: Option<&str>, repo_sync_duration_ms: u64, repo_sync_err: Option<&str>, ) -> Result { let snapshot_pack_started = std::time::Instant::now(); let pack = fresh_point.to_publication_point_snapshot(); let snapshot_pack_ms = snapshot_pack_started.elapsed().as_millis() as u64; if let Some(timing) = self.timing.as_ref() { timing.record_phase_nanos( "fresh_snapshot_pack_total", snapshot_pack_ms.saturating_mul(1_000_000), ); } self.record_publication_point_step_ms( &ca.manifest_rsync_uri, "fresh_snapshot_pack", snapshot_pack_ms, ); let mut ccr_projection = None; let mut ccr_projection_build_ms = 0; let mut ccr_append_ms = 0; if self.ccr_accumulator.is_some() { let ccr_projection_build_started = std::time::Instant::now(); let subordinate_skis = discovered_children .iter() .map(|child| { if let Some(projection) = &child.child_entry_projection { return hex::decode(&projection.child_ski) .map_err(|error| error.to_string()); } let der = child.handle.ca_certificate_der(self.store)?; let certificate = ResourceCertificate::decode_der(der.as_ref()) .map_err(|error| error.to_string())?; certificate .tbs .extensions .subject_key_identifier .clone() .ok_or_else(|| "child certificate missing SubjectKeyIdentifier".to_string()) }) .collect::, String>>()?; let ccr_manifest_projection = crate::ccr::projection::from_snapshot(ca, &pack, subordinate_skis)?; ccr_projection_build_ms = ccr_projection_build_started.elapsed().as_millis() as u64; ccr_projection = Some(ccr_manifest_projection); } let (_, commit_warning) = crate::validation::manifest::commit_manifest_history( self.store, &fresh_point.history_candidate, ) .map_err(|error| format!("persist manifest freshness failed: {error}"))?; if let Some(warning) = commit_warning { warnings.retain(|item| !item.message.starts_with("manifest filename changed:")); warnings.push(warning); } for warning in &mut warnings { if warning.message.starts_with("manifest filename changed:") { warning.message.push_str(&format!( " new_number={} new_this_update={} candidate_accepted=true", hex::encode_upper(&pack.manifest_number_be), pack.this_update.rfc3339_utc )); } } if let Some(projection) = ccr_projection { let started = std::time::Instant::now(); self.append_ccr_manifest_projection(&projection)?; ccr_append_ms = started.elapsed().as_millis() as u64; } if let Some(timing) = self.timing.as_ref() { timing.record_phase_nanos( "fresh_ccr_projection_build_total", ccr_projection_build_ms.saturating_mul(1_000_000), ); timing.record_phase_nanos( "fresh_ccr_append_total", ccr_append_ms.saturating_mul(1_000_000), ); } self.record_publication_point_step_ms( &ca.manifest_rsync_uri, "fresh_ccr_projection_build", ccr_projection_build_ms, ); self.record_publication_point_step_ms( &ca.manifest_rsync_uri, "fresh_ccr_append", ccr_append_ms, ); let audit_build_started = std::time::Instant::now(); let audit = build_publication_point_audit_from_snapshot( ca, PublicationPointSource::Fresh, repo_sync_source, repo_sync_phase, Some(repo_sync_duration_ms), repo_sync_err, &pack, &warnings, &objects, &child_audits, ); let audit_build_ms = audit_build_started.elapsed().as_millis() as u64; if let Some(timing) = self.timing.as_ref() { timing.record_phase_nanos( "fresh_audit_build_total", audit_build_ms.saturating_mul(1_000_000), ); } self.record_publication_point_step_ms( &ca.manifest_rsync_uri, "fresh_audit_build", audit_build_ms, ); Ok(FreshPublicationPointFinalizeOutput { result: PublicationPointRunResult { source: PublicationPointSource::Fresh, snapshot: Some(pack), warnings, objects, audit, discovered_children, }, snapshot_pack_ms, ccr_projection_build_ms, ccr_append_ms, audit_build_ms, }) } }