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

nordisk/gunnar

crates/gunnar-pairing-door/src/front.rs

//! **The front's role, run by the box the installer laid on the FRONT slot**
//! (edda nordisk-pairing 0.4).
//!
//! vm1 boots its OWN medium, the front medium (`gunnar-front-installer-open`,
//! the `gunnar-front` binary; owner 2026-09-25: "twin/main gunnar share ISO,
//! but NOT the ui/monetize"). The installer's ledger has a FRONT slot at its
//! address, so the installer feeds it role `front`.
//! Then this box is what `gunnar-pairing-door --ledger` was on the old front,
//! from what the installer FED it instead of a ledger pushed by the operator:
//!
//! * **the relay** — the WireGuard hub on udp/[`nordisk_pairing::RELAY_PORT`],
//!   keyed with this box's OWN tunnel key (the key its presentation carried,
//!   so the key the installer feeds every box as the `front` peer — edda's
//!   `Door::front_tunnel`), listing every fed box peer by its overlay /32;
//! * **the twin's certification** — once the fed peers name a primary and a
//!   twin with utility addresses (0.4.1 `Peer::utility`), signed with the
//!   front's own certify key. The front mints it and OFFERS its public half
//!   ([`OFFER_CERTIFY_PUBKEY`]); the installer feeds it onward and the elected
//!   primary installs it as the machine admin [`CERTIFY_PRINCIPAL`] — so no
//!   console key is carried to the front by anybody.

use crate::certify::{self, Record, Target};
use crate::grpc::GrpcBoxes;
use nordisk_pairing::join::{JoinReply, Peer};
use nordisk_pairing::{Role, CONTROL_PORT};
use std::collections::BTreeMap;
use std::net::Ipv4Addr;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use std::time::Duration;

/// The offer key of the front's certify key (edda nordisk-pairing 0.3.1
/// `GET /install/offer`): its OpenSSH public key line. Offered only by a box
/// fed role `front`.
pub const OFFER_CERTIFY_PUBKEY: &str = "certify_pubkey";

/// The principal the primary installs the offered certify key as: a machine
/// with the gunnar-admin grant — what the twin's enrolment and certificate
/// need signed ([`crate::certify`]).
pub const CERTIFY_PRINCIPAL: &str = "front-certify";

/// Which boxes to certify, from what the installer FED the front: the fed
/// peers with role primary/twin and their utility addresses. `None` until the
/// election has spoken and both carry one.
pub fn target_of(reply: &JoinReply) -> Option<Target> {
    let of = |role: Role| reply.peers.iter().find(|p| p.role == role);
    let twin = of(Role::Twin)?;
    let primary = of(Role::Primary)?;
    // Over the tunnel (WireGuard-only, owner 2026-10-04): the overlay
    // addresses. The twin's own loop becomes the twin of the primary's
    // overlay address — the SAME string must be given again for
    // BecomeTwin's idempotent answer (`tunnel_addr::primary_control`).
    let twin_at = twin.overlay;
    let primary_at = reply.primary_overlay.unwrap_or(primary.overlay);
    // A box laid again keeps its overlay address: which boxes these ARE is
    // their utility address (else their tunnel key), so a reborn twin is
    // certified again.
    let who = |p: &Peer, u: Option<Ipv4Addr>| {
        u.or(p.utility)
            .map(|u| u.to_string())
            .unwrap_or_else(|| p.tunnel_pubkey.trim().to_string())
    };
    let generation = format!(
        "{} {}",
        who(twin, None),
        who(primary, reply.primary_utility)
    );
    Some(Target {
        twin: twin.name.clone(),
        primary: primary.name.clone(),
        twin_ctl: format!("{twin_at}:{CONTROL_PORT}"),
        primary_ctl: format!("{primary_at}:{CONTROL_PORT}"),
        primary_for_twin: format!("{primary_at}:{CONTROL_PORT}"),
        generation,
    })
}

/// **Should the twin be certified (again) for `t`?**
///
/// Never certified: yes. Certified for exactly this pair: no. Certified for
/// ANOTHER pair — the main was recharged at a new address, or a new twin was
/// laid (the installer re-admitted it) — yes. A record written before the
/// pair was kept ([`Record::for_target`] empty) and certified: no, as before.
pub fn certify_again(record: &Record, t: &certify::Target) -> bool {
    !record.certified || (!record.for_target.is_empty() && record.for_target != t.key())
}

/// **The front's own interface on the overlay** (WireGuard-only, owner
/// 2026-10-04): a kernel tun holding [`nordisk_pairing::OVERLAY_FRONT`]/24,
/// fed by the relay ([`wg_appliance_core::hubrelay::TunPort`]). Every
/// connection the front makes to a box — the console, monetize, the forge
/// forward, the certification — goes through it to the box's overlay
/// address; the clock and the plugin door are served on it.
pub const HUB_IFACE: &str = "wg-hub";

