gunnar-server 3.0.0

The gunnar git server: SSH and smart-HTTP git transports and the gRPC control plane. The appliance half, extracted from gunnar-cli so the server code lives in its own crate.
Documentation
//! **Where the WireGuard tunnel is, for the listeners that follow it** (owner's
//! ruling 2026-09-22, amended 2026-09-24): the mutual replication listener
//! (`:50052`) answers on the outer address (eth1) for the process lifetime and,
//! once the tunnel is up, ALSO on `wg0`'s address ([`replication_extra`]).
//! `:50051` does not move.
//!
//! One value, published by whoever raises the tunnel — [`crate::wg`] at start,
//! and again when `Bootstrap.AssignTunnelAddress` raises it at RUNTIME — and
//! watched by the replication listener, which rebinds without a restart. Kept
//! outside `wg` on purpose: this module has no WireGuard in it, so a build
//! without `wg-client` still compiles the listener's rule (and its answer is
//! always "no tunnel").

use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::sync::OnceLock;

use tokio::sync::watch;

fn channel() -> &'static watch::Sender<Option<Ipv4Addr>> {
    static TX: OnceLock<watch::Sender<Option<Ipv4Addr>>> = OnceLock::new();
    TX.get_or_init(|| watch::channel(None).0)
}

/// The tunnel came up at `address` (or went away: `None`).
pub fn publish(address: Option<Ipv4Addr>) {
    channel().send_replace(address);
}

/// The tunnel address now, if there is a tunnel.
pub fn current() -> Option<Ipv4Addr> {
    *channel().borrow()
}

/// A receiver that wakes when the tunnel address changes.
pub fn subscribe() -> watch::Receiver<Option<Ipv4Addr>> {
    channel().subscribe()
}

/// **`--wg-only`, process-wide** (owner, 2026-10-04): set once at start, read
/// by the pairing — which address of the primary a twin joins.
static WG_ONLY: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);

/// Set the posture (at start).
pub fn set_wg_only(on: bool) {
    WG_ONLY.store(on, std::sync::atomic::Ordering::SeqCst);
}

/// Is this box WireGuard-only?
pub fn wg_only() -> bool {
    WG_ONLY.load(std::sync::atomic::Ordering::SeqCst)
}

/// **Is a box with this pairing seed WireGuard-only?** `Some(true)`: a seed
/// whose tunnel is WireGuard — an estate box, every box-to-box connection over
/// the overlay. `Some(false)` (another tunnel kind) or `None` (no seed: a
/// standalone appliance): only if `--wg-only` says so.
pub fn estate_is_wg_only(seed_is_wireguard: Option<bool>) -> bool {
    seed_is_wireguard == Some(true)
}

/// **The primary's address a twin joins** (`<it>:50051`): its utility
/// address, as it always was — or, `wg_only`, its OVERLAY address and nothing
/// else (a plain-IP connection to the primary is refused there).
pub fn primary_control(
    utility: Option<Ipv4Addr>,
    overlay: Option<Ipv4Addr>,
    wg_only: bool,
) -> Option<Ipv4Addr> {
    if wg_only {
        overlay
    } else {
        utility.or(overlay)
    }
}

/// **A fed `host:port` of a box, over the tunnel**: the installer (shared
/// with holger, whose boxes are not WireGuard-only) feeds a box's UTILITY
/// address — `recharge_from` = the twin's control plane. A WireGuard-only box
/// dials the same box's OVERLAY address: `pairs` is (utility, overlay) of
/// every fed peer. An address no peer holds is left as it is.
pub fn over_the_tunnel(at: &str, pairs: &[(Ipv4Addr, Ipv4Addr)]) -> String {
    let Ok(sa) = at.parse::<SocketAddr>() else {
        return at.to_string();
    };
    let IpAddr::V4(ip) = sa.ip() else {
        return at.to_string();
    };
    match pairs.iter().find(|(u, _)| *u == ip) {
        Some((_, o)) => SocketAddr::new(IpAddr::V4(*o), sa.port()).to_string(),
        None => at.to_string(),
    }
}

