Keep Idunn RUDP ingress alive for Mimir health

This commit is contained in:
Meta Vens 2026-06-16 08:05:42 +01:00
commit 7386d84495
2 changed files with 180 additions and 44 deletions

View file

@ -372,10 +372,10 @@ fn is_fresh_daemon_published_health(
if health.transport != CULTNET_RUDP_PROTOCOL_ID {
return false;
}
let Some(now_seconds) = parse_unix_timestamp(now) else {
let Some(now_seconds) = parse_timestamp_seconds(now) else {
return false;
};
let Some(observed_seconds) = parse_unix_timestamp(&health.observed_at) else {
let Some(observed_seconds) = parse_timestamp_seconds(&health.observed_at) else {
return false;
};
if observed_seconds > now_seconds {
@ -388,6 +388,61 @@ fn parse_unix_timestamp(value: &str) -> Option<u64> {
value.strip_prefix("unix:")?.parse().ok()
}
fn parse_timestamp_seconds(value: &str) -> Option<u64> {
parse_unix_timestamp(value).or_else(|| parse_utc_iso_timestamp_seconds(value))
}
fn parse_utc_iso_timestamp_seconds(value: &str) -> Option<u64> {
if value.len() < 19 {
return None;
}
if !value.ends_with('Z') && !value.contains("+00:00") {
return None;
}
let year = value.get(0..4)?.parse::<i32>().ok()?;
let month = value.get(5..7)?.parse::<u32>().ok()?;
let day = value.get(8..10)?.parse::<u32>().ok()?;
let hour = value.get(11..13)?.parse::<u32>().ok()?;
let minute = value.get(14..16)?.parse::<u32>().ok()?;
let second = value.get(17..19)?.parse::<u32>().ok()?;
if value.as_bytes().get(4) != Some(&b'-')
|| value.as_bytes().get(7) != Some(&b'-')
|| value.as_bytes().get(10) != Some(&b'T')
|| value.as_bytes().get(13) != Some(&b':')
|| value.as_bytes().get(16) != Some(&b':')
|| !(1..=12).contains(&month)
|| !(1..=31).contains(&day)
|| hour > 23
|| minute > 59
|| second > 60
{
return None;
}
let days = days_from_civil(year, month, day)?;
let seconds = days
.checked_mul(86_400)?
.checked_add(i64::from(hour) * 3_600)?
.checked_add(i64::from(minute) * 60)?
.checked_add(i64::from(second))?;
u64::try_from(seconds).ok()
}
fn days_from_civil(year: i32, month: u32, day: u32) -> Option<i64> {
let year = i64::from(year) - if month <= 2 { 1 } else { 0 };
let era = if year >= 0 { year } else { year - 399 } / 400;
let year_of_era = year - era * 400;
let month = i64::from(month);
let day = i64::from(day);
let day_of_year = (153 * (month + if month > 2 { -3 } else { 9 }) + 2) / 5 + day - 1;
if !(0..=365).contains(&day_of_year) {
return None;
}
let day_of_era = year_of_era * 365 + year_of_era / 4 - year_of_era / 100 + day_of_year;
Some(era * 146_097 + day_of_era - 719_468)
}
fn publish_runtime_transport_check(
options: &CommonOptions,
store_lock: &Arc<Mutex<()>>,
@ -580,53 +635,67 @@ fn run_rudp_health_ingress_loop(
match handle_rudp_health_datagram(&socket, &mut sessions, &buffer[..size], source) {
Ok(frames) => {
for frame in frames {
let message = decode_cultnet_message_from_slice(
let message = match decode_cultnet_message_from_slice(
&frame.payload,
CultNetWireContract::CultNetSchemaV0,
)?;
) {
Ok(message) => message,
Err(error) => {
publish_rudp_ingress_failure(
&options,
&store_lock,
local_addr,
&format!(
"rejected RUDP health schema bytes from {source}: {error}"
),
&observed_at,
);
continue;
}
};
match health_from_rudp_message(&message) {
Ok(mut health) => {
health.publication_source = "daemon-published".to_string();
health.transport = CULTNET_RUDP_PROTOCOL_ID.to_string();
with_store_node(&options, &store_lock, |node| {
node.put(&health.daemon_id, &health)?;
Ok(())
})?;
println!(
"Idunn accepted RUDP health for {} from {} over {}.",
health.daemon_id, source, frame.channel_id
);
if let Err(error) =
with_store_node(&options, &store_lock, |node| {
node.put(&health.daemon_id, &health)?;
Ok(())
})
{
eprintln!(
"Idunn RUDP health ingress failed to persist {} from {}: {}",
health.daemon_id, source, error
);
} else {
println!(
"Idunn accepted RUDP health for {} from {} over {}.",
health.daemon_id, source, frame.channel_id
);
}
}
Err(error) => {
let failed = rudp_health_ingress_record(
"idunn-rudp-health-ingress",
publish_rudp_ingress_failure(
&options,
&store_lock,
local_addr,
"degraded",
&format!(
"rejected RUDP health schema frame from {source}: {error}"
),
&observed_at,
);
with_store_node(&options, &store_lock, |node| {
node.put(&failed.ingress_id, &failed)?;
Ok(())
})?;
}
}
}
}
Err(error) => {
let failed = rudp_health_ingress_record(
"idunn-rudp-health-ingress",
publish_rudp_ingress_failure(
&options,
&store_lock,
local_addr,
"degraded",
&format!("rejected RUDP health datagram from {source}: {error}"),
&observed_at,
);
with_store_node(&options, &store_lock, |node| {
node.put(&failed.ingress_id, &failed)?;
Ok(())
})?;
}
}
}
@ -643,6 +712,28 @@ fn run_rudp_health_ingress_loop(
}
}
fn publish_rudp_ingress_failure(
options: &CommonOptions,
store_lock: &Arc<Mutex<()>>,
local_addr: SocketAddr,
detail: &str,
observed_at: &str,
) {
let failed = rudp_health_ingress_record(
"idunn-rudp-health-ingress",
local_addr,
"degraded",
detail,
observed_at,
);
if let Err(error) = with_store_node(options, store_lock, |node| {
node.put(&failed.ingress_id, &failed)?;
Ok(())
}) {
eprintln!("Idunn RUDP health ingress failed to persist ingress failure: {error}");
}
}
fn handle_rudp_health_datagram(
socket: &UdpSocket,
sessions: &mut HashMap<SocketAddr, CultNetRudpSession>,
@ -748,11 +839,23 @@ fn swarm_surgery_plan(
targets: &[DaemonTarget],
updated_at: &str,
) -> IdunnSwarmSurgeryPlanRecord {
let has_mimir_eve_dashboard = targets
.iter()
.any(|target| target.daemon_id == "mimir-eve-dashboard");
let next_target = if has_mimir_eve_dashboard {
"mimir-eve-dashboard"
let next_target = [
"nightwing-eve-dashboard",
"nightwing-eve-browser-reference",
"vili",
"yggdrasil-streampixels",
"yggdrasil-repixelizer",
"yggdrasil-heimdall",
]
.iter()
.copied()
.find(|candidate| {
targets
.iter()
.any(|target| target.enabled && target.daemon_id == *candidate)
});
let next_target = if let Some(target) = next_target {
target
} else if let Some(target) = targets.iter().find(|target| target.enabled) {
target.daemon_id.as_str()
} else {
@ -786,11 +889,11 @@ fn swarm_surgery_plan(
"5. Delete or demote compatibility probes once every target has daemon-owned publication and advertised lifecycle authority.".to_string(),
],
current_phase:
"Phase 10: move Mimir Eve dashboard health from local compatibility probes to daemon-published RUDP state, with Raven Muninn scheduled-task action repair queued as a background-only ops invariant."
"Phase 11: move Nightwing Eve runtime surfaces from compatibility service probes to daemon-published RUDP state, with Raven Muninn scheduled-task action repair queued as a background-only ops invariant."
.to_string(),
next_target: next_target.to_string(),
cut_line:
"Muninn, Idunn, Odin, Stonks, Weksa, VoidBot, and Nightwing Gjallar now exercise daemon-owned RUDP health. Mimir Eve dashboard is the next local runtime cut. Live Raven still needs GameCult-Muninn, GameCult-Muninn-Activate, and GameCult-Muninn-VideoProof task actions repaired to execute wscript.exe hidden launchers directly; application is blocked while Raven SSH is unreachable."
"Muninn, Idunn, Odin, Stonks, Weksa, VoidBot, Nightwing Gjallar, and Mimir Eve dashboard now exercise daemon-owned RUDP health. Nightwing Eve runtime surfaces are the next local runtime cut. Live Raven still needs GameCult-Muninn, GameCult-Muninn-Activate, and GameCult-Muninn-VideoProof task actions repaired to execute wscript.exe hidden launchers directly; application is blocked while Raven SSH is unreachable."
.to_string(),
verification_layer:
"CultMesh keepalive store records plus live Idunn decision cycles, not process exit codes or chat summaries."
@ -986,14 +1089,25 @@ fn daemon_surgery_plan(target: &DaemonTarget, updated_at: &str) -> IdunnDaemonSu
];
}
"mimir-eve-dashboard" => {
status = "partial-rudp-health-live";
severity = "high";
owner = "Mimir dashboard runtime";
current_mechanism =
"Mimir dashboard health is currently a local compatibility probe with no Idunn restart authority."
"Mimir Eve dashboard publishes mimir.cultnet-rudp-provider-health over CultNet/RUDP from the Nightwing systemd broker; provider advertisement and command boundary still retain compatibility projections."
.to_string();
blockers.push(
"No restart/deploy command authority is currently advertised to Idunn.".to_string(),
);
intended_authority =
"Mimir dashboard publishes daemon health, provider catalog, Eve dashboard state, command boundary, and transport profile as typed CultMesh/CultNet records over cultnet.transport.rudp.v0; HTTP/WebSocket remain lowerings for clients only."
.to_string();
cut_line =
"Keep the local HTTP probe as fallback only until Mimir provider advertisement and command_boundary records are also daemon-owned RUDP publications."
.to_string();
steps = vec![
"Keep live mimir.cultnet-rudp-provider-health publication running from the Nightwing Eve dashboard broker.".to_string(),
"Publish Mimir Eve dashboard provider advertisement and retained state records over cultnet.transport.rudp.v0.".to_string(),
"Publish Mimir Eve dashboard command_boundary and transport_profile records from the daemon runtime.".to_string(),
"Teach Odin to prefer Mimir RUDP/CultMesh provider records over compatibility HTTP catalog ingestion.".to_string(),
"Delete or demote health-mimir-eve-dashboard.cmd to a manual compatibility probe with no lifecycle truth.".to_string(),
];
}
"weksa" => {
status = "partial-rudp-health-live";
@ -2016,12 +2130,27 @@ mod tests {
enabled: true,
interval_seconds: 30,
};
let nightwing_eve_dashboard = DaemonTarget {
daemon_id: "nightwing-eve-dashboard".to_string(),
verse_id: "nightwing.local".to_string(),
name: "Nightwing Eve dashboard".to_string(),
health_contract: health_contract(
"nightwing.cultnet-rudp-eve-dashboard-health",
"failed",
),
health_command: Some("health-nightwing-eve-dashboard.cmd".to_string()),
deploy_command: None,
restart_command: Some("restart-nightwing-eve-dashboard.cmd".to_string()),
enabled: true,
interval_seconds: 30,
};
let plan = swarm_surgery_plan(
"starfire-local",
&[
odin,
mimir,
nightwing_eve_dashboard,
stonks,
weksa.clone(),
voidbot.clone(),
@ -2035,11 +2164,11 @@ mod tests {
assert_eq!(plan.plan_id, "swarm-surgery:starfire-local");
assert_eq!(plan.status, "active-transport-migration");
assert_eq!(plan.next_target, "mimir-eve-dashboard");
assert!(plan.current_phase.contains("Mimir Eve dashboard"));
assert_eq!(plan.next_target, "nightwing-eve-dashboard");
assert!(plan.current_phase.contains("Nightwing Eve"));
assert!(
plan.cut_line
.contains("Muninn, Idunn, Odin, Stonks, Weksa, VoidBot, and Nightwing Gjallar")
.contains("Muninn, Idunn, Odin, Stonks, Weksa, VoidBot, Nightwing Gjallar, and Mimir Eve dashboard")
);
assert!(plan.cut_line.contains("GameCult-Muninn-Activate"));
assert!(plan.cut_line.contains("GameCult-Muninn-VideoProof"));
@ -2154,6 +2283,11 @@ mod tests {
&health, &desired, "unix:100"
));
health.observed_at = "1970-01-01T00:01:35.0000000+00:00".to_string();
assert!(is_fresh_daemon_published_health(
&health, &desired, "unix:100"
));
health.observed_at = "unix:1".to_string();
assert!(!is_fresh_daemon_published_health(
&health, &desired, "unix:100"

View file

@ -279,15 +279,17 @@ serialized provider witness refresh. VoidBot publishes
`voidbot.cultnet-rudp-stack-health` after each GameCult Local Orchestrator
pulse. Gjallar publishes
`gjallar.cultnet-rudp-framebuffer-composition-health` from the C# Nightwing
framebuffer service. Live Idunn cycles accept these records before command-probe fallback.
framebuffer service. Mimir Eve dashboard publishes
`mimir.cultnet-rudp-provider-health` from the Nightwing systemd broker. Live
Idunn cycles accept these records before command-probe fallback.
Raven's Muninn scheduled-task repair is a separate ops invariant:
`GameCult-Muninn`, `GameCult-Muninn-Activate`, and
`GameCult-Muninn-VideoProof` must execute `wscript.exe` hidden launcher actions,
not raw `.cmd` task actions.
Next: move Mimir Eve dashboard health off local compatibility evidence and onto
daemon-owned RUDP state, then continue runtime-by-runtime until compatibility
probes can be deleted or demoted. Stonks, Weksa, VoidBot, and Gjallar still owe
Next: move the remaining Nightwing Eve runtime surfaces off compatibility
service probes, then continue runtime-by-runtime until compatibility probes can
be deleted or demoted. Stonks, Weksa, VoidBot, Gjallar, and Mimir still owe
provider advertisement and command-boundary RUDP publication before their
compatibility surfaces can be purely display/debug lowerings; Gjallar also owes
native CultMesh/RUDP deck input to replace the current Odin WebSocket lowering