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
//! **The disk-headroom gate on real stores: `Repos.Create` and the push door
//! refuse from ONE function, red first (measured, not asserted).**
//!
//! `gunnar-ssh/tests/disk_headroom.rs` proves the chain over an in-memory
//! control plane. This file proves the two things only the server can: that
//! `CoreControlPlane::create_repo` refuses under the floor and leaves NO row
//! and NO directory behind, and that `serve::front_control` — the wiring the
//! real `gunnar serve` runs — puts the disk gate on the front door. Every
//! refusal has its allow twin.

use std::sync::Arc;

use gunnar_core::{Grant, Level, NewRepo, Principal, PrincipalKind, RepoRef};
use gunnar_grpc::{ControlError, ControlPlane as _};
use gunnar_proto::RepoPath;
use gunnar_server::control_plane::CoreControlPlane;
use gunnar_server::stores::ServerStores;
use gunnar_ssh::command::GitService;
use gunnar_ssh::server::{Dispatch, DispatchError};
use gunnar_ssh::{Control, DiskFloor, DiskHeadroomGate};

struct Rig {
    _dir: tempfile::TempDir,
    stores: Arc<ServerStores>,
}

impl Rig {
    fn new() -> Rig {
        let dir = tempfile::tempdir().expect("tempdir");
        let stores = Arc::new(ServerStores::open(dir.path()).expect("stores"));
        stores
            .catalog()
            .create_namespace("team")
            .expect("namespace");
        stores
            .auth()
            .add_principal(Principal::new("owner", "owner", PrincipalKind::User).expect("legal"))
            .expect("principal");
        stores
            .auth()
            .grant(Grant::namespace("owner", "team", Level::Admin))
            .expect("grant");
        Rig { _dir: dir, stores }
    }

    fn gate(&self, bytes: u64) -> Arc<DiskHeadroomGate> {
        Arc::new(DiskHeadroomGate::new(
            self.stores.root(),
            DiskFloor { percent: 0, bytes },
        ))
    }

    /// The SAME wiring `gunnar serve` uses, so this file asserts the server's
    /// front door and not a copy of it.
    fn front(&self, gate: Arc<DiskHeadroomGate>) -> Control {
        #[cfg(feature = "metered")]
        {
            gunnar_server::serve::front_control(&self.stores, Default::default(), gate, None)
        }
        #[cfg(not(feature = "metered"))]
        {
            gunnar_server::serve::front_control(&self.stores, Default::default(), gate)
        }
    }

    fn create(&self, plane: &CoreControlPlane, name: &str) -> Result<(), ControlError> {
        plane
            .create_repo(gunnar_grpc::NewRepo {
                path: RepoPath::new("team", name),
                hash_kind: gunnar_proto::HashKind::Unspecified,
                default_branch: String::new(),
                visibility: gunnar_proto::Visibility::Private,
                description: String::new(),
                adopt: false,
            })
            .map(|_| ())
    }

    fn rows(&self) -> Vec<String> {
        self.stores
            .catalog()
            .list(Some("team"))
            .expect("list")
            .iter()
            .map(|r| r.name.clone())
            .collect()
    }
}

fn owner() -> Principal {
    Principal::new("owner", "owner", PrincipalKind::User).expect("legal")
}

#[test]
fn repos_create_under_the_floor_is_refused_with_the_gates_line_and_leaves_nothing_behind() {
    let rig = Rig::new();
    let gate = rig.gate(u64::MAX);
    let room = gate.headroom().expect("statvfs");
    let plane = CoreControlPlane::new(Arc::clone(&rig.stores)).with_headroom(Arc::clone(&gate));

    let err = rig
        .create(&plane, "proj")
        .expect_err("a full disk must refuse a new repository");
    let ControlError::ResourceExhausted(line) = &err else {
        panic!("RESOURCE_EXHAUSTED, not {err:?}");
    };
    assert_eq!(
        Some(line.as_str()),
        gate.refusal().as_deref(),
        "the RPC's line IS the gate's line — one function, both doors"
    );
    // The customer's line is plain (wiring tracer B 2026-10-08): the free bytes are the journal's.
    assert!(
        line.starts_with(gunnar_ssh::AT_FLOOR)
            && !line.contains(&gunnar_ssh::headroom::human(room.free)),
        "the line says the storage is full, without numbers: {line}"
    );
    assert!(
        rig.rows().is_empty(),
        "no catalog row may be left: {:?}",
        rig.rows()
    );
    let path = RepoRef::new("team", "proj").expect("legal");
    assert!(
        !rig.stores.repo_dir(&path).expect("a path").exists(),
        "no directory may be left behind either"
    );
}

#[test]
fn repos_create_with_room_under_the_floor_is_allowed() {
    let rig = Rig::new();
    let plane = CoreControlPlane::new(Arc::clone(&rig.stores)).with_headroom(rig.gate(1));
    rig.create(&plane, "proj")
        .expect("room, so the repository is created");
    assert_eq!(rig.rows(), ["proj"]);
}

#[test]
fn a_bare_plane_gates_nothing() {
    let rig = Rig::new();
    let plane = CoreControlPlane::new(Arc::clone(&rig.stores));
    rig.create(&plane, "proj")
        .expect("no gate wired, no refusal");
}

#[test]
fn the_front_door_serve_builds_refuses_the_push_under_the_floor_and_allows_it_with_room() {
    let rig = Rig::new();
    let path = RepoRef::new("team", "proj").expect("legal");
    rig.stores
        .create_repo(NewRepo::new(path.clone()), false)
        .expect("repo");

    // Red: the floor no disk can meet.
    let control = rig.front(rig.gate(u64::MAX));
    assert_eq!(
        control
            .plan_gates()
            .iter()
            .map(|g| g.name())
            .collect::<Vec<_>>()[0],
        "disk-headroom",
        "the disk gate is the FIRST link of the front door's chain: {control:?}"
    );
    let err = Dispatch::new(control)
        .authorise_service(&owner(), None, GitService::ReceivePack, path.clone())
        .expect_err("a full disk refuses the push before any pack byte");
    assert!(
        matches!(&err, DispatchError::Plan { line, .. } if line.starts_with(gunnar_ssh::AT_FLOOR)),
        "{err:?}"
    );

    // Green twin: room.
    Dispatch::new(rig.front(rig.gate(1)))
        .authorise_service(&owner(), None, GitService::ReceivePack, path.clone())
        .expect("room under the floor is an allowed push");

    // And a push-to-create — the first-repository door — is the same call,
    // so it is refused the same way.
    let fresh = RepoRef::new("team", "brand-new").expect("legal");
    let err = Dispatch::new(rig.front(rig.gate(u64::MAX)))
        .authorise_service(&owner(), None, GitService::ReceivePack, fresh)
        .expect_err("push-to-create on a full disk is refused before the row");
    assert!(matches!(&err, DispatchError::Plan { .. }), "{err:?}");
    assert!(
        rig.rows().iter().all(|r| r != "brand-new"),
        "{:?}",
        rig.rows()
    );
}