static HUB_PORT: std::sync::OnceLock<Arc<dyn wg_appliance_core::hubrelay::HubPort>> =
    std::sync::OnceLock::new();

/// **Open the front's overlay interface** — once, at boot, before the clock
/// and the plugin door bind on it; the relay takes it when the front's role
/// starts. Needs CAP_NET_ADMIN (PID 1 on the front medium has it).
pub fn open_hub() -> Result<std::net::Ipv4Addr, String> {
    if HUB_PORT.get().is_some() {
        return Ok(nordisk_pairing::OVERLAY_FRONT);
    }
    let (net, prefix) = nordisk_pairing::OVERLAY_NET;
    let _ = net;
    let tun = wg_appliance_core::tun::TunDevice::create(HUB_IFACE, 1420)
        .map_err(|e| format!("{HUB_IFACE}: {e} (CAP_NET_ADMIN?)"))?;
    wg_appliance_core::addr::assign_ipv4(HUB_IFACE, nordisk_pairing::OVERLAY_FRONT, prefix)
        .map_err(|e| {
            format!(
                "{HUB_IFACE} {}/{prefix}: {e}",
                nordisk_pairing::OVERLAY_FRONT
            )
        })?;
    wg_appliance_core::addr::link_up(HUB_IFACE).map_err(|e| format!("{HUB_IFACE} up: {e}"))?;
    let _ = HUB_PORT.set(Arc::new(wg_appliance_core::hubrelay::TunPort(tun)));
    Ok(nordisk_pairing::OVERLAY_FRONT)
}

/// The front's overlay interface, when [`open_hub`] opened it.
pub fn hub_port() -> Option<Arc<dyn wg_appliance_core::hubrelay::HubPort>> {
    HUB_PORT.get().cloned()
}

/// Pure: **where the front serves its egress door** (the lean estate, A19 rows 10-14: main
/// and the twin hold no public IPv4 and leave through this front, Skidbladnir
/// nordisk-pairing `egress`): the overlay address its hub opened, never the utility one.
/// `None`: no overlay, no door.
pub fn egress_door_bind(overlay: Option<Ipv4Addr>) -> Option<std::net::SocketAddr> {
    overlay
        .filter(|o| *o == nordisk_pairing::OVERLAY_FRONT)
        .map(|o| std::net::SocketAddr::from((o, nordisk_pairing::egress::EGRESS_PORT)))
}

/// The relay's private key from the box's `wg0.key` (base64, as the tunnel
/// lane keeps it) — the key its presentation carried.
pub fn relay_key_from_b64(b64: &str) -> Result<[u8; 32], String> {
    wg_appliance_core::decode_key(b64.trim())
        .map_err(|e| format!("the box's tunnel key is not a WireGuard key: {}", e.0))
}

/// Pure: (name, tunnel key, overlay) of every fed box peer with a key — the
/// front's own entry excluded.
pub fn to_list(peers: &[Peer]) -> Vec<(String, String, Ipv4Addr)> {
    peers
        .iter()
        .filter(|p| p.role != Role::Front && !p.tunnel_pubkey.trim().is_empty())
        .map(|p| {
            (
                p.name.clone(),
                p.tunnel_pubkey.trim().to_string(),
                p.overlay,
            )
        })
        .collect()
}

/// The jera job that certifies the twin once the election is in (A32-14).
pub const CERTIFY_JOB: &str = "certify-twin";

/// **The front, running.** Owned for the life of the process: dropping it
/// stops the relay.
pub struct FrontRole {
    relay: wg_appliance_core::hubrelay::HubRelay,
    /// The key each overlay address is listed with, and the PSK (in memory
    /// only, as the fed reply itself is).
    listed: BTreeMap<Ipv4Addr, (String, Option<String>)>,
    key: gunnar_core::ssh_key::PrivateKey,
    record: Arc<Mutex<Record>>,
    record_path: PathBuf,
    /// The pair ([`certify::Target::key`]) being or last certified by this process.
    certifying: Option<String>,
    /// The pair the certify loops are told is wanted ([`certify::Wanted`]).
    wanted: certify::Wanted,
}

