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

nordisk/gunnar

crates/gunnar-cli/tests/anonymous_access.rs

#![cfg(feature = "server")]

//! **`Repo.visibility` made to mean something: a stranger clones a public
//! repository, over real SSH, with stock `git` and no key at all**.
//!
//! Before this, every operation gunnar served required a registered key.
//! `Repo.visibility` was in the model and nothing honoured it, so "public"
//! meant "readable by anyone who already has an account here". That is the
//! difference between a git host for people with accounts and a place you can
//! publish something.
//!
//! ```text
//!   git clone ssh://anonymous@host/ns/repo.git      no -i, no agent, no key
//!        └─ russh auth_none ── ANONYMOUS_SSH_USER ──▶ Principal::anonymous()
//!             └─ exec ── sandbox ──▶ RepoCatalog
//!                  └─ AuthTable::may(anonymous, repo, Read)   ← the ONE ACL
//!                       └─ Visibility::Public ? serve : refuse-as-if-absent
//! ```
//!
//! # Why these tests and not unit tests of the predicate
//!
//! An authorisation rule that has only ever been unit-tested has never been
//! asked the question by a real client. `gunnar-core`'s `tests/acl.rs` pins the
//! rule and `gunnar-ssh`'s `tests/anonymous.rs` pins the handshake; this file
//! is the only place where **stock `git`**, which nobody here wrote, is the one
//! deciding whether gunnar is a git server a stranger can use.
//!
//! # Measure, never assert, and the bar is higher: this is authorisation code
//!
//! Every claim below is written with its opposite beside it, and the two
//! constants that could hide a dead check are proved dead-or-alive by
//! `breaking_the_visibility_check_in_either_direction_turns_tests_red`, which
//! is not a comment: it drives the real `AuthTable` with visibility pinned to
//! `Public` and then to `Private` and asserts that each pin breaks the half of
//! the suite it should. Neither constant survives.

mod common;

use common::{local_repo, tools_present, Harness, HarnessOptions};
use gunnar_core::{
    AuthTable, Grant, HashKind, Level, NewRepo, Principal, PrincipalKind, RepoRef, Visibility,
    ANONYMOUS_PRINCIPAL_ID,
};

/// A server with `team/project` **public** and the anonymous door open. This is
/// the shipped default configuration with one repository published.
fn published() -> Harness {
    Harness::start_with_options(
        "team",
        "project",
        HashKind::Sha1,
        HarnessOptions {
            visibility: Visibility::Public,
            ..HarnessOptions::default()
        },
    )
}

/// Seed the repository with one commit through the harness's *authenticated*
/// key, so what a stranger reads back was demonstrably put there by somebody
/// who was allowed to.
fn seed(harness: &Harness, content: &str) -> String {
    let work = harness.dir.path().join("work");
    let head = local_repo(harness, &work, HashKind::Sha1, content);
    harness
        .git(
            &work,
            &[
                "push",
                &harness.url("team", "project"),
                "HEAD:refs/heads/main",
            ],
        )
        .expect_ok("the seeding push");
    head
}

/// What `refs/heads/main` points at on the server right now, or `None`.
///
/// Applied output for the push tests: an anonymous push that was refused must
/// leave the ref where it was, and "the command failed" is not the same claim.
fn stored_head(harness: &Harness) -> Option<String> {
    let repo = harness
        .stores
        .catalog()
        .open(&RepoRef::new("team", "project").unwrap())
        .unwrap();
    let store = harness.stores.repo_store(&repo).expect("open");
    // `RefStore::iter` + `Target::Object` became `GitOps::refs` + `RefRow::oid`
    // on 2026-08-10. A symbolic row carries `oid: None`, so the `_ => None` arm
    // that used to refuse a `Target::Symbolic` is the `and_then` here — same
    // answer, and still not a hex string invented from a symref's target.
    let rows = gunnar_store::git_storage_trait::GitOps::refs(store.serve()).expect("read the refs");
    rows.iter()
        .find(|row| row.name == "refs/heads/main")
        .and_then(|row| row.oid.as_ref())
        .map(|raw| {
            gix_hash::ObjectId::try_from(raw.as_slice())
                .expect("a well-sized oid")
                .to_string()
        })
}

// ───────────────────────────────────────────────────────────────────────────
// The prize
// ───────────────────────────────────────────────────────────────────────────

