Retire unadmitted Model Atlas daemons
This commit is contained in:
parent
57cfd762e7
commit
56267201b7
6 changed files with 24 additions and 352 deletions
12
Cargo.toml
12
Cargo.toml
|
|
@ -77,18 +77,6 @@ tungstenite = { git = "https://github.com/openai-oss-forks/tungstenite-rs", rev
|
|||
[patch."ssh://git@github.com/openai-oss-forks/tungstenite-rs.git"]
|
||||
tungstenite = { git = "https://github.com/openai-oss-forks/tungstenite-rs", rev = "9200079d3b54a1ff51072e24d81fd354f085156f" }
|
||||
|
||||
[[bin]]
|
||||
name = "epiphany-atlas-publisher"
|
||||
path = "epiphany-core/src/bin/epiphany-atlas-publisher.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "epiphany-model-entanglement-projector"
|
||||
path = "epiphany-core/src/bin/epiphany-model-entanglement-projector.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "epiphany-atlas-impact-ingress"
|
||||
path = "epiphany-core/src/bin/epiphany-atlas-impact-ingress.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "epiphany-release"
|
||||
path = "epiphany-core/src/bin/epiphany-release.rs"
|
||||
|
|
|
|||
|
|
@ -1,100 +0,0 @@
|
|||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
|
||||
use anyhow::{Result, bail};
|
||||
use epiphany_core::{
|
||||
AtlasImpactIngressRunConfig, AtlasImpactIngressRunOutcome, AtlasRepositoryIdentity,
|
||||
run_atlas_impact_ingress_once,
|
||||
};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let (args, switches) = arguments()?;
|
||||
let once = switches.contains("--once");
|
||||
let running = Arc::new(AtomicBool::new(true));
|
||||
let stop = running.clone();
|
||||
ctrlc::set_handler(move || stop.store(false, Ordering::SeqCst))?;
|
||||
while running.load(Ordering::SeqCst) {
|
||||
let config = AtlasImpactIngressRunConfig {
|
||||
local_repository: AtlasRepositoryIdentity::new(
|
||||
required(&args, "--swarm-id")?,
|
||||
required(&args, "--workspace-id")?,
|
||||
)?,
|
||||
projector_repository: AtlasRepositoryIdentity::new(
|
||||
required(&args, "--projector-swarm-id")?,
|
||||
required(&args, "--projector-workspace-id")?,
|
||||
)?,
|
||||
runtime_id: required(&args, "--runtime-id")?.into(),
|
||||
runtime_mind_store: path(&args, "--mind-store")?,
|
||||
local_verse_store: path(&args, "--verse-store")?,
|
||||
projector_store: path(&args, "--projector-store")?,
|
||||
impact_store: path(&args, "--impact-store")?,
|
||||
resident_self_store: path(&args, "--resident-self-store")?,
|
||||
cooldown_after_completion_ms: args
|
||||
.get("--cooldown-ms")
|
||||
.map(|value| value.parse())
|
||||
.transpose()?
|
||||
.unwrap_or(30_000),
|
||||
now_unix_ms: now_unix_ms()?,
|
||||
};
|
||||
match run_atlas_impact_ingress_once(&config)? {
|
||||
AtlasImpactIngressRunOutcome::HeldByBrake {
|
||||
brake_id,
|
||||
last_projection,
|
||||
} => println!(
|
||||
"atlas impact ingress held by brake {brake_id}; last projection remains read-only: {}",
|
||||
last_projection.is_some()
|
||||
),
|
||||
AtlasImpactIngressRunOutcome::Evaluated {
|
||||
admitted_impacts,
|
||||
scheduled_pressures,
|
||||
held_impacts,
|
||||
} => println!(
|
||||
"atlas impact ingress admitted {admitted_impacts}, scheduled {scheduled_pressures}, held {held_impacts}"
|
||||
),
|
||||
}
|
||||
if once {
|
||||
break;
|
||||
}
|
||||
std::thread::sleep(Duration::from_secs(5));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn arguments() -> Result<(BTreeMap<String, String>, BTreeSet<String>)> {
|
||||
let mut values = BTreeMap::new();
|
||||
let mut switches = BTreeSet::new();
|
||||
let mut args = std::env::args().skip(1);
|
||||
while let Some(name) = args.next() {
|
||||
if name == "--once" {
|
||||
switches.insert(name);
|
||||
continue;
|
||||
}
|
||||
if !name.starts_with("--") {
|
||||
bail!("unexpected Atlas impact argument {name}")
|
||||
}
|
||||
values.insert(
|
||||
name.clone(),
|
||||
args.next()
|
||||
.ok_or_else(|| anyhow::anyhow!("{name} requires a value"))?,
|
||||
);
|
||||
}
|
||||
Ok((values, switches))
|
||||
}
|
||||
|
||||
fn required<'a>(args: &'a BTreeMap<String, String>, name: &str) -> Result<&'a str> {
|
||||
args.get(name)
|
||||
.map(String::as_str)
|
||||
.ok_or_else(|| anyhow::anyhow!("missing required {name}"))
|
||||
}
|
||||
fn path(args: &BTreeMap<String, String>, name: &str) -> Result<PathBuf> {
|
||||
Ok(PathBuf::from(required(args, name)?))
|
||||
}
|
||||
fn now_unix_ms() -> Result<u64> {
|
||||
Ok(SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)?
|
||||
.as_millis()
|
||||
.try_into()?)
|
||||
}
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
|
||||
use anyhow::{Result, bail};
|
||||
use epiphany_core::{
|
||||
ATLAS_PUBLISHER_HEARTBEAT_INTERVAL_MS, AtlasPublisherRunConfig, AtlasPublisherRunOutcome,
|
||||
AtlasRepositoryIdentity, run_atlas_publisher_once,
|
||||
};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let (args, switches) = arguments()?;
|
||||
let once = switches.contains("--once");
|
||||
let running = Arc::new(AtomicBool::new(true));
|
||||
let stop = running.clone();
|
||||
ctrlc::set_handler(move || stop.store(false, Ordering::SeqCst))?;
|
||||
while running.load(Ordering::SeqCst) {
|
||||
let config = AtlasPublisherRunConfig {
|
||||
repository: AtlasRepositoryIdentity::new(
|
||||
required(&args, "--swarm-id")?,
|
||||
required(&args, "--workspace-id")?,
|
||||
)?,
|
||||
runtime_id: required(&args, "--runtime-id")?.into(),
|
||||
runtime_incarnation_id: required(&args, "--runtime-incarnation-id")?.into(),
|
||||
runtime_mind_store: path(&args, "--mind-store")?,
|
||||
local_verse_store: path(&args, "--verse-store")?,
|
||||
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()?,
|
||||
};
|
||||
match run_atlas_publisher_once(&config)? {
|
||||
AtlasPublisherRunOutcome::HeldByBrake { brake_id } => {
|
||||
println!("atlas publisher held by brake {brake_id}")
|
||||
}
|
||||
AtlasPublisherRunOutcome::Published {
|
||||
new_publications,
|
||||
transported_publications,
|
||||
} => println!(
|
||||
"atlas publisher committed {new_publications} new records and transported {transported_publications} immutable records"
|
||||
),
|
||||
}
|
||||
if once {
|
||||
break;
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(ATLAS_PUBLISHER_HEARTBEAT_INTERVAL_MS));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn arguments() -> Result<(BTreeMap<String, String>, BTreeSet<String>)> {
|
||||
let mut values = BTreeMap::new();
|
||||
let mut switches = BTreeSet::new();
|
||||
let mut args = std::env::args().skip(1);
|
||||
while let Some(name) = args.next() {
|
||||
if name == "--once" {
|
||||
switches.insert(name);
|
||||
continue;
|
||||
}
|
||||
if !name.starts_with("--") {
|
||||
bail!("unexpected Atlas publisher argument {name}")
|
||||
}
|
||||
values.insert(
|
||||
name.clone(),
|
||||
args.next()
|
||||
.ok_or_else(|| anyhow::anyhow!("{name} requires a value"))?,
|
||||
);
|
||||
}
|
||||
Ok((values, switches))
|
||||
}
|
||||
|
||||
fn required<'a>(args: &'a BTreeMap<String, String>, name: &str) -> Result<&'a str> {
|
||||
args.get(name)
|
||||
.map(String::as_str)
|
||||
.ok_or_else(|| anyhow::anyhow!("missing required {name}"))
|
||||
}
|
||||
|
||||
fn path(args: &BTreeMap<String, String>, name: &str) -> Result<PathBuf> {
|
||||
Ok(PathBuf::from(required(args, name)?))
|
||||
}
|
||||
|
||||
fn now_unix_ms() -> Result<u64> {
|
||||
Ok(SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)?
|
||||
.as_millis()
|
||||
.try_into()?)
|
||||
}
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
|
||||
use anyhow::{Result, bail};
|
||||
use epiphany_core::{
|
||||
AtlasProjectorRunConfig, AtlasProjectorRunOutcome, AtlasRepositoryIdentity,
|
||||
AtlasStoreWriteOutcome, pin_atlas_publisher_trust_anchor, run_atlas_projector_once,
|
||||
};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let (args, switches, command) = arguments()?;
|
||||
let projector = AtlasRepositoryIdentity::new(
|
||||
required(&args, "--swarm-id")?,
|
||||
required(&args, "--workspace-id")?,
|
||||
)?;
|
||||
if command == "pin-trust" {
|
||||
let publisher = AtlasRepositoryIdentity::new(
|
||||
required(&args, "--publisher-swarm-id")?,
|
||||
required(&args, "--publisher-workspace-id")?,
|
||||
)?;
|
||||
let expected_revision = args
|
||||
.get("--expected-revision")
|
||||
.map(|value| value.parse())
|
||||
.transpose()?;
|
||||
let outcome = pin_atlas_publisher_trust_anchor(
|
||||
&path(&args, "--projector-store")?,
|
||||
projector,
|
||||
publisher,
|
||||
&path(&args, "--trust-anchor")?,
|
||||
expected_revision,
|
||||
now_unix_ms()?,
|
||||
)?;
|
||||
if outcome == AtlasStoreWriteOutcome::Conflict {
|
||||
bail!("Atlas trust pin CAS conflicted")
|
||||
}
|
||||
println!("Atlas publisher trust pin admitted: {outcome:?}");
|
||||
return Ok(());
|
||||
}
|
||||
if command != "run" {
|
||||
bail!("Atlas projector command must be `run` or `pin-trust`")
|
||||
}
|
||||
let once = switches.contains("--once");
|
||||
let running = Arc::new(AtomicBool::new(true));
|
||||
let stop = running.clone();
|
||||
ctrlc::set_handler(move || stop.store(false, Ordering::SeqCst))?;
|
||||
while running.load(Ordering::SeqCst) {
|
||||
let config = AtlasProjectorRunConfig {
|
||||
projector_repository: projector.clone(),
|
||||
runtime_id: required(&args, "--runtime-id")?.into(),
|
||||
local_verse_store: path(&args, "--verse-store")?,
|
||||
projector_store: path(&args, "--projector-store")?,
|
||||
projector_cultmesh_store: path(&args, "--cultmesh-store")?,
|
||||
odin_endpoint: required(&args, "--odin-endpoint")?.parse()?,
|
||||
now_unix_ms: now_unix_ms()?,
|
||||
};
|
||||
match run_atlas_projector_once(&config)? {
|
||||
AtlasProjectorRunOutcome::HeldByBrake {
|
||||
brake_id,
|
||||
last_projection,
|
||||
} => println!(
|
||||
"atlas projector held by brake {brake_id}; last projection remains read-only: {}",
|
||||
last_projection.is_some()
|
||||
),
|
||||
AtlasProjectorRunOutcome::Projected {
|
||||
projection,
|
||||
accepted_publications,
|
||||
rejected_publications,
|
||||
} => println!(
|
||||
"atlas projector {} accepted {accepted_publications}, rejected {rejected_publications}, derived {} edges and {} cycles",
|
||||
projection.projection_sha256,
|
||||
projection.entanglements.len(),
|
||||
projection.cycles.len()
|
||||
),
|
||||
}
|
||||
if once {
|
||||
break;
|
||||
}
|
||||
std::thread::sleep(Duration::from_secs(5));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn arguments() -> Result<(BTreeMap<String, String>, BTreeSet<String>, String)> {
|
||||
let mut raw = std::env::args().skip(1);
|
||||
let command = raw.next().unwrap_or_else(|| "run".into());
|
||||
let mut values = BTreeMap::new();
|
||||
let mut switches = BTreeSet::new();
|
||||
while let Some(name) = raw.next() {
|
||||
if name == "--once" {
|
||||
switches.insert(name);
|
||||
continue;
|
||||
}
|
||||
if !name.starts_with("--") {
|
||||
bail!("unexpected Atlas projector argument {name}")
|
||||
}
|
||||
values.insert(
|
||||
name.clone(),
|
||||
raw.next()
|
||||
.ok_or_else(|| anyhow::anyhow!("{name} requires a value"))?,
|
||||
);
|
||||
}
|
||||
Ok((values, switches, command))
|
||||
}
|
||||
|
||||
fn required<'a>(args: &'a BTreeMap<String, String>, name: &str) -> Result<&'a str> {
|
||||
args.get(name)
|
||||
.map(String::as_str)
|
||||
.ok_or_else(|| anyhow::anyhow!("missing required {name}"))
|
||||
}
|
||||
fn path(args: &BTreeMap<String, String>, name: &str) -> Result<PathBuf> {
|
||||
Ok(PathBuf::from(required(args, name)?))
|
||||
}
|
||||
fn now_unix_ms() -> Result<u64> {
|
||||
Ok(SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)?
|
||||
.as_millis()
|
||||
.try_into()?)
|
||||
}
|
||||
|
|
@ -84,15 +84,6 @@ pub fn required_packaged_release_binaries(target_triple: &str) -> Vec<(&'static
|
|||
vec![
|
||||
("release-publisher", file_name("epiphany-release")),
|
||||
("state-steward", file_name("epiphany-state")),
|
||||
("atlas-publisher", file_name("epiphany-atlas-publisher")),
|
||||
(
|
||||
"model-entanglement-projector",
|
||||
file_name("epiphany-model-entanglement-projector"),
|
||||
),
|
||||
(
|
||||
"atlas-impact-ingress",
|
||||
file_name("epiphany-atlas-impact-ingress"),
|
||||
),
|
||||
("repository-body", file_name("epiphany-repository-body")),
|
||||
("host-identity", file_name("epiphany-host-identity")),
|
||||
("swarm", file_name("epiphany-swarm")),
|
||||
|
|
|
|||
|
|
@ -111,21 +111,25 @@ not merely that the projector drew one.
|
|||
- `atlas.publish`, `atlas.project`, and `atlas.impact_ingress` brakes stop writes
|
||||
and scheduling while retaining the last projection read-only.
|
||||
|
||||
## Live organs
|
||||
## Library organs; deployment not admitted
|
||||
|
||||
- `epiphany-atlas-publisher` reads a local Mind store, publishes immutable
|
||||
events to its publisher CultCache/CultMesh store, and transports them to
|
||||
Odin. Its signing identity lives in a separate private `.cc` store.
|
||||
- `epiphany-model-entanglement-projector` pins repository trust anchors, queries
|
||||
Odin, verifies publications, derives a deterministic projection, and
|
||||
publishes the canonical Eve provider and surface documents.
|
||||
- `epiphany-atlas-impact-ingress` compares a projection with locally owned
|
||||
claims, admits exact impact documents, and asks local Resident Self to
|
||||
schedule permitted pressure.
|
||||
- The publisher library reads a local Mind store, publishes immutable events to
|
||||
its publisher CultCache/CultMesh store, and transports them to Odin. Its
|
||||
signing identity lives in a separate private `.cc` store.
|
||||
- The projector library pins repository trust anchors, queries Odin, verifies
|
||||
publications, derives a deterministic projection, and publishes canonical
|
||||
Eve provider and surface documents.
|
||||
- The impact-ingress library compares a projection with locally owned claims,
|
||||
admits exact impact documents, and asks local Resident Self to schedule
|
||||
permitted pressure.
|
||||
|
||||
Each organ has its own store. The local Mind, private identity, publisher
|
||||
events, CultMesh publication, Odin catalog, projection, and impact scheduler
|
||||
state are never collapsed into a writable mega-graph.
|
||||
These are typed library owners, not three production daemons. The former
|
||||
command wrappers had no unit, live consumer, deployment phase, or independent
|
||||
failure-isolation contract and are not shipped. If the operational gate is
|
||||
adopted, its process topology must be designed from the admitted lifecycle
|
||||
rather than resurrected from those wrappers. The local Mind, private identity,
|
||||
publisher events, CultMesh publication, Odin catalog, projection, and impact
|
||||
scheduler state must not collapse into a writable mega-graph.
|
||||
|
||||
## Starfire pilot topology
|
||||
|
||||
|
|
@ -148,7 +152,7 @@ pinned by exact repository coordinates; key rotation is operator-gated.
|
|||
## Rollout gates
|
||||
|
||||
1. Keep impact ingress absent. Let ordinary Modeling admit the three local seed
|
||||
facts into isolated Mind stores, then run the publishers and projector until
|
||||
facts into isolated Mind stores, then exercise the publisher and projector owners until
|
||||
signed publications, exact watermarks, and the retained Eve tree exist.
|
||||
Engage `atlas.publish`, `atlas.project`, and `atlas.impact_ingress`, run one
|
||||
more publisher/projector cycle plus the first ingress cycle, and prove every
|
||||
|
|
@ -158,9 +162,10 @@ pinned by exact repository coordinates; key rotation is operator-gated.
|
|||
2. Release Atlas scopes for Modeling and Soul while leaving existing Hands
|
||||
authorization unchanged. A temporary Eve contract change must wake Odin
|
||||
Modeling and then Epiphany Modeling without an operator prompt.
|
||||
3. Place the organs under the existing daemon supervisor. Sustain the pilot
|
||||
while observing publisher/projector lag, per-publisher watermarks,
|
||||
rejection count, cycles, and pending impacts through CultMesh/Eve.
|
||||
3. Admit only the minimum Idunn-managed process topology justified by the
|
||||
proven lifecycle and failure-isolation requirements. Sustain the pilot while
|
||||
observing publisher/projector lag, per-publisher watermarks, rejection
|
||||
count, cycles, and pending impacts through CultMesh/Eve.
|
||||
4. Only after sustained local proof, register topology and recovery runbooks in
|
||||
`gamecult-ops`. Yggdrasil and the public Verse remain outside V1.
|
||||
|
||||
|
|
|
|||
Reference in a new issue