impl FrontRole {
    /// Bind the relay on udp/`port` with this box's tunnel private key, and
    /// hold the certify key; the certify record is kept at `record_path`.
    pub fn start(
        port: u16,
        relay_private: [u8; 32],
        certify_key: gunnar_core::ssh_key::PrivateKey,
        record_path: &Path,
    ) -> Result<FrontRole, String> {
        // With the front's own overlay interface open, the hub is a box on its
        // overlay: what the boxes send to .1 reaches the front's processes, and
        // what they send to a box is sealed to it (WireGuard-only).
        // A32-14, stays a task: the front's relay, held by the role for its life.
        let relay = match hub_port() {
            Some(p) => wg_appliance_core::hubrelay::HubRelay::spawn_with_port(
                port,
                relay_private,
                nordisk_pairing::OVERLAY_FRONT,
                p,
            ),
            // A32-14, stays a task: the same relay on the default port.
            None => wg_appliance_core::hubrelay::HubRelay::spawn(
                port,
                relay_private,
                nordisk_pairing::OVERLAY_FRONT,
            ),
        }
        .map_err(|e| format!("the relay could not bind udp/{port}: {e}"))?;
        let record = Arc::new(Mutex::new(Record::load(record_path)?));
        Ok(FrontRole {
            relay,
            listed: BTreeMap::new(),
            key: certify_key,
            record,
            record_path: record_path.to_path_buf(),
            certifying: None,
            wanted: Arc::new(Mutex::new(None)),
        })
    }

    /// The relay's public key (base64) — the box's own tunnel key.
    pub fn public(&self) -> &str {
        &self.relay.public_b64
    }

    /// `(overlay, key)` of every box the relay lists.
    pub fn relay_peers(&self) -> Vec<(Ipv4Addr, String)> {
        self.relay
            .verdict()
            .peers
            .iter()
            .map(|(a, k, _)| (*a, wg_appliance_core::encode_key(k)))
            .collect()
    }

    /// The overlay addresses of the boxes the relay holds a pre-shared key for.
    pub fn relay_psk(&self) -> Vec<Ipv4Addr> {
        self.relay.verdict().with_psk
    }

    /// The pair a certify loop may still work on (`<twin_ctl> <primary_ctl>`).
    pub fn wanted(&self) -> Option<String> {
        self.wanted.lock().map(|w| w.clone()).unwrap_or_default()
    }

    /// The certify record so far.
    pub fn record(&self) -> Record {
        self.record.lock().map(|r| r.clone()).unwrap_or_default()
    }