/// **A stranger clones a public repository.** No key, no account, stock `git`.
///
/// Asserted on the **working tree on disk**, not on an exit code: a clone that
/// "succeeded" into an empty repository exits zero and is indistinguishable
/// from this one by status alone.
#[test]
fn an_anonymous_caller_clones_a_public_repository() {
    assert!(
        tools_present(),
        "this test requires `git` and `ssh` on PATH"
    );
    let harness = published();
    let content = "published to the world\n";
    let head = seed(&harness, content);

    let dest = harness.dir.path().join("stranger-clone");
    let out = harness.git_anonymous(
        harness.dir.path(),
        &[
            "clone",
            "--quiet",
            &harness.anonymous_url("team", "project"),
            dest.to_str().unwrap(),
        ],
    );
    out.expect_ok("a stranger with no key must be able to clone a PUBLIC repository");

    let cloned = std::fs::read_to_string(dest.join("hello.txt"))
        .expect("the anonymous clone has a working tree in it");
    assert_eq!(cloned, content, "the anonymous clone got the wrong bytes");

    let rev = harness.git(&dest, &["rev-parse", "HEAD"]);
    rev.expect_ok("git rev-parse in the anonymous clone");
    assert_eq!(
        rev.stdout.trim(),
        head,
        "the anonymous clone is not at the commit that was pushed"
    );

    // The server counted it as a stranger, not as a key that authenticated.
    let metrics = harness.server.as_ref().expect("running").metrics();
    assert!(
        metrics.connections_anonymous >= 1,
        "the anonymous clone was not counted as anonymous: {metrics:?}"
    );
}

/// **The red half of the same repository.** Made private, the identical clone
/// is refused, so the green above came from `visibility` and not from a server
/// that serves everybody.
#[test]
fn the_same_repository_made_private_refuses_the_same_anonymous_clone() {
    assert!(
        tools_present(),
        "this test requires `git` and `ssh` on PATH"
    );
    let harness = published();
    seed(&harness, "published, then withdrawn\n");
    let url = harness.anonymous_url("team", "project");

    let allowed = harness.git_anonymous(
        harness.dir.path(),
        &[
            "clone",
            "--quiet",
            &url,
            harness.dir.path().join("while-public").to_str().unwrap(),
        ],
    );
    allowed.expect_ok("while public");

    harness.set_visibility("team", "project", Visibility::Private);

    let refused = harness.git_anonymous(
        harness.dir.path(),
        &[
            "clone",
            "--quiet",
            &url,
            harness.dir.path().join("while-private").to_str().unwrap(),
        ],
    );
    refused.expect_failure("an anonymous clone of a PRIVATE repository");
    assert!(
        refused
            .stderr
            .contains("repository not found, or you do not have access"),
        "the refusal must be gunnar's, and must not say the repository exists: {}",
        refused.stderr
    );
    assert!(
        !harness.dir.path().join("while-private").exists()
            || std::fs::read_dir(harness.dir.path().join("while-private"))
                .map(|mut d| d.next().is_none())
                .unwrap_or(true),
        "a refused clone left a populated directory behind"
    );
}

