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

nordisk/monetize

crates/monetize-guardian/src/fleet.rs

//! **The pull channel's front door** (G1-pull): `Fleet.Report`, `Fleet.Orders`, `Fleet.Ask`.
//!
//! Every call is a [`FleetCall`] signed by the calling box's own Ed25519 key, registered for
//! `(instance, role)` at install. Anyone else is refused in one sentence that names nothing.
//! Monetize answers; it never calls back.

use monetize_embed::fleet::{call, verify_fleet_call, Ask, FleetCall, OrdersAfter, Report, SignedOrder, LONG_POLL_MAX_MS};
use monetize_product::guardian::{Finding, Remedy};

use crate::runner::Guardian;

/// Why a box's call is refused.
#[derive(Clone, PartialEq, Eq, Debug, thiserror::Error)]
pub enum FleetRefusal {
    /// Unknown estate or box, wrong product, bad signature, stale: all said the same way.
    #[error("the call is not signed by a box this monetize knows")]
    Unauthenticated,
    #[error("{0}")]
    Malformed(String),
    #[error("{0}")]
    Unavailable(String),
    /// Heard and refused by name: what the box asked is not its to have (a disk that is not
    /// its own, more than is paid).
    #[error("{0}")]
    Refused(String),
}

/// What `Fleet.Ask` answers at once; anything that changes the estate comes later as orders.
#[derive(Clone, PartialEq, Eq, Debug, Default)]
pub struct AskAnswer {
    pub case_id: String,
    pub words: String,
}

/// The remedies a box may ask for.
fn asked_remedy(kind: &str) -> Option<Remedy> {
    Remedy::parse(kind)
}

impl Guardian {
    /// The caller is the box it says it is, and the call is the one it says.
    pub fn authenticate(&self, c: &FleetCall, signature: &[u8], want: &str, now_unix_ms: u64) -> Result<(), FleetRefusal> {
        if c.call != want {
            return Err(FleetRefusal::Malformed(format!("a {:?} call on the {want} door", c.call)));
        }
        if self.store.product_of(&c.instance).as_deref() != Some(c.product.as_str()) {
            return Err(FleetRefusal::Unauthenticated);
        }
        let key = self.store.key_of(&c.instance, &c.role).ok_or(FleetRefusal::Unauthenticated)?;
        verify_fleet_call(c, signature, &key, now_unix_ms).map_err(|_| FleetRefusal::Unauthenticated)
    }

    /// **A box's first call (TOFU)**: when no key is registered for `(instance, role)` and a
    /// pin window is open for it (a sold estate's install, a case that just laid that role),
    /// `box_key` is pinned if the call verifies under it, and the window closes. `Ok(false)`:
    /// the key presented is the one registered (nothing to pin). Outside a window: refused.
    ///
    /// ★ PIN-1 (prod audit 2026-10-12): a window open while a key IS registered — the
    /// estate's owner said its boxes were laid AGAIN — lets a box presenting a NEW key
    /// replace the old one, once (the window closes on the re-pin): a reinstalled box mints
    /// a new fleet key on its new data disk and was refused for ever.
    pub fn first_call(&self, c: &FleetCall, signature: &[u8], box_key: &[u8], now_unix_ms: u64) -> Result<bool, FleetRefusal> {
        if self.store.product_of(&c.instance).as_deref() != Some(c.product.as_str()) {
            return Err(FleetRefusal::Unauthenticated);
        }
        let open = self.store.pin_window(&c.instance, &c.role) >= now_unix_ms;
        if let Some(pinned) = self.store.key_of(&c.instance, &c.role) {
            if pinned.as_bytes().as_slice() == box_key {
                return Ok(false);
            }
            if !open {
                return Err(FleetRefusal::Unauthenticated);
            }
        }
        if !open {
            return Err(FleetRefusal::Unauthenticated);
        }
        let raw: [u8; 32] = box_key.try_into().map_err(|_| FleetRefusal::Unauthenticated)?;
        let key = ed25519_dalek::VerifyingKey::from_bytes(&raw).map_err(|_| FleetRefusal::Unauthenticated)?;
        verify_fleet_call(c, signature, &key, now_unix_ms).map_err(|_| FleetRefusal::Unauthenticated)?;
        let b64 = monetize_embed::signing::base64_encode(&raw);
        self.store.register_key(&c.instance, &c.role, &b64).map_err(|e| FleetRefusal::Unavailable(e.to_string()))?;
        self.store.open_pin(&c.instance, &c.role, 0).map_err(|e| FleetRefusal::Unavailable(e.to_string()))?;
        if let Some(mut case) = self.store.open_case(&c.instance) {
            case.say(now_unix_ms, format!("the new {} called home: its key pinned on its first call", c.role));
            // The key is pinned whatever the case's log says: the box is not refused for it.
            if let Err(err) = self.store.put_case(&case) {
                eprintln!("monetize-guardian: ALERT guardian.case-unsaved {}: the call home of the new {} is not in its log ({err})", case.id, c.role);
            }
        }
        Ok(true)
    }

