2026-07-16 03:46:31 +01:00
use crate ::open_epiphany_cultmesh_node ;
2026-08-09 08:27:27 +01:00
use anyhow ::{ Context , Result , bail } ;
use chrono ::Utc ;
use cultcache_rs ::SingleFileMessagePackBackingStore ;
use std ::path ::Path ;
pub use epiphany_release_construction ::* ;
2026-07-16 03:46:31 +01:00
const RELEASE_KEY_PREFIX : & str = " epiphany-local/packaged-release/by-id/ " ;
const RELEASE_HEAD_KEY : & str = " epiphany-local/packaged-release/current " ;
2026-07-19 00:03:11 +01:00
2026-07-16 03:46:31 +01:00
pub fn publish_epiphany_packaged_release (
store : & Path ,
runtime_id : & str ,
entry : EpiphanyPackagedReleaseEntry ,
) -> Result < EpiphanyPackagedReleaseEntry > {
validate_epiphany_packaged_release ( & entry ) ? ;
verify_epiphany_packaged_release_files ( & entry ) ? ;
if entry . runtime_id ! = runtime_id {
bail! ( " release witness runtime disagrees with target Verse " ) ;
}
let node = open_epiphany_cultmesh_node ( store , runtime_id . to_string ( ) ) ? ;
let identity_key = format! ( " {RELEASE_KEY_PREFIX} {} " , entry . release_id ) ;
let existing = node . get ::< EpiphanyPackagedReleaseEntry > ( & identity_key ) ? ;
if existing . as_ref ( ) . is_some_and ( | current | current ! = & entry ) {
bail! ( " immutable packaged release identity collision " ) ;
}
let head = EpiphanyPackagedReleaseHead {
schema_version : EPIPHANY_PACKAGED_RELEASE_HEAD_SCHEMA_VERSION . into ( ) ,
runtime_id : runtime_id . into ( ) ,
release_id : entry . release_id . clone ( ) ,
2026-08-09 08:27:27 +01:00
witness_sha256 : epiphany_packaged_release_witness_sha256 ( & entry ) ? ,
2026-07-16 03:46:31 +01:00
published_at_utc : Utc ::now ( ) . to_rfc3339 ( ) ,
} ;
let prior_head = node
. cache ( )
. get_envelope ::< EpiphanyPackagedReleaseHead > ( RELEASE_HEAD_KEY ) ? ;
let mut expected = Vec ::new ( ) ;
if let Some ( envelope ) = prior_head {
expected . push ( envelope ) ;
}
let mut replacements = Vec ::new ( ) ;
if existing . is_none ( ) {
replacements . push ( node . cache ( ) . prepare_entry ( & identity_key , & entry ) ? . 0 ) ;
}
replacements . push ( node . cache ( ) . prepare_entry ( RELEASE_HEAD_KEY , & head ) ? . 0 ) ;
if ! SingleFileMessagePackBackingStore ::new ( store )
. compare_and_swap_batch ( & expected , replacements ) ?
{
bail! ( " packaged release publication lost current-head compare-and-swap " ) ;
}
Ok ( entry )
}
pub fn load_epiphany_packaged_release (
store : & Path ,
runtime_id : & str ,
release_id : & str ,
) -> Result < Option < EpiphanyPackagedReleaseEntry > > {
open_epiphany_cultmesh_node ( store , runtime_id . to_string ( ) ) ?
. get ( & format! ( " {RELEASE_KEY_PREFIX} {release_id} " ) )
}
pub fn load_epiphany_packaged_release_head (
store : & Path ,
runtime_id : & str ,
) -> Result < Option < EpiphanyPackagedReleaseHead > > {
open_epiphany_cultmesh_node ( store , runtime_id . to_string ( ) ) ? . get ( RELEASE_HEAD_KEY )
}
pub fn authenticate_epiphany_packaged_release (
store : & Path ,
runtime_id : & str ,
release_id : & str ,
expected_witness_sha256 : & str ,
) -> Result < EpiphanyPackagedReleaseEntry > {
2026-08-09 08:27:27 +01:00
validate_epiphany_packaged_release_sha256 ( expected_witness_sha256 ) ? ;
2026-07-16 03:46:31 +01:00
let entry = load_epiphany_packaged_release ( store , runtime_id , release_id ) ?
. context ( " packaged release witness is absent " ) ? ;
validate_epiphany_packaged_release ( & entry ) ? ;
verify_epiphany_packaged_release_files ( & entry ) ? ;
2026-08-09 08:27:27 +01:00
if epiphany_packaged_release_witness_sha256 ( & entry ) ? ! = expected_witness_sha256 {
2026-07-16 03:46:31 +01:00
bail! ( " packaged release witness digest disagrees with pinned task authority " ) ;
}
Ok ( entry )
}