/// **Read only.** A stranger cannot push to a public repository, and the ref on
/// the server is asserted **unchanged** afterwards. An exit code is a claim
/// about `git`, not about the server.
#[test]
fn an_anonymous_caller_cannot_push_to_a_public_repository() {
    assert!(
        tools_present(),
        "this test requires `git` and `ssh` on PATH"
    );
    let harness = published();
    let head = seed(&harness, "one\n");
    assert_eq!(stored_head(&harness).as_deref(), Some(head.as_str()));

    // Clone it as a stranger — which works — then try to push back.
    let dest = harness.dir.path().join("stranger");
    harness
        .git_anonymous(
            harness.dir.path(),
            &[
                "clone",
                "--quiet",
                &harness.anonymous_url("team", "project"),
                dest.to_str().unwrap(),
            ],
        )
        .expect_ok("the anonymous clone");

    std::fs::write(dest.join("hello.txt"), "vandalised\n").unwrap();
    harness
        .git_anonymous(&dest, &["add", "hello.txt"])
        .expect_ok("add");
    harness
        .git_anonymous(
            &dest,
            &[
                "-c",
                "user.name=nobody",
                "-c",
                "user.email=nobody@example.invalid",
                "commit",
                "--quiet",
                "-m",
                "vandalism",
            ],
        )
        .expect_ok("commit");

    let out = harness.git_anonymous(&dest, &["push", "origin", "HEAD:refs/heads/main"]);
    out.expect_failure("an ANONYMOUS PUSH to a public repository");
    assert!(
        out.stderr
            .contains("repository not found, or you do not have access"),
        "the refusal must be gunnar's grant check, not a transport accident: {}",
        out.stderr
    );

    // …and a force push, and a push to a brand-new ref, and a delete. A gate
    // that only refused fast-forwards onto an existing branch would be green
    // above and open below.
    for args in [
        &["push", "--force", "origin", "HEAD:refs/heads/main"][..],
        &["push", "origin", "HEAD:refs/heads/anonymous-branch"],
        &["push", "origin", ":refs/heads/main"],
    ] {
        harness
            .git_anonymous(&dest, args)
            .expect_failure(&format!("anonymous `git {}`", args.join(" ")));
    }

    // The applied output: nothing moved.
    assert_eq!(
        stored_head(&harness).as_deref(),
        Some(head.as_str()),
        "an anonymous push CHANGED a ref on the server"
    );
    let refs = {
        let repo = harness
            .stores
            .catalog()
            .open(&RepoRef::new("team", "project").unwrap())
            .unwrap();
        let store = harness.stores.repo_store(&repo).expect("open");
        gunnar_store::git_storage_trait::GitOps::refs(store.serve()).expect("read")
    };
    assert!(
        !refs.iter().any(|row| row.name.contains("anonymous-branch")),
        "an anonymous push CREATED a ref: {refs:?}"
    );
}

/// **A private repository and one that does not exist refuse byte for byte.**
///
/// One name, one server, two states. A comparison across two different names
/// cannot be byte-for-byte, because the pkt-line length prefix follows the name
/// the client itself chose (measured: `0046` against `0047`). Blanking enough
/// of the answer to make two names comparable would blank the thing being
/// measured.
///
/// The third probe is the red half: published, the identical command succeeds.
#[test]
fn a_private_repository_and_an_absent_one_refuse_identically() {
    assert!(
        tools_present(),
        "this test requires `git` and `ssh` on PATH"
    );
    let harness = published();
    let url = harness.anonymous_url("team", "hidden");
    let clone = |into: &str| {
        harness.git_anonymous(
            harness.dir.path(),
            &[
                "clone",
                "--quiet",
                &url,
                harness.dir.path().join(into).to_str().unwrap(),
            ],
        )
    };

    // 1. It does not exist.
    let absent = clone("absent");
    absent.expect_failure("cloning a repository that does not exist");

    // 2. It exists, and is private.
    harness
        .stores
        .catalog()
        .create(
            NewRepo::new(RepoRef::new("team", "hidden").unwrap())
                .hash_kind(HashKind::Sha1)
                .visibility(Visibility::Private),
        )
        .expect("create it private");
    let private = clone("private");
    private.expect_failure("cloning a private repository anonymously");

    assert_eq!(
        (private.status, private.stderr.as_str()),
        (absent.status, absent.stderr.as_str()),
        "a private repository answers a stranger differently from an absent one under the \
         identical command. That difference is an oracle: every private repository on the \
         server can be found by guessing names."
    );
    assert!(
        private
            .stderr
            .contains("repository not found, or you do not have access"),
        "both must be refusals, not two equal successes: {}",
        private.stderr
    );

    // 3. Published, the same command works, so the two equal answers above are
    // not simply "gunnar refuses everything".
    harness.set_visibility("team", "hidden", Visibility::Public);
    harness
        .stores
        .repo_store(
            &harness
                .stores
                .catalog()
                .open(&RepoRef::new("team", "hidden").unwrap())
                .unwrap(),
        )
        .expect("materialise it on disk");
    clone("published").expect_ok(
        "the identical anonymous clone must succeed once the repository is public, or the \
         indistinguishability above means nothing",
    );
}

// ───────────────────────────────────────────────────────────────────────────
// No regression for the caller that does have a key
// ───────────────────────────────────────────────────────────────────────────