    /// **`Fleet.Report`**: the box's heartbeat and its acks. Answers the seqs it acked.
    pub fn report(&self, c: &FleetCall, signature: &[u8], now_unix_ms: u64) -> Result<Vec<u64>, FleetRefusal> {
        self.authenticate(c, signature, call::REPORT, now_unix_ms)?;
        let report: Report = if c.body.trim().is_empty() { Report::default() } else { serde_json::from_str(&c.body).map_err(|e| FleetRefusal::Malformed(format!("the report is not a report: {e}")))? };
        self.store.record_report(&c.instance, &c.role, now_unix_ms, &report).map_err(|e| FleetRefusal::Unavailable(e.to_string()))
    }

    /// **`Fleet.Orders`**, once: the signed orders queued for the calling box after its
    /// `after`, and how long the caller may hold the call open waiting for one.
    pub fn orders(&self, c: &FleetCall, signature: &[u8], now_unix_ms: u64) -> Result<(Vec<SignedOrder>, u64), FleetRefusal> {
        self.authenticate(c, signature, call::ORDERS, now_unix_ms)?;
        let ask: OrdersAfter = if c.body.trim().is_empty() { OrdersAfter::default() } else { serde_json::from_str(&c.body).map_err(|e| FleetRefusal::Malformed(format!("not an orders request: {e}")))? };
        Ok((self.store.orders_for(&c.instance, &c.role, ask.after), ask.wait_ms.min(LONG_POLL_MAX_MS)))
    }

    /// **`Fleet.Ask`**: the estate asks for a remedy (`new_twin`, `new_server`,
    /// `recharge_from_twin`, `review_twin`). It opens a case — approved only by a human or
    /// the estate's own auto-fix opt-in, never by the asking box.
    pub fn ask(&self, c: &FleetCall, signature: &[u8], now_unix_ms: u64) -> Result<AskAnswer, FleetRefusal> {
        self.authenticate(c, signature, call::ASK, now_unix_ms)?;
        let ask: Ask = serde_json::from_str(&c.body).map_err(|e| FleetRefusal::Malformed(format!("not an ask: {e}")))?;
        let remedy = asked_remedy(&ask.kind).ok_or_else(|| FleetRefusal::Malformed(format!("{:?} is nothing an estate may ask for: new_twin, new_server, recharge_from_twin or review_twin", ask.kind)))?;
        if let Some(open) = self.store.open_case(&c.instance) {
            return Ok(AskAnswer { case_id: open.id.clone(), words: format!("case {} is already {}", open.id, open.state.as_str()) });
        }
        let finding = Finding {
            code: format!("asked:{}", remedy.as_str()),
            role: c.role.clone(),
            words: format!("the {} asked for {}", c.role, remedy.words().to_lowercase()),
            evidence: vec![format!("Fleet.Ask from {}: {}", c.role, ask.body)],
            remedies: vec![remedy],
            auto: Some(remedy),
        };
        let case = self.open(&c.instance, &c.product, finding, now_unix_ms).map_err(|e| FleetRefusal::Unavailable(e.to_string()))?;
        Ok(AskAnswer { case_id: case.id.clone(), words: format!("case {} opened; a human (or the estate's auto-fix) decides", case.id) })
    }
}