Take runtime identity, bind, and state root from Idunn
The daemon declared its own GHOSTLIGHT_RUNTIME_ID, GHOSTLIGHT_DUNGEON_BIND, and GHOSTLIGHT_DUNGEON_ROOT. Under the managed contract Idunn owns all three: the runtime id arrives inside the activation bundle, the bind is a candidate socket Idunn hands down, and the state root is an admitted workload path. A service that also names them from its own environment is a second opinion about facts it does not own. runtime.rs already resolved the runtime id from the admission and kept GHOSTLIGHT_RUNTIME_ID only as the unmanaged fallback. This is the caller side catching up: main.rs accepts --state-root and passes it through, and the recipe declares the state_root argument binding plus the three GAMECULT_IDUNN_* variables while dropping the three the daemon no longer reads. The binding is required, not cosmetic. Idunn refuses a stateful workload with no state_root argument binding and refuses one whose binding disagrees with the admitted root, so the recipe could not have deployed without it. The acceptance harness kept its own GHOSTLIGHT_RUNTIME_ID; it is renamed to GHOSTLIGHT_ACCEPTANCE_RUNTIME_ID so a test-supplied identity cannot be mistaken for the managed one. The mesh surface test compared the published record against a freshly built surface, including updatedAtUtc, which is a live timestamp. It now asserts the structural fields, parses the timestamp, and compares surface and commands. cargo test --workspace: 89 passed, 0 failed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
7e73504819
commit
751b5c4150
4 changed files with 35 additions and 14 deletions
|
|
@ -6,7 +6,16 @@ mod mesh;
|
|||
mod runtime;
|
||||
mod world;
|
||||
|
||||
use anyhow::bail;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
runtime::run().await
|
||||
let args = std::env::args_os().skip(1).collect::<Vec<_>>();
|
||||
let state_root = match args.as_slice() {
|
||||
[flag, path] if flag == "--state-root" => Some(PathBuf::from(path)),
|
||||
[] => None,
|
||||
_ => bail!("usage: ghostlight-dungeon [--state-root PATH]"),
|
||||
};
|
||||
runtime::run(state_root).await
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
use crate::{eve, world::WorldSnapshot};
|
||||
use anyhow::Result;
|
||||
use chrono::Utc;
|
||||
use chrono::{DateTime, Utc};
|
||||
use cultcache_rs::DatabaseEntry;
|
||||
use cultmesh_rs::{
|
||||
CultMesh, CultMeshNode, CultMeshNodeOptions, CultMeshRudpDocumentPublishOptions,
|
||||
|
|
@ -384,12 +384,21 @@ mod tests {
|
|||
assert_eq!(health["surfaceVersion"], 0);
|
||||
assert_eq!(publisher.health().unwrap(), health);
|
||||
let node = publisher.node.lock().unwrap();
|
||||
assert_eq!(
|
||||
node.get_required::<EveSurfaceRecord>(SURFACE_KEY)
|
||||
.unwrap()
|
||||
.value,
|
||||
eve::mesh_surface(None)
|
||||
let surface = node
|
||||
.get_required::<EveSurfaceRecord>(SURFACE_KEY)
|
||||
.unwrap()
|
||||
.value;
|
||||
assert_eq!(surface["type"], "surface-state");
|
||||
assert_eq!(surface["schema"], "gamecult.eve.surface.v1");
|
||||
assert_eq!(surface["providerId"], PROVIDER_ID);
|
||||
assert_eq!(surface["version"], 0);
|
||||
assert!(
|
||||
surface["updatedAtUtc"]
|
||||
.as_str()
|
||||
.is_some_and(|value| value.parse::<DateTime<Utc>>().is_ok())
|
||||
);
|
||||
assert_eq!(surface["surface"], eve::mesh_surface(None)["surface"]);
|
||||
assert_eq!(surface["commands"], eve::mesh_surface(None)["commands"]);
|
||||
assert!(
|
||||
node.get_required::<EveProviderAdvertisementRecord>(ADVERTISEMENT_KEY)
|
||||
.is_ok()
|
||||
|
|
|
|||
|
|
@ -4441,8 +4441,8 @@ mod tests {
|
|||
.expect("GHOSTLIGHT_CONTROLLER_CONNECTOR must be a socket address");
|
||||
let connector_credential = std::env::var_os("GHOSTLIGHT_CONTROLLER_CREDENTIAL")
|
||||
.expect("GHOSTLIGHT_CONTROLLER_CREDENTIAL is required");
|
||||
let runtime_id =
|
||||
std::env::var("GHOSTLIGHT_RUNTIME_ID").expect("GHOSTLIGHT_RUNTIME_ID is required");
|
||||
let runtime_id = std::env::var("GHOSTLIGHT_ACCEPTANCE_RUNTIME_ID")
|
||||
.expect("GHOSTLIGHT_ACCEPTANCE_RUNTIME_ID is required");
|
||||
let models = ControllerModels {
|
||||
projector: std::env::var("GHOSTLIGHT_CONTROLLER_PROJECTOR_MODEL")
|
||||
.expect("GHOSTLIGHT_CONTROLLER_PROJECTOR_MODEL is required"),
|
||||
|
|
|
|||
|
|
@ -58,13 +58,13 @@ argv = [
|
|||
]
|
||||
required_environment = [
|
||||
"GHOSTLIGHT_ACCEPTANCE_PERSONA_PROSE_LOG",
|
||||
"GHOSTLIGHT_ACCEPTANCE_RUNTIME_ID",
|
||||
"GHOSTLIGHT_CONTROLLER_CONNECTOR",
|
||||
"GHOSTLIGHT_CONTROLLER_CREDENTIAL",
|
||||
"GHOSTLIGHT_CONTROLLER_INTERPRETER_MODEL",
|
||||
"GHOSTLIGHT_CONTROLLER_OPERATIONAL_MODEL",
|
||||
"GHOSTLIGHT_CONTROLLER_PERSONA_MODEL",
|
||||
"GHOSTLIGHT_CONTROLLER_PROJECTOR_MODEL",
|
||||
"GHOSTLIGHT_RUNTIME_ID",
|
||||
]
|
||||
|
||||
[[artifacts]]
|
||||
|
|
@ -85,10 +85,16 @@ destination = "web"
|
|||
[service]
|
||||
executable_artifact = "ghostlight-dungeon"
|
||||
required_adjacent_artifacts = ["web"]
|
||||
arguments = []
|
||||
arguments = [
|
||||
{ kind = "literal", value = "--state-root" },
|
||||
{ kind = "binding", name = "state_root" },
|
||||
]
|
||||
transport = "http"
|
||||
route_required = true
|
||||
required_environment = [
|
||||
"GAMECULT_IDUNN_CANDIDATE_BIND",
|
||||
"GAMECULT_IDUNN_PROCESS_WRITE_LEASE",
|
||||
"GAMECULT_IDUNN_RUNTIME_BUNDLE",
|
||||
"GHOSTLIGHT_CONTROLLER_CONNECTOR",
|
||||
"GHOSTLIGHT_CONTROLLER_CREDENTIAL",
|
||||
"GHOSTLIGHT_CONTROLLER_INTERPRETER_MODEL",
|
||||
|
|
@ -97,14 +103,11 @@ required_environment = [
|
|||
"GHOSTLIGHT_CONTROLLER_PROJECTOR_MODEL",
|
||||
"GHOSTLIGHT_DISCORD_GUILD_ID",
|
||||
"GHOSTLIGHT_DISCORD_ROLE_ID",
|
||||
"GHOSTLIGHT_DUNGEON_BIND",
|
||||
"GHOSTLIGHT_DUNGEON_ROOT",
|
||||
"GHOSTLIGHT_HEIMDALL_APP_SECRET_FILE",
|
||||
"GHOSTLIGHT_HEIMDALL_BASE_URL",
|
||||
"GHOSTLIGHT_LOCATED_SERVICE",
|
||||
"GHOSTLIGHT_ODIN_RUDP",
|
||||
"GHOSTLIGHT_PUBLIC_APP_URL",
|
||||
"GHOSTLIGHT_RUNTIME_ID",
|
||||
"GHOSTLIGHT_SERVICE_ID",
|
||||
"GHOSTLIGHT_SESSION_WRAPPING_KEY_FILE",
|
||||
]
|
||||
|
|
|
|||
Loading…
Reference in a new issue