/// **The regression guard.** A principal with a `Read` grant is unaffected: it
/// clones (public *and* private), it still cannot push, and it is served as
/// **itself** rather than falling through to the anonymous identity.
#[test]
fn an_authenticated_read_grant_is_unaffected() {
    assert!(
        tools_present(),
        "this test requires `git` and `ssh` on PATH"
    );
    let harness = published();
    let content = "authenticated readers still work\n";
    seed(&harness, content);

    // A second, private repository only a grant can reach.
    harness
        .stores
        .catalog()
        .create(
            NewRepo::new(RepoRef::new("team", "inner").unwrap())
                .hash_kind(HashKind::Sha1)
                .visibility(Visibility::Private),
        )
        .expect("create");
    harness
        .stores
        .repo_store(
            &harness
                .stores
                .catalog()
                .open(&RepoRef::new("team", "inner").unwrap())
                .unwrap(),
        )
        .expect("materialise");

    // A reader with its own key and a Read grant on both.
    let private = gunnar_core::mint_ed25519().expect("mint");
    let parsed = gunnar_core::ssh_key::PrivateKey::from_openssh(&private).expect("parse");
    let public = parsed.public_key().to_openssh().expect("render");
    let key_path = harness.dir.path().join("id_reader");
    std::fs::write(&key_path, &private).expect("write");
    #[cfg(unix)]
    {
        use std::os::unix::fs::PermissionsExt;
        std::fs::set_permissions(&key_path, std::fs::Permissions::from_mode(0o600)).unwrap();
    }
    harness
        .stores
        .auth()
        .add_principal(Principal::new("reader", "reader", PrincipalKind::User).unwrap())
        .expect("add");
    harness
        .stores
        .auth()
        .register_key("reader", &public, None)
        .expect("register");
    for repo in ["project", "inner"] {
        harness
            .stores
            .auth()
            .grant(Grant::repo(
                "reader",
                RepoRef::new("team", repo).unwrap(),
                Level::Read,
            ))
            .expect("grant read");
    }

    let ssh = format!(
        "ssh -i {} -o IdentitiesOnly=yes -o IdentityAgent=none -o StrictHostKeyChecking=no \
         -o UserKnownHostsFile=/dev/null -o BatchMode=yes -o LogLevel=ERROR",
        key_path.display()
    );

    // It clones the public one…
    let dest = harness.dir.path().join("reader-public");
    harness
        .git_with_ssh(
            harness.dir.path(),
            &ssh,
            &[
                "clone",
                "--quiet",
                &harness.url("team", "project"),
                dest.to_str().unwrap(),
            ],
        )
        .expect_ok("a Read grant still clones a public repository");
    assert_eq!(
        std::fs::read_to_string(dest.join("hello.txt")).expect("worktree"),
        content
    );

    // …and it reaches the PRIVATE one, which no stranger can. This is the half
    // that would break if the publickey path had been folded into the anonymous
    // one.
    let inner = harness.dir.path().join("reader-inner");
    harness
        .git_with_ssh(
            harness.dir.path(),
            &ssh,
            &[
                "clone",
                "--quiet",
                &harness.url("team", "inner"),
                inner.to_str().unwrap(),
            ],
        )
        .expect_ok("a Read grant must still reach a PRIVATE repository");
    assert!(
        harness
            .git_anonymous(
                harness.dir.path(),
                &[
                    "clone",
                    "--quiet",
                    &harness.anonymous_url("team", "inner"),
                    harness.dir.path().join("stranger-inner").to_str().unwrap(),
                ],
            )
            .status
            != 0,
        "a stranger reached the private repository the reader's grant is for"
    );

    // …and Read is still not Write.
    std::fs::write(dest.join("more.txt"), "more\n").unwrap();
    harness.git_with_ssh(&dest, &ssh, &["add", "more.txt"]);
    harness.git_with_ssh(
        &dest,
        &ssh,
        &[
            "-c",
            "user.name=r",
            "-c",
            "user.email=r@x",
            "commit",
            "--quiet",
            "-m",
            "more",
        ],
    );
    harness
        .git_with_ssh(&dest, &ssh, &["push", "origin", "HEAD:refs/heads/main"])
        .expect_failure("a Read-only principal must still be refused a push");
}

