Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/monetize

crates/monetize-server/tests/the_signed_holger_guest_guards_its_estate.rs

//! ★ **The signed Holger guest guards its estate on the ONE box** (lane W3, 2026-10-19). The ONE
//! box links no product (monetize-admin is `Linked::open()` plus shop-pairing), so Holger reaches
//! it only as a signed guest. Until W3 the guest had no guardian: `GuestProduct` answered no
//! `Product::guardian`, the runner skipped every Holger estate, and a main the front laid again
//! (M3's boot hold: `data=fresh serving=no`) waited forever for a `recharge_from_twin` nobody
//! queued. Now the guest declares the `guardian` lane and answers the three guardian ops
//! (`guardian.diagnose`, `guardian.plan`, `guardian.observe`) with Holger's own rules, and the
//! box's guardian tick queues the signed orders itself.
//!
//! Gunnar's rules, which Holger's guardian keeps word for word (gunnar cf02854a): a re-laid
//! main that reports itself HELD (`data=fresh serving=no`) is no finding of its own — it is the
//! box the rebuild plan (New server: fence, lay, heard, recharge, recharged, restate) laid, and
//! that plan's recharge step fills it (holger's `the_holger_estate_end_to_end_against_the_one_box`
//! runs it whole). A person may press Recharge from twin: a main that still SERVES is fenced
//! first (`fence_primary`), a held one is recharged at once. A healthy estate gets no order.
//!
//! The module is the REAL one: `HOLGER_GUEST_WASM` (the Build Thing's signed build, its `.p7s`
//! beside it, under `HOLGER_GUEST_TRUST_ROOT`), else the build in `$CARGO_TARGET_DIR`
//! (`cargo build -p holger-monetize-guest --release --target wasm32-unknown-unknown` in the holger
//! sibling), signed here under a CA of its own. It never skips.

use std::path::{Path, PathBuf};

use monetize_embed::fleet::{kind, OrderAck, Report};
use monetize_embed::signing::base64_encode;
use monetize_product::guardian::role;
use monetize_server::config::Config;
use monetize_server::registry::Registry;
use monetize_server::state::AppState;

const BUILD_CMD: &str = "cargo build -p holger-monetize-guest --release --target wasm32-unknown-unknown   (in the holger sibling)";
const ESTATE: &str = "acme";
const MAIN_AT: &str = "10.6.0.10:50051";
const TWIN_AT: &str = "10.6.0.12:50051";

fn scratch(name: &str) -> PathBuf {
    let p = std::env::temp_dir().join(format!("monetize-holger-guest-guards-{name}-{}", std::process::id()));
    let _ = std::fs::remove_dir_all(&p);
    std::fs::create_dir_all(&p).unwrap();
    p
}

/// The module beside the config, signed, and the root its block names.
fn signed_holger_guest(dir: &Path) -> (PathBuf, PathBuf) {
    let module = dir.join("plugin.holger.wasm");
    if let (Ok(built), Ok(root)) = (std::env::var("HOLGER_GUEST_WASM"), std::env::var("HOLGER_GUEST_TRUST_ROOT")) {
        std::fs::copy(&built, &module).unwrap_or_else(|e| panic!("HOLGER_GUEST_WASM={built}: {e}"));
        std::fs::copy(format!("{built}.p7s"), dir.join("plugin.holger.wasm.p7s")).expect("the signed build's .p7s");
        return (module, PathBuf::from(root));
    }
    use guest_host::verify::sign::{self, dev};
    let target = std::env::var("CARGO_TARGET_DIR").map(PathBuf::from).unwrap_or_else(|_| Path::new(env!("CARGO_MANIFEST_DIR")).join("../../../holger/target"));
    let built = target.join("wasm32-unknown-unknown/release/holger_monetize_guest.wasm");
    assert!(built.is_file(), "{} has not been built. This test loads the REAL Holger guest; it does not skip.\nBuild it with:\n    {BUILD_CMD}", built.display());
    std::fs::copy(&built, &module).unwrap();
    let (ca_key, ca_der) = dev::mint_ca("plugin-root").unwrap();
    let root = dir.join("korp-root-ca.der");
    std::fs::write(&root, &ca_der).unwrap();
    sign::sign_module_file(&module, &dev::new_ed25519_signer(&ca_key, &ca_der, "plugin-signing").unwrap()).unwrap();
    (module, root)
}

