From 7b385b04454c22c63bb7e9aa8b0f8a59618af1dc Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sun, 16 Aug 2026 20:22:34 +0200 Subject: [PATCH] Give Atlas publisher a durable identity lifecycle --- epiphany-core/src/atlas/runtime.rs | 41 +++++++++++++++++-- .../src/bin/epiphany-atlas-publisher.rs | 1 + notes/epiphany-current-algorithmic-map.md | 9 ++++ notes/fresh-workspace-handoff.md | 9 ++++ 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/epiphany-core/src/atlas/runtime.rs b/epiphany-core/src/atlas/runtime.rs index 0a8cfcea..d3dd2014 100644 --- a/epiphany-core/src/atlas/runtime.rs +++ b/epiphany-core/src/atlas/runtime.rs @@ -7,7 +7,10 @@ use cultcache_rs::{ CacheBackingStore, CultCache, CultCacheEnvelope, DatabaseEntry, SingleFileMessagePackBackingStore, }; -use cultnet_rs::{ServiceIdentityProfile, ServiceIdentityTrustAnchor, enroll_service_identity_at}; +use cultnet_rs::{ + ServiceIdentityProfile, ServiceIdentitySigner, ServiceIdentityTrustAnchor, + enroll_service_identity_at, export_service_identity_trust_anchor, open_service_identity_at, +}; use super::*; @@ -39,6 +42,7 @@ pub struct AtlasPublisherRunConfig { pub publisher_store: PathBuf, pub publisher_cultmesh_store: PathBuf, pub identity_store: PathBuf, + pub trust_anchor_store: PathBuf, pub odin_endpoint: SocketAddr, pub now_unix_ms: u64, } @@ -248,6 +252,7 @@ pub fn run_atlas_publisher_once( &config.publisher_store, &config.publisher_cultmesh_store, &config.identity_store, + &config.trust_anchor_store, ])?; if let Some(brake_id) = atlas_brake_id( &config.local_verse_store, @@ -263,8 +268,8 @@ pub fn run_atlas_publisher_once( { bail!("Atlas publisher repository/runtime differs from its current Body basis") } - let signer = - enroll_service_identity_at::(&config.identity_store)?; + let signer = open_or_enroll_atlas_repository_identity(&config.identity_store)?; + export_service_identity_trust_anchor(&signer, &config.trust_anchor_store)?; let store = AtlasCultCacheStore::new(&config.publisher_store, config.repository.clone())?; ensure_local_publisher_trust(&store, &signer, config.now_unix_ms)?; let heartbeat_sequence = next_heartbeat_sequence(&store)?; @@ -319,6 +324,16 @@ pub fn run_atlas_publisher_once( }) } +fn open_or_enroll_atlas_repository_identity( + identity_store: &Path, +) -> Result> { + if identity_store.exists() { + open_service_identity_at::(identity_store) + } else { + enroll_service_identity_at::(identity_store) + } +} + pub fn run_atlas_projector_once( config: &AtlasProjectorRunConfig, ) -> Result { @@ -1406,4 +1421,24 @@ mod tests { assert!(stale_source.load_local_atlas_mind_snapshot().is_err()); Ok(()) } + + #[test] + fn publisher_identity_reopens_and_exports_one_stable_public_anchor() -> Result<()> { + let temp = tempfile::tempdir()?; + let identity_store = temp.path().join("publisher-identity.cc"); + let trust_anchor_store = temp.path().join("publisher-trust-anchor.cc"); + + let enrolled = open_or_enroll_atlas_repository_identity(&identity_store)?; + let first_anchor = export_service_identity_trust_anchor(&enrolled, &trust_anchor_store)?; + let reopened = open_or_enroll_atlas_repository_identity(&identity_store)?; + let replayed_anchor = export_service_identity_trust_anchor(&reopened, &trust_anchor_store)?; + + assert_eq!(enrolled.entry(), reopened.entry()); + assert_eq!(first_anchor, replayed_anchor); + assert_eq!( + read_atlas_repository_trust_anchor(&trust_anchor_store)?, + first_anchor + ); + Ok(()) + } } diff --git a/epiphany-core/src/bin/epiphany-atlas-publisher.rs b/epiphany-core/src/bin/epiphany-atlas-publisher.rs index 93eaf5ed..8fff7a26 100644 --- a/epiphany-core/src/bin/epiphany-atlas-publisher.rs +++ b/epiphany-core/src/bin/epiphany-atlas-publisher.rs @@ -29,6 +29,7 @@ fn main() -> Result<()> { publisher_store: path(&args, "--publisher-store")?, publisher_cultmesh_store: path(&args, "--cultmesh-store")?, identity_store: path(&args, "--identity-store")?, + trust_anchor_store: path(&args, "--trust-anchor-store")?, odin_endpoint: required(&args, "--odin-endpoint")?.parse()?, now_unix_ms: now_unix_ms()?, }; diff --git a/notes/epiphany-current-algorithmic-map.md b/notes/epiphany-current-algorithmic-map.md index ab54f157..3ff42bd1 100644 --- a/notes/epiphany-current-algorithmic-map.md +++ b/notes/epiphany-current-algorithmic-map.md @@ -4753,3 +4753,12 @@ presentation-only; domain mutation routes back to the owning repository's Modeling interface. Brake scopes `atlas.publish`, `atlas.project`, and `atlas.impact_ingress` freeze writes and scheduling while preserving the last projection as visibly stale read-only state. + +The repository publisher also owns the lifecycle of its signing identity. +Its private identity store is enrolled only when absent and reopened exactly on +later cycles; retries cannot rotate or replace it. Every unbraked cycle exports +the same public trust anchor idempotently to a physically separate store for +operator-gated projector pinning. The projector never reads the private store, +and an existing divergent public anchor refuses instead of being repaired. +This cut is required for both the second publisher heartbeat and real trust +enrollment; one-shot identity creation is not a viable daemon lifecycle. diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index 4c8e1c70..7c8e65fb 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -47,6 +47,15 @@ publishers/projector establish signed sight; then engage all three Atlas brakes and prove later organ cycles are held while the last Eve projection remains visibly stale/read-only and no lane wakes. +Gate 1 preflight exposed and cut one publisher lifecycle defect before live +evidence was admitted. The publisher now enrolls its private signing identity +only when absent, reopens that exact identity on later heartbeats, and exports +one idempotent public trust anchor to a separate operator-readable store. This +keeps identity rotation impossible by retry and gives the projector a real +public anchor to pin without reading private state. The focused identity +reopen/export test passes; the operational gate remains in progress and is not +yet accepted. + Public positioning was synchronized on 2026-08-16 without changing that next rite. `docs/positioning.md` is the current claim owner: Epiphany is the organizational layer above frontier AI workers; the primary failure is global