/// An unregistered **key** is still refused, with the anonymous door open
/// beside it. Offering a key is a claim to be somebody; "no key" and "a key
/// nobody knows" must not have collapsed into one another.
#[test]
fn an_unregistered_key_is_still_refused_even_for_a_public_repository() {
    assert!(
        tools_present(),
        "this test requires `git` and `ssh` on PATH"
    );
    let harness = published();
    seed(&harness, "public\n");

    let private = gunnar_core::mint_ed25519().expect("mint");
    let key_path = harness.dir.path().join("id_stranger");
    std::fs::write(&key_path, &private).expect("write");
    #[cfg(unix)]
    {
        use std::os::unix::fs::PermissionsExt;
        std::fs::set_permissions(&key_path, std::fs::Permissions::from_mode(0o600)).unwrap();
    }
    let ssh = format!(
        "ssh -i {} -o IdentitiesOnly=yes -o IdentityAgent=none -o StrictHostKeyChecking=no \
         -o UserKnownHostsFile=/dev/null -o BatchMode=yes -o LogLevel=ERROR \
         -o PreferredAuthentications=publickey",
        key_path.display()
    );
    harness
        .git_with_ssh(
            harness.dir.path(),
            &ssh,
            &[
                "clone",
                "--quiet",
                &harness.url("team", "project"),
                harness.dir.path().join("bad-key").to_str().unwrap(),
            ],
        )
        .expect_failure("an unregistered key must be refused even on a public repository");

    // The red half: the same repository, no key at all, is served.
    harness
        .git_anonymous(
            harness.dir.path(),
            &[
                "clone",
                "--quiet",
                &harness.anonymous_url("team", "project"),
                harness.dir.path().join("no-key").to_str().unwrap(),
            ],
        )
        .expect_ok("no key at all is admitted; a key nobody registered is not");
}

// ───────────────────────────────────────────────────────────────────────────
// The switch, and the control plane
// ───────────────────────────────────────────────────────────────────────────

/// `--no-anonymous` closes the door at the listener, and the same server with
/// it open is the red half.
#[test]
fn no_anonymous_refuses_a_stranger_even_on_a_public_repository() {
    assert!(
        tools_present(),
        "this test requires `git` and `ssh` on PATH"
    );
    let closed = Harness::start_with_options(
        "team",
        "project",
        HashKind::Sha1,
        HarnessOptions {
            visibility: Visibility::Public,
            allow_anonymous: false,
            ..HarnessOptions::default()
        },
    );
    seed(&closed, "not for strangers\n");
    closed
        .git_anonymous(
            closed.dir.path(),
            &[
                "clone",
                "--quiet",
                &closed.anonymous_url("team", "project"),
                closed.dir.path().join("nope").to_str().unwrap(),
            ],
        )
        .expect_failure("`allow_anonymous: false` must refuse a stranger on a PUBLIC repository");

    // …and the key still works on that same server: one door closed, not the
    // building.
    closed
        .git(
            closed.dir.path(),
            &[
                "clone",
                "--quiet",
                &closed.url("team", "project"),
                closed.dir.path().join("keyed").to_str().unwrap(),
            ],
        )
        .expect_ok("a registered key must still work with anonymous access off");

    // The same configuration with the switch ON serves the stranger.
    let open = published();
    seed(&open, "for strangers\n");
    open.git_anonymous(
        open.dir.path(),
        &[
            "clone",
            "--quiet",
            &open.anonymous_url("team", "project"),
            open.dir.path().join("yes").to_str().unwrap(),
        ],
    )
    .expect_ok("the same configuration with the switch on must serve the stranger");
}