fn key(seed: u8) -> String {
    base64_encode(ed25519_dalek::SigningKey::from_bytes(&[seed; 32]).verifying_key().as_bytes())
}

fn now_ms() -> u64 {
    std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis() as u64
}

/// The ONE box as it serves Holger: the signed guest in `[products.holger] guest = …`, and NO
/// linked product. The estate's main and twin are declared by their fleet keys.
fn one_box(name: &str) -> (PathBuf, AppState) {
    let dir = scratch(name);
    let (module, root) = signed_holger_guest(&dir);
    let config = dir.join("monetize.toml");
    std::fs::write(
        &config,
        format!(
            "data_dir = {:?}\n[products.holger]\nfree = true\ninstance = \"{ESTATE}\"\nguest = {:?}\nguest_trust_roots = [{:?}]\ngrants = []\nfleet_keys = {{ main = \"{}\", twin = \"{}\" }}\n",
            dir.join("data").display().to_string(),
            module.display().to_string(),
            root.display().to_string(),
            key(11),
            key(12),
        ),
    )
    .unwrap();
    let cfg = Config::load(&config).unwrap();
    let state = AppState::in_memory(Registry::from_config(&cfg).expect("the signed Holger guest loads"));
    monetize_server::fleet::declare_pulled(&state, &cfg).expect("acme is declared under holger");
    (dir, state)
}

fn report(state: &AppState, r: &str, words: &[(&str, &str)], acked: &[u64]) {
    let health = words.iter().map(|(k, v)| (k.to_string(), v.to_string())).collect();
    let acked = acked.iter().map(|&seq| OrderAck { seq, ok: true, detail: "done".into(), receipt_json: String::new() }).collect();
    let rep = Report { build: "w3".into(), health, acked, measured_unix_ms: now_ms(), ..Default::default() };
    state.fleet.guardian().unwrap().store.record_report(ESTATE, r, now_ms(), &rep).unwrap();
}

fn healthy_twin(state: &AppState) {
    report(state, role::TWIN, &[("serving", "no"), ("data", "ok"), ("serves_at", TWIN_AT), ("primary", "live"), ("complete", "yes")], &[]);
}

/// The box's own guardian ticks (what monetize-server's fleet loop runs) until the main has an
/// order queued, at most `n` times. Answers the main's queued orders, oldest first.
fn tick_until_queued(state: &AppState, n: usize) -> Vec<monetize_embed::fleet::SignedOrder> {
    let g = state.fleet.guardian().unwrap().clone();
    for _ in 0..n {
        monetize_server::fleet::tick(state);
        let q = g.store.orders_for(ESTATE, role::MAIN, 0);
        if !q.is_empty() {
            return q;
        }
    }
    g.store.orders_for(ESTATE, role::MAIN, 0)
}

fn auto_fix(state: &AppState) {
    let g = state.fleet.guardian().unwrap().clone();
    g.set_auto_fix(ESTATE, true, "owner@test", true).expect("the estate opts into auto-fix");
}

#[test]
fn the_holger_guest_answers_the_guardian_and_a_healthy_estate_gets_no_order() {
    let (dir, state) = one_box("healthy");
    let p = state.registry.products().find(|p| p.id() == "holger").expect("holger is served by its guest");
    assert!(p.guardian().is_some(), "the Holger guest brings its guardian to the ONE box (the runner skipped every Holger estate without it)");
    auto_fix(&state);
    report(&state, role::MAIN, &[("serving", "yes"), ("data", "ok"), ("serves_at", MAIN_AT), ("twin", "paired")], &[]);
    healthy_twin(&state);
    assert!(tick_until_queued(&state, 5).is_empty(), "a healthy estate gets no order");
    assert!(state.fleet.guardian().unwrap().store.open_case(ESTATE).is_none(), "and no case");
    let _ = std::fs::remove_dir_all(&dir);
}