/// **The ONE rule for the replication listener's SECOND door** (2026-09-24).
/// The listener on `derived` (the utility address, eth1's IP, port + 1) is
/// bound for the process lifetime and never moves: a tunnel that is raised but
/// has no handshake — or goes down later — must never take away the path that
/// works. With a tunnel, a second listener answers on the tunnel address on
/// the SAME port. `None` when there is nothing to add: no tunnel; an
/// `explicit` `--grpc-replication-bind` (the operator's word, never widened);
/// or a `derived` bind that already covers the tunnel (unspecified, or the
/// tunnel address itself).
///
/// Until 2026-09-24 the rule MOVED the listener onto the tunnel the moment
/// wg0 was raised — before any handshake — and the twin was certified with
/// that host, so a tunnel that never came up left the twin nothing to follow.
pub fn replication_extra(
    derived: SocketAddr,
    explicit: bool,
    tunnel: Option<Ipv4Addr>,
) -> Option<SocketAddr> {
    let t = tunnel?;
    if explicit || derived.ip().is_unspecified() || derived.ip() == IpAddr::V4(t) {
        return None;
    }
    Some(SocketAddr::new(IpAddr::V4(t), derived.port()))
}

#[cfg(test)]
mod tests {
    use super::*;

    fn eth1() -> SocketAddr {
        "10.0.3.15:50052".parse().unwrap()
    }

    /// With a tunnel address, a SECOND door on wg0's address, same port —
    /// the utility one stays (the caller never unbinds it).
    #[test]
    fn with_a_tunnel_address_a_second_door_opens_on_wg0() {
        let t: Ipv4Addr = "10.77.0.2".parse().unwrap();
        assert_eq!(
            replication_extra(eth1(), false, Some(t)),
            Some("10.77.0.2:50052".parse().unwrap())
        );
    }

    /// With none, only the utility door.
    #[test]
    fn without_a_tunnel_there_is_only_the_utility_door() {
        assert_eq!(replication_extra(eth1(), false, None), None);
    }

    /// The operator's explicit bind is never widened; an unspecified bind
    /// already answers on wg0.
    #[test]
    fn an_explicit_or_unspecified_bind_gets_no_second_door() {
        let t = Some("10.77.0.2".parse().unwrap());
        assert_eq!(replication_extra(eth1(), true, t), None);
        assert_eq!(
            replication_extra("0.0.0.0:50052".parse().unwrap(), false, t),
            None
        );
        assert_eq!(
            replication_extra("10.77.0.2:50052".parse().unwrap(), false, t),
            None
        );
    }

    /// A recharged main is fed the twin's UTILITY address to restore from; a
    /// WireGuard-only box dials the twin's overlay address instead.
    #[test]
    fn a_fed_utility_address_is_dialled_over_the_tunnel() {
        let pairs = [
            (Ipv4Addr::new(10, 13, 8, 3), Ipv4Addr::new(10, 44, 0, 3)),
            (Ipv4Addr::new(10, 13, 8, 2), Ipv4Addr::new(10, 44, 0, 2)),
        ];
        assert_eq!(
            over_the_tunnel("10.13.8.3:50051", &pairs),
            "10.44.0.3:50051"
        );
        assert_eq!(
            over_the_tunnel("10.9.9.9:50051", &pairs),
            "10.9.9.9:50051",
            "nobody's: as fed"
        );
        assert_eq!(
            over_the_tunnel("twin.example:50051", &pairs),
            "twin.example:50051"
        );
    }

    /// An estate box (a WireGuard pairing seed) is WireGuard-only; a box with
    /// no seed — a standalone appliance — is not, unless told.
    #[test]
    fn an_estate_box_is_wg_only_and_a_standalone_appliance_is_not() {
        assert!(estate_is_wg_only(Some(true)));
        assert!(!estate_is_wg_only(Some(false)));
        assert!(!estate_is_wg_only(None));
    }

    /// **WireGuard-only**: a twin joins the primary's overlay address, never its
    /// utility one; otherwise the utility address, as before.
    #[test]
    fn under_wg_only_a_twin_joins_the_primarys_overlay_address() {
        let (u, o) = (
            Some(Ipv4Addr::new(10, 13, 8, 2)),
            Some(Ipv4Addr::new(10, 44, 0, 2)),
        );
        assert_eq!(primary_control(u, o, true), o);
        assert_eq!(
            primary_control(u, None, true),
            None,
            "no overlay: nothing to join, never the plain IP"
        );
        assert_eq!(primary_control(u, o, false), u);
        assert_eq!(primary_control(None, o, false), o);
    }

    /// A runtime publish wakes a watcher with the new address (what the
    /// listener's rebind waits on).
    #[tokio::test]
    async fn a_runtime_publish_wakes_the_watcher() {
        let mut rx = subscribe();
        let before = current();
        let t: Ipv4Addr = "10.77.0.9".parse().unwrap();
        publish(Some(t));
        rx.changed().await.expect("the watcher is woken");
        assert_eq!(*rx.borrow(), Some(t));
        assert_eq!(current(), Some(t));
        publish(before);
    }
}