/// **An anonymous caller reaches no gRPC control-plane call, by any route.**
///
/// Three independent statements, because one alone would be a coincidence:
///
/// 1. the control plane refuses a call with no credential at all, and there is
///    no credential a stranger could produce, because signing one needs a
///    private key whose public half is in the identity table;
/// 2. the anonymous principal cannot *get* into that table, so no key can ever
///    be registered to it. Asserted here against the same live `AuthTable` the
///    running server authenticates against;
/// 3. `gunnar-grpc`'s copy of the reserved id is the same string as
///    `gunnar-core`'s. `gunnar-grpc` deliberately does not link `gunnar-core`;
///    this crate links both, so this is where the copy is stopped from
///    drifting. `gunnar-grpc`'s `tests/auth.rs` proves the gate *refuses* that
///    id; this proves it is refusing the right one.
#[test]
fn an_anonymous_caller_reaches_no_control_plane_call() {
    assert!(
        tools_present(),
        "this test requires `git` and `ssh` on PATH"
    );

    // (3) the drift guard, first because it costs nothing.
    assert_eq!(
        gunnar_grpc::ANONYMOUS_PRINCIPAL_ID,
        ANONYMOUS_PRINCIPAL_ID,
        "`gunnar-grpc` is refusing a different string from the one `gunnar-core` reserves, so \
         the control plane's anonymous guard is watching a name nobody uses"
    );

    let harness = Harness::start_with_grpc("team", "project", HashKind::Sha1);
    harness.set_visibility("team", "project", Visibility::Public);
    seed(&harness, "public\n");

    // The stranger can clone, so the anonymous path really is open on this
    // server, and everything below is about a door that is not.
    harness
        .git_anonymous(
            harness.dir.path(),
            &[
                "clone",
                "--quiet",
                &harness.anonymous_url("team", "project"),
                harness.dir.path().join("stranger").to_str().unwrap(),
            ],
        )
        .expect_ok("the anonymous git path is open on this server");

    // (2) the anonymous principal cannot be given an identity on the LIVE
    // table this server authenticates against.
    let auth: &AuthTable = harness.stores.auth();
    assert!(auth.add_principal(Principal::anonymous()).is_err());
    assert!(auth
        .register_key(ANONYMOUS_PRINCIPAL_ID, &harness_public_key(&harness), None)
        .is_err());
    assert!(auth
        .principal(ANONYMOUS_PRINCIPAL_ID)
        .expect("read")
        .is_none());
    assert!(!auth
        .may_server(ANONYMOUS_PRINCIPAL_ID, Level::Read)
        .expect("acl"));

    // (1) every RPC, with no credential.
    let channel = harness
        .runtime
        .block_on(
            tonic::transport::Endpoint::from_shared(harness.grpc_url())
                .unwrap()
                .connect(),
        )
        .expect("connect to the control plane");
    let mut repos = gunnar_proto::repos_client::ReposClient::new(channel.clone());
    let mut refs = gunnar_proto::refs_client::RefsClient::new(channel.clone());
    let mut admin = gunnar_proto::admin_client::AdminClient::new(channel.clone());
    let mut access = gunnar_proto::access_client::AccessClient::new(channel);

    let path = Some(gunnar_proto::RepoPath {
        namespace: "team".into(),
        repo: "project".into(),
    });
    let refused: Vec<(&str, tonic::Code)> = harness.runtime.block_on(async {
        vec![
            (
                "Repos.List",
                repos
                    .list(tonic::Request::new(
                        gunnar_proto::ListReposRequest::default(),
                    ))
                    .await
                    .err()
                    .map(|s| s.code())
                    .unwrap_or(tonic::Code::Ok),
            ),
            (
                "Repos.Get",
                repos
                    .get(tonic::Request::new(gunnar_proto::GetRepoRequest {
                        path: path.clone(),
                    }))
                    .await
                    .err()
                    .map(|s| s.code())
                    .unwrap_or(tonic::Code::Ok),
            ),
            (
                "Refs.List",
                refs.list(tonic::Request::new(gunnar_proto::ListRefsRequest {
                    repo: path.clone(),
                    ..Default::default()
                }))
                .await
                .err()
                .map(|s| s.code())
                .unwrap_or(tonic::Code::Ok),
            ),
            (
                "Admin.Health",
                admin
                    .health(tonic::Request::new(gunnar_proto::HealthRequest::default()))
                    .await
                    .err()
                    .map(|s| s.code())
                    .unwrap_or(tonic::Code::Ok),
            ),
            (
                "Access.ListKeys",
                access
                    .list_keys(tonic::Request::new(gunnar_proto::ListKeysRequest::default()))
                    .await
                    .err()
                    .map(|s| s.code())
                    .unwrap_or(tonic::Code::Ok),
            ),
        ]
    });
    for (method, code) in &refused {
        assert_eq!(
            *code,
            tonic::Code::Unauthenticated,
            "{method} answered a caller with no credential with {code:?}. A stranger who can \
             clone this server's public repository must reach NO control-plane call, and \
             `Repos.Get`/`Refs.List` on a public repository is exactly the tempting exception."
        );
    }

    // The red half of the loop: the SAME calls with the harness's real
    // credential are served. Without this, a control plane that was simply down
    // would satisfy every assertion above.
    harness.runtime.block_on(async {
        let mut repos = gunnar_proto::repos_client::ReposClient::new(
            tonic::transport::Endpoint::from_shared(harness.grpc_url())
                .unwrap()
                .connect()
                .await
                .expect("connect"),
        );
        repos
            .get(harness.authed("Repos.Get", gunnar_proto::GetRepoRequest { path }))
            .await
            .expect("an authenticated caller with a server-wide Read must be served");
    });
}