    /// **Take what the installer fed** (every time it changes): list new box
    /// peers on the relay, and once the verdict is fed start certifying the
    /// twin in a thread of its own (once per process; a record that says
    /// certified is not redone). Returns what was done, to be said.
    pub fn take(&mut self, reply: &JoinReply) -> Vec<String> {
        let mut said = Vec::new();
        for (name, key, overlay) in to_list(&reply.peers) {
            // The PSK the installer fed the front for this box (`wg_psk.<box>`,
            // 2026-10-06): the relay keys every session with it. Remembered with
            // the key, so a re-fed PSK relists the box and a restart's same feed
            // changes nothing.
            let fed_psk = reply
                .feed
                .get(&nordisk_pairing::installer::feed_wg_psk_for(&name))
                .map(|p| p.trim().to_string());
            let psk = match fed_psk.as_deref().map(wg_appliance_core::decode_key) {
                None => None,
                Some(Ok(p)) => Some(p),
                Some(Err(_)) => {
                    said.push(format!("`{name}` was fed a value that is not a WireGuard pre-shared key — not listed with it"));
                    continue;
                }
            };
            let memo = (key.clone(), fed_psk.clone());
            if self.listed.get(&overlay) == Some(&memo) {
                continue;
            }
            match wg_appliance_core::decode_key(&key) {
                Ok(k) => {
                    let known = self.listed.contains_key(&overlay);
                    let rekeyed = known
                        && self.listed.get(&overlay).map(|(k, _)| k.as_str()) != Some(key.as_str());
                    // Named, so a relay timer line says `wg{peer=relay:<name>:<fp>}`.
                    // A re-feed after a re-admission: the box at this address was
                    // laid again with a key of its own — re-keyed (its name kept),
                    // never refused. A restart re-feeds the same pairs: no change.
                    let listed = if known {
                        self.relay.relist_peer_with_psk(k, psk, overlay)
                    } else {
                        self.relay
                            .add_named_peer_with_psk(k, psk, overlay, &name)
                            .map(|()| true)
                            .or_else(|_| self.relay.relist_peer_with_psk(k, psk, overlay))
                    };
                    let with = if psk.is_some() {
                        " with its pre-shared key"
                    } else {
                        ""
                    };
                    match listed {
                        Ok(true) if rekeyed => said.push(format!("`{name}` re-keyed on the relay at {overlay}{with} (the box was laid again)")),
                        Ok(true) if known => said.push(format!("`{name}` takes a new pre-shared key on the relay at {overlay}")),
                        Ok(_) => said.push(format!("`{name}` listed on the relay as {overlay}{with}")),
                        Err(e) => said.push(format!("`{name}` could not be listed at {overlay}: {e}")),
                    }
                }
                Err(_) => said.push(format!(
                    "`{name}` was fed a tunnel key that is not a WireGuard key — not listed"
                )),
            }
            self.listed.insert(overlay, memo);
        }
        // L1: a box listed before and no longer named with a key DIED — the installer is
        // laying it again. Its session is forgotten now, not after two minutes of handshakes
        // toward a machine that is gone; its new key lists it again.
        let fed: std::collections::BTreeSet<Ipv4Addr> = to_list(&reply.peers)
            .into_iter()
            .map(|(_, _, o)| o)
            .collect();
        let gone: Vec<Ipv4Addr> = self
            .listed
            .keys()
            .filter(|o| !fed.contains(o))
            .copied()
            .collect();
        for overlay in gone {
            self.listed.remove(&overlay);
            if self.relay.forget_peer(overlay) {
                let name = reply
                    .peers
                    .iter()
                    .find(|p| p.overlay == overlay)
                    .map(|p| p.name.clone())
                    .unwrap_or_else(|| overlay.to_string());
                said.push(format!("`{name}` forgotten on the relay at {overlay}: the installer's feed no longer names it with a key (the box is being laid again)"));
            }
        }
        if let Some(target) = target_of(reply) {
            let pair = target.key();
            if self.certifying.as_deref() != Some(pair.as_str()) {
                self.certifying = Some(pair.clone());
                // Every loop started before for another pair stops (R2 after U3: the
                // loop for the DEAD twin kept dialling it between the new loop's tries).
                *self.wanted.lock().unwrap_or_else(|p| p.into_inner()) = Some(pair.clone());
                if !certify_again(&self.record(), &target) {
                    // A record kept from an older build names no pair (a keep-upgrade from
                    // prod's d697fa4): it is THIS pair's, adopted, so a later move of the
                    // twin or the primary is certified again rather than skipped.
                    let adopted = {
                        let mut r = self.record.lock().unwrap_or_else(|p| p.into_inner());
                        if r.for_target.is_empty() {
                            r.for_target = pair.clone();
                            Some(r.clone())
                        } else {
                            None
                        }
                    };
                    if let Some(r) = adopted {
                        if let Err(e) = r.save(&self.record_path) {
                            said.push(format!("the kept certify record could not be updated: {e}"));
                        }
                    }
                    said.push(format!(
                        "twin `{}` is already certified ({}), not redone",
                        target.twin,
                        self.record_path.display()
                    ));
                } else {
                    said.push(format!(
                        "the election is in — certifying twin `{}` ({}) against primary `{}` ({})",
                        target.twin, target.twin_ctl, target.primary, target.primary_ctl
                    ));
                    let (key, record, path, wanted) = (
                        self.key.clone(),
                        Arc::clone(&self.record),
                        self.record_path.clone(),
                        Arc::clone(&self.wanted),
                    );
                    // A named jera job (A32-14): a certify that panics is said (ALERT) and a
                    // FAILED row, and asked again after the settle; the front's stop ends it.
                    let say: gunnar_common::jobs::Say =
                        Arc::new(|l: &str| tracing::error!("front: {l}"));
                    let mut settled = false;
                    let spawned =
                        gunnar_common::jobs::start(CERTIFY_JOB, certify::SETTLE, say, move || {
                            // Ordered after the installer's re-feed of the other
                            // boxes (this front's certify key), as far as a wait
                            // can: see `certify::SETTLE` and `certify::Tone`.
                            if !std::mem::replace(&mut settled, true) {
                                return gunnar_common::jobs::Round::After(certify::SETTLE);
                            }
                            match GrpcBoxes::new(key.clone(), Duration::from_secs(15)) {
                                Ok(boxes) => {
                                    certify::run_while(
                                        &boxes,
                                        &target,
                                        &record,
                                        Some(&path),
                                        certify::door_waits(),
                                        &wanted,
                                    );
                                }
                                Err(e) => record.lock().unwrap_or_else(|p| p.into_inner()).why = e,
                            }
                            gunnar_common::jobs::Round::Done
                        });
                    if let Err(e) = spawned {
                        said.push(format!("the certify job could not start: {e}"));
                        self.certifying = None;
                    }
                }
            }
        }
        said
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use nordisk_pairing::join::Assigned;
    use std::collections::BTreeMap;

    fn peer(
        name: &str,
        role: Role,
        key: &str,
        overlay: Ipv4Addr,
        utility: Option<Ipv4Addr>,
    ) -> Peer {
        Peer {
            name: name.into(),
            role,
            tunnel_pubkey: key.into(),
            overlay,
            endpoint: None,
            utility,
        }
    }

    fn fed_front(peers: Vec<Peer>, primary_utility: Option<Ipv4Addr>) -> JoinReply {
        JoinReply {
            estate: "gunnar.rs".into(),
            me: Assigned {
                name: "front".into(),
                overlay: nordisk_pairing::OVERLAY_FRONT,
            },
            cert_pem: None,
            role: Some(Role::Front),
            primary_overlay: None,
            primary_utility,
            peers,
            feed: BTreeMap::new(),
        }
    }

    const K1: &str = "xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=";

    /// The front lists the fed boxes, never itself, and certifies from the fed
    /// verdict over the TUNNEL (WireGuard-only, owner 2026-10-04): the overlay
    /// addresses, the same string the twin joined the primary at.
    #[test]
    fn the_front_lists_the_fed_boxes_and_certifies_over_the_tunnel() {
        let front = nordisk_pairing::join::FrontTunnel {
            tunnel_pubkey: K1.into(),
            endpoint: "95.0.0.1:51820".into(),
        }
        .as_peer();
        let main = peer(
            "main",
            Role::Primary,
            "KM",
            nordisk_pairing::OVERLAY_BOX_A,
            Some(Ipv4Addr::new(10, 13, 8, 2)),
        );
        let twin = peer(
            "twin",
            Role::Twin,
            "",
            nordisk_pairing::OVERLAY_BOX_B,
            Some(Ipv4Addr::new(10, 13, 8, 3)),
        );
        let r = fed_front(
            vec![front, main.clone(), twin.clone()],
            Some(Ipv4Addr::new(10, 13, 8, 2)),
        );
        assert_eq!(
            to_list(&r.peers),
            vec![(
                "main".to_string(),
                "KM".to_string(),
                nordisk_pairing::OVERLAY_BOX_A
            )],
            "not the front, not a box without a key"
        );
        let t = target_of(&r).expect("the verdict is fed");
        assert_eq!(
            (
                t.twin.as_str(),
                t.twin_ctl.as_str(),
                t.primary_ctl.as_str(),
                t.primary_for_twin.as_str()
            ),
            (
                "twin",
                "10.44.0.3:50051",
                "10.44.0.2:50051",
                "10.44.0.2:50051"
            )
        );
        // The door's own statement of the primary's overlay is the one the twin joined.
        let said = JoinReply {
            primary_overlay: Some(nordisk_pairing::OVERLAY_BOX_A),
            ..fed_front(vec![main, twin], None)
        };
        assert_eq!(
            target_of(&said).unwrap().primary_for_twin,
            "10.44.0.2:50051"
        );
    }

    /// The relay runs on the box's own key and lists a fed box once.
    #[test]
    fn the_relay_runs_on_the_boxes_own_key_and_lists_each_fed_box_once() {
        let dir = tempfile_dir();
        let private = [7u8; 32];
        let public = wg_appliance_core::encode_key(
            &wg_appliance_core::keys::public_from_private_bytes(&private),
        );
        let key =
            gunnar_core::ssh_key::PrivateKey::from_openssh(gunnar_core::mint_ed25519().unwrap())
                .unwrap();
        let mut f = FrontRole::start(0, private, key, &dir.join("certify.toml")).unwrap();
        assert_eq!(
            f.public(),
            public,
            "the relay is keyed with the box's own tunnel key"
        );
        let r = fed_front(
            vec![peer(
                "main",
                Role::Primary,
                K1,
                nordisk_pairing::OVERLAY_BOX_A,
                None,
            )],
            None,
        );
        assert_eq!(
            f.take(&r),
            vec![format!(
                "`main` listed on the relay as {}",
                nordisk_pairing::OVERLAY_BOX_A
            )]
        );
        assert!(f.take(&r).is_empty(), "listed once");
    }

    const PSK: &str = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=";
    const PSK2: &str = "ICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj8=";

    /// **Each box is listed with the pre-shared key the installer fed the
    /// front for it** (2026-10-06, the PQ transport lane: `wg_psk.<box>`), and
    /// said so — never the key. The same feed again changes nothing; a new PSK
    /// for the same box relists it; a fed value that is not a key is said and
    /// the box is not listed with it.
    #[test]
    fn each_fed_box_is_listed_with_its_preshared_key_and_a_new_one_relists_it() {
        let dir = tempfile_dir();
        let key =
            gunnar_core::ssh_key::PrivateKey::from_openssh(gunnar_core::mint_ed25519().unwrap())
                .unwrap();
        let mut f = FrontRole::start(0, [6u8; 32], key, &dir.join("certify.toml")).unwrap();
        let mut r = fed_front(
            vec![peer(
                "main",
                Role::Primary,
                K1,
                nordisk_pairing::OVERLAY_BOX_A,
                None,
            )],
            None,
        );
        r.feed.insert(
            nordisk_pairing::installer::feed_wg_psk_for("main"),
            PSK.into(),
        );
        let said = f.take(&r);
        assert_eq!(
            said,
            vec![format!(
                "`main` listed on the relay as {} with its pre-shared key",
                nordisk_pairing::OVERLAY_BOX_A
            )]
        );
        assert!(!said.concat().contains(PSK));
        assert_eq!(f.relay_psk(), vec![nordisk_pairing::OVERLAY_BOX_A]);
        assert!(f.take(&r).is_empty(), "the same feed: nothing to do");
        r.feed.insert(
            nordisk_pairing::installer::feed_wg_psk_for("main"),
            PSK2.into(),
        );
        let said = f.take(&r);
        assert!(
            said.iter()
                .any(|l| l.contains("`main`") && l.contains("pre-shared key")),
            "{said:?}"
        );
        assert_eq!(f.relay_psk(), vec![nordisk_pairing::OVERLAY_BOX_A]);
        r.feed.insert(
            nordisk_pairing::installer::feed_wg_psk_for("main"),
            "not-a-key".into(),
        );
        let said = f.take(&r);
        assert!(
            said.iter()
                .any(|l| l.contains("`main`") && l.contains("not a WireGuard pre-shared key")),
            "{said:?}"
        );
    }

    /// **A box laid again** (the installer re-admits it: nordisk-pairing
    /// `Door::readmit`) keeps its overlay address and brings a NEW tunnel key:
    /// the relay re-keys that address instead of refusing the new box.
    #[test]
    fn a_relaid_box_is_relisted_at_its_address_with_its_new_key() {
        let dir = tempfile_dir();
        let key =
            gunnar_core::ssh_key::PrivateKey::from_openssh(gunnar_core::mint_ed25519().unwrap())
                .unwrap();
        let mut f = FrontRole::start(0, [9u8; 32], key, &dir.join("certify.toml")).unwrap();
        let (_, fresh) = wg_appliance_core::keys::generate_keypair_b64();
        let before = fed_front(
            vec![peer(
                "main",
                Role::Primary,
                K1,
                nordisk_pairing::OVERLAY_BOX_A,
                None,
            )],
            None,
        );
        f.take(&before);
        let after = fed_front(
            vec![peer(
                "main",
                Role::Primary,
                &fresh,
                nordisk_pairing::OVERLAY_BOX_A,
                None,
            )],
            None,
        );
        let said = f.take(&after);
        assert!(
            said.iter()
                .any(|l| l.contains("`main`") && l.contains("re-keyed")),
            "{said:?}"
        );
        assert_eq!(
            f.relay_peers(),
            vec![(nordisk_pairing::OVERLAY_BOX_A, fresh.clone())],
            "the slot took the new key"
        );
        assert!(f.take(&after).is_empty(), "then nothing to do");
    }

    /// **L1 (estate-box check 3, release-3 recharge): a box the re-feed no longer names is
    /// forgotten on the relay at once.** The main died; the installer re-fed the front with
    /// the twin only (the new main has no key until it pairs). The relay kept the dead
    /// main's session and sealed the twin's every packet to it — `HANDSHAKE(REKEY_TIMEOUT)`
    /// every 5 s, then `ERROR CONNECTION_EXPIRED` (MEASURED: 2 min of it, estate-box gate1).
    /// The new box's key, when it comes, lists it again.
    #[test]
    fn a_box_the_refeed_no_longer_names_is_forgotten_and_its_new_key_lists_it_again() {
        let dir = tempfile_dir();
        let key =
            gunnar_core::ssh_key::PrivateKey::from_openssh(gunnar_core::mint_ed25519().unwrap())
                .unwrap();
        let mut f = FrontRole::start(0, [5u8; 32], key, &dir.join("certify.toml")).unwrap();
        let (_, twin_key) = wg_appliance_core::keys::generate_keypair_b64();
        let twin = peer(
            "twin",
            Role::Twin,
            &twin_key,
            nordisk_pairing::OVERLAY_BOX_B,
            None,
        );
        f.take(&fed_front(
            vec![
                peer(
                    "main",
                    Role::Primary,
                    K1,
                    nordisk_pairing::OVERLAY_BOX_A,
                    None,
                ),
                twin.clone(),
            ],
            None,
        ));
        // The re-feed after main died: main with no key (being laid again).
        let said = f.take(&fed_front(
            vec![
                peer(
                    "main",
                    Role::Primary,
                    "",
                    nordisk_pairing::OVERLAY_BOX_A,
                    None,
                ),
                twin.clone(),
            ],
            None,
        ));
        assert!(
            said.iter()
                .any(|l| l.contains("`main`") && l.contains("forgotten")),
            "{said:?}"
        );
        assert!(
            f.take(&fed_front(vec![twin.clone()], None)).is_empty(),
            "forgotten once"
        );
        // The new main's key lists it again.
        let (_, fresh) = wg_appliance_core::keys::generate_keypair_b64();
        let said = f.take(&fed_front(
            vec![
                peer(
                    "main",
                    Role::Primary,
                    &fresh,
                    nordisk_pairing::OVERLAY_BOX_A,
                    None,
                ),
                twin,
            ],
            None,
        ));
        assert!(
            said.iter()
                .any(|l| l.contains("`main`")
                    && l.contains(&nordisk_pairing::OVERLAY_BOX_A.to_string())),
            "{said:?}"
        );
        assert!(
            f.relay_peers()
                .contains(&(nordisk_pairing::OVERLAY_BOX_A, fresh)),
            "the slot took the new key"
        );
    }

    /// **A re-feed at new addresses hands the certify loop the new pair** (estate-box3
    /// life1, R2 after U3: row recharge-twin-certify-stale): the loop started for the dead
    /// twin must learn it is no longer wanted, and only the new pair is.
    #[test]
    fn a_refeed_at_new_addresses_hands_the_certify_loop_the_new_pair() {
        let dir = tempfile_dir();
        let key =
            gunnar_core::ssh_key::PrivateKey::from_openssh(gunnar_core::mint_ed25519().unwrap())
                .unwrap();
        let mut f = FrontRole::start(0, [3u8; 32], key, &dir.join("certify.toml")).unwrap();
        let (_, main_key) = wg_appliance_core::keys::generate_keypair_b64();
        let (_, twin_key) = wg_appliance_core::keys::generate_keypair_b64();
        let main = peer(
            "main",
            Role::Primary,
            &main_key,
            nordisk_pairing::OVERLAY_BOX_A,
            Some(Ipv4Addr::new(127, 6, 0, 14)),
        );
        let before = fed_front(
            vec![
                main.clone(),
                peer(
                    "twin",
                    Role::Twin,
                    &twin_key,
                    nordisk_pairing::OVERLAY_BOX_B,
                    Some(Ipv4Addr::new(127, 6, 0, 13)),
                ),
            ],
            Some(Ipv4Addr::new(127, 6, 0, 14)),
        );
        let said = f.take(&before);
        assert!(
            said.iter()
                .any(|l| l.contains("certifying twin `twin` (10.44.0.3:50051)")),
            "{said:?}"
        );
        assert_eq!(
            f.wanted().as_deref(),
            Some("10.44.0.3:50051 10.44.0.2:50051 127.6.0.13 127.6.0.14")
        );
        // R2: the twin laid again as vm5 at new addresses, with a new tunnel key.
        let (_, reborn) = wg_appliance_core::keys::generate_keypair_b64();
        let after = fed_front(
            vec![
                main,
                peer(
                    "twin",
                    Role::Twin,
                    &reborn,
                    nordisk_pairing::OVERLAY_BOX_B,
                    Some(Ipv4Addr::new(127, 6, 0, 15)),
                ),
            ],
            Some(Ipv4Addr::new(127, 6, 0, 14)),
        );
        let said = f.take(&after);
        assert!(
            said.iter()
                .any(|l| l.contains("certifying twin `twin` (10.44.0.3:50051)")),
            "the reborn twin keeps its overlay address and is certified again: {said:?}"
        );
        assert_eq!(
            f.wanted().as_deref(),
            Some("10.44.0.3:50051 10.44.0.2:50051 127.6.0.15 127.6.0.14"),
            "the loop for the dead twin is no longer wanted"
        );
    }

    /// The twin is certified again when the estate MOVED (a recharged main
    /// or a new twin at another address) — never twice for the same pair, and
    /// a record written before this field reads as certified.
    #[test]
    fn a_certification_is_redone_only_for_a_moved_pair() {
        let t = |twin: &str, primary: &str| certify::Target {
            twin: "twin".into(),
            primary: "main".into(),
            twin_ctl: twin.into(),
            primary_ctl: primary.into(),
            primary_for_twin: primary.into(),
            generation: String::new(),
        };
        let a = t("10.13.8.3:50051", "10.13.8.2:50051");
        let done = certify::Record {
            certified: true,
            for_target: a.key(),
            ..Default::default()
        };
        assert!(!certify_again(&done, &a), "the same pair: not redone");
        assert!(
            certify_again(&done, &t("10.13.8.3:50051", "10.13.9.140:50051")),
            "the main moved"
        );
        assert!(
            certify_again(&done, &t("10.13.9.77:50051", "10.13.8.2:50051")),
            "a new twin"
        );
        assert!(
            certify_again(&certify::Record::default(), &a),
            "never certified"
        );
        assert!(
            !certify_again(
                &certify::Record {
                    certified: true,
                    ..Default::default()
                },
                &a
            ),
            "an older record, certified: kept"
        );
    }

    /// **A certify record kept from an older build** (prod d697fa4: no `for_target`)
    /// parses, is not redone for the pair it was fed, and is adopted as that pair's — so
    /// a twin recharged later at new addresses is certified again (R2 after a
    /// keep-upgrade), never skipped as "already certified".
    #[test]
    fn an_older_record_kept_across_an_upgrade_is_adopted_for_its_pair() {
        let dir = tempfile_dir();
        let path = dir.join("certify.toml");
        std::fs::write(&path, "certified = true\nattempts = 3\ntwin = \"twin\"\nprimary = \"main\"\nwhy = \"\"\nreplication = \"10.44.0.2:50052\"\ndays_left = 300\nat_unix_ms = 1\n").unwrap();
        let key =
            gunnar_core::ssh_key::PrivateKey::from_openssh(gunnar_core::mint_ed25519().unwrap())
                .unwrap();
        let mut f = FrontRole::start(0, [4u8; 32], key, &path).unwrap();
        let (_, mk) = wg_appliance_core::keys::generate_keypair_b64();
        let (_, tk) = wg_appliance_core::keys::generate_keypair_b64();
        let main = peer(
            "main",
            Role::Primary,
            &mk,
            nordisk_pairing::OVERLAY_BOX_A,
            Some(Ipv4Addr::new(127, 7, 0, 14)),
        );
        let said = f.take(&fed_front(
            vec![
                main.clone(),
                peer(
                    "twin",
                    Role::Twin,
                    &tk,
                    nordisk_pairing::OVERLAY_BOX_B,
                    Some(Ipv4Addr::new(127, 7, 0, 13)),
                ),
            ],
            Some(Ipv4Addr::new(127, 7, 0, 14)),
        ));
        assert!(
            said.iter().any(|l| l.contains("already certified")),
            "{said:?}"
        );
        assert_eq!(
            certify::Record::load(&path).unwrap().for_target,
            "10.44.0.3:50051 10.44.0.2:50051 127.7.0.13 127.7.0.14",
            "adopted"
        );
        let said = f.take(&fed_front(
            vec![
                main,
                peer(
                    "twin",
                    Role::Twin,
                    &tk,
                    nordisk_pairing::OVERLAY_BOX_B,
                    Some(Ipv4Addr::new(127, 7, 0, 15)),
                ),
            ],
            Some(Ipv4Addr::new(127, 7, 0, 14)),
        ));
        assert!(
            said.iter()
                .any(|l| l.contains("certifying twin `twin` (10.44.0.3:50051)")),
            "a moved twin is certified again: {said:?}"
        );
    }

    fn tempfile_dir() -> PathBuf {
        let d = std::env::temp_dir().join(format!(
            "gunnar-front-{}-{}",
            std::process::id(),
            std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .unwrap()
                .as_nanos()
        ));
        std::fs::create_dir_all(&d).unwrap();
        d
    }

    /// ★ **The front serves its egress door on its overlay address, never the utility one**
    /// (the lean estate, A19 rows 10-14: main and the twin hold no public IPv4 and leave through
    /// this front; the door is the TCP forward their mail, webhooks, mirrors and SSO ride).
    #[test]
    fn the_egress_door_is_served_on_the_overlay_only() {
        assert_eq!(
            egress_door_bind(Some(nordisk_pairing::OVERLAY_FRONT)),
            Some("10.44.0.1:3128".parse().unwrap())
        );
        assert_eq!(
            egress_door_bind(None),
            None,
            "no overlay: no door (never on the utility address)"
        );
        assert_eq!(
            egress_door_bind(Some(Ipv4Addr::new(10, 6, 0, 2))),
            None,
            "only the front's overlay address"
        );
    }
}

/// ★ **The twin's certify is a named jera job** (A32-14): it was a raw `certify-twin` thread whose
/// handle was dropped, so a certify that panicked left the pair "certifying" for good, without a
/// word. Read off the source (the lib and the door binary).
#[cfg(test)]
mod a32_jobs {
    #[test]
    fn the_twins_certify_is_a_jera_job_not_a_thread() {
        for (file, src) in [
            ("front.rs", include_str!("front.rs")),
            (
                "bin/gunnar-pairing-door.rs",
                include_str!("bin/gunnar-pairing-door.rs"),
            ),
        ] {
            let code = &src[..src.find("#[cfg(test)]").unwrap_or(src.len())];
            assert!(
                code.contains("gunnar_common::jobs::start(CERTIFY_JOB"),
                "{file}: the certify starts as a jera job"
            );
            assert!(
                !code.contains(".name(\"certify-twin\".into())"),
                "{file}: the certify is still a raw thread"
            );
        }
    }
}