fn press(state: &AppState, remedy: monetize_product::guardian::Remedy) {
    let g = state.fleet.guardian().unwrap().clone();
    g.press(&monetize_server::fleet::ShopWorld { state }, ESTATE, remedy, "", "owner@test", true, state.now()).expect("the operator's button");
}

#[test]
fn a_held_main_is_no_case_of_its_own_and_its_recharge_is_pressed() {
    let (dir, state) = one_box("held");
    auto_fix(&state);
    // M3's boot hold: a main laid again serves nothing and holds nothing. Gunnar's guardian has
    // no finding for it (the rebuild plan that laid it fills it), so auto-fix queues nothing.
    report(&state, role::MAIN, &[("serving", "no"), ("data", "fresh"), ("serves_at", MAIN_AT), ("twin", "none")], &[]);
    healthy_twin(&state);
    assert!(tick_until_queued(&state, 5).is_empty(), "no separate held-main case");
    assert!(state.fleet.guardian().unwrap().store.open_case(ESTATE).is_none());
    // The operator presses Recharge from twin: a held main is fenced by its hold, so ONE recharge.
    press(&state, monetize_product::guardian::Remedy::RechargeFromTwin);
    let q = tick_until_queued(&state, 4);
    assert_eq!(q.iter().map(|o| o.kind.as_str()).collect::<Vec<_>>(), [kind::RECHARGE_FROM_TWIN], "one signed recharge, queued by the box");
    let body: serde_json::Value = serde_json::from_str(&q[0].body).unwrap();
    assert_eq!((body["main"].as_str(), body["twin"].as_str(), body["accept_lost"].as_bool()), (Some(MAIN_AT), Some(TWIN_AT), Some(false)), "{body}");
    assert!(!q[0].signature.is_empty(), "signed under holger's key");
    // The main pulls it, recharges and is released: the books are restated on it.
    report(&state, role::MAIN, &[("serving", "yes"), ("data", "ok"), ("serves_at", MAIN_AT), ("twin", "paired")], &[q[0].seq]);
    let q = tick_until_queued(&state, 4);
    assert_eq!(q.iter().map(|o| o.kind.as_str()).collect::<Vec<_>>(), [kind::RESTATE_ENTITLEMENTS], "the recharged main gets its books back");
    let _ = std::fs::remove_dir_all(&dir);
}

#[test]
fn a_main_that_still_serves_is_fenced_first_then_recharged() {
    let (dir, state) = one_box("serving");
    // A recharge that is not whole on a main that still takes writes: fence it, then recharge.
    report(&state, role::MAIN, &[("serving", "yes"), ("data", "restoring"), ("serves_at", MAIN_AT), ("twin", "paired")], &[]);
    healthy_twin(&state);
    press(&state, monetize_product::guardian::Remedy::RechargeFromTwin);
    let q = tick_until_queued(&state, 4);
    assert_eq!(q.iter().map(|o| o.kind.as_str()).collect::<Vec<_>>(), [kind::FENCE_PRIMARY], "the fence order comes first");
    let body: serde_json::Value = serde_json::from_str(&q[0].body).unwrap();
    assert_eq!(body["primary"].as_str(), Some(MAIN_AT), "{body}");
    report(&state, role::MAIN, &[("serving", "fenced"), ("data", "restoring"), ("serves_at", MAIN_AT), ("twin", "paired")], &[q[0].seq]);
    let q = tick_until_queued(&state, 4);
    assert_eq!(q.iter().map(|o| o.kind.as_str()).collect::<Vec<_>>(), [kind::RECHARGE_FROM_TWIN], "then the recharge order");
    let _ = std::fs::remove_dir_all(&dir);
}