/// Read the harness principal's registered `authorized_keys` line back out, so
/// the "cannot register a key to anonymous" assertion uses a key that is
/// genuinely well formed. A malformed one would be refused for the wrong
/// reason, and the guard would be blind.
fn harness_public_key(harness: &Harness) -> String {
    harness
        .stores
        .auth()
        .keys_of("tester")
        .expect("read")
        .first()
        .expect("the harness key")
        .openssh
        .clone()
}

// ───────────────────────────────────────────────────────────────────────────
// Measure, never assert: neither constant survives
// ───────────────────────────────────────────────────────────────────────────

/// **The guard on the guards.** Pin the visibility check to one answer and then
/// to the other, and assert that each pin breaks the half of this file it
/// should.
///
/// This is the identity-value trap in its exact form: `may` reads
/// `repo.visibility == Public`. A version that always said `Public` serves
/// every private repository to the internet and passes every *positive* test
/// above; a version that never said `Public` serves nobody and passes every
/// *negative* one. Only running both breaks tells them apart.
///
/// The break is applied to the **repository record the ACL reads**, not to a
/// copy of the predicate, because a rewritten predicate here would be a
/// different function from the one that ships.
#[test]
fn breaking_the_visibility_check_in_either_direction_turns_tests_red() {
    let store = gunnar_core::memory_control_store().expect("control store");
    let auth = AuthTable::new(std::sync::Arc::clone(&store)).expect("table");
    let catalog = gunnar_core::RepoCatalog::new(store).expect("catalog");
    catalog.create_namespace("team").expect("namespace");

    let open = catalog
        .create(NewRepo::new(RepoRef::new("team", "open").unwrap()).visibility(Visibility::Public))
        .expect("create");
    let shut = catalog
        .create(NewRepo::new(RepoRef::new("team", "shut").unwrap()).visibility(Visibility::Private))
        .expect("create");

    // The shipped behaviour, both halves. This is the pair the two breaks below
    // each destroy one of.
    assert!(auth
        .may(ANONYMOUS_PRINCIPAL_ID, &open, Level::Read)
        .expect("acl"));
    assert!(!auth
        .may(ANONYMOUS_PRINCIPAL_ID, &shut, Level::Read)
        .expect("acl"));

    // ── break 1: "everything is public" ────────────────────────────────────
    //
    // The private repository is now readable by a stranger. Every positive test
    // in this file stays green; this is the assertion that goes red.
    let always_public = Repo {
        visibility: Visibility::Public,
        ..shut.clone()
    };
    assert!(
        auth.may(ANONYMOUS_PRINCIPAL_ID, &always_public, Level::Read)
            .expect("acl"),
        "with visibility pinned to Public the private repository must become readable. If it \
         does not, `may` is not reading the field at all and the whole feature is decorative"
    );
    // …and that is exactly what the suite must refuse. Stated as the inverse of
    // the shipped answer, so this line IS the red demonstration:
    assert_ne!(
        auth.may(ANONYMOUS_PRINCIPAL_ID, &always_public, Level::Read)
            .expect("acl"),
        auth.may(ANONYMOUS_PRINCIPAL_ID, &shut, Level::Read)
            .expect("acl"),
        "pinning visibility to Public changed nothing, so the private-repository tests in this \
         file could not have been testing the visibility check"
    );

    // ── break 2: "nothing is public" ───────────────────────────────────────
    let never_public = Repo {
        visibility: Visibility::Private,
        ..open.clone()
    };
    assert!(
        !auth
            .may(ANONYMOUS_PRINCIPAL_ID, &never_public, Level::Read)
            .expect("acl"),
        "with visibility pinned to Private the public repository must stop being readable"
    );
    assert_ne!(
        auth.may(ANONYMOUS_PRINCIPAL_ID, &never_public, Level::Read)
            .expect("acl"),
        auth.may(ANONYMOUS_PRINCIPAL_ID, &open, Level::Read)
            .expect("acl"),
        "pinning visibility to Private changed nothing, so the public-clone tests in this file \
         could not have been testing the visibility check"
    );

    // Neither break touches the level: write is refused under both pins, so
    // "public" never becomes "writable" by any route through this field.
    for repo in [&open, &shut, &always_public, &never_public] {
        for level in [Level::Write, Level::Admin] {
            assert!(!auth.may(ANONYMOUS_PRINCIPAL_ID, repo, level).expect("acl"));
        }
    }
}

use gunnar_core::Repo;