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

nordisk/gunnar-ui

crates/gunnar-ui-server/src/account_model.rs

//! **The owner's account model (2026-10-03), the console's half**, driven end
//! to end against a gunnar that keeps a grant table and a namespace list.
//!
//! Nothing is bought and nothing is a flag an operator sets:
//!
//! 1. every account's namespace and events are private until its admin says
//!    otherwise (`PATCH /api/account/namespaces/{ns}/settings`), and a session
//!    that is not the account's admin is refused before anything is signed;
//! 2. a repository's visibility is the account admin's (gunnar's default for
//!    every account);
//! 3. the account admin enrolls NEW employees (`POST /api/account/employees`):
//!    gunnar's `Accounts.EnrollEmployee` and an invite pinned to the new name;
//! 4. `/api/namespaces/{ns}/page` — what a visitor sees at `/<ns>` — lists only
//!    the public repositories of a private namespace, and a private namespace
//!    with none answers exactly as one that does not exist.
//!
//! Tests only, and its own module for the reason [`crate::tenant_import`] is:
//! what is asserted is a property of the whole path — session → bound
//! principal → gunnar's grant table → `Repos.SetVisibility` — not of one
//! handler.
//!
//! # What gunnar HEARD, and from whom, is the assertion
//!
//! Since 2026-10-04 (thin layer) the console signs `Repos.SetVisibility`,
//! `Repos.SetNamespaceSettings`, `Accounts.EnrollEmployee` and
//! `Accounts.ListEmployees` AS THE PERSON, and the fake below decides the way
//! gunnar does: the console's key holds a server-wide `Admin`; a person is the
//! account admin of their own namespace and of any namespace they hold `Admin`
//! over, and is refused in gunnar's words otherwise. So every row asserts two
//! logs: `asked` (every such call, and WHOSE key signed it) and `heard` (what
//! gunnar actually changed) — a refusal is a call signed by the person that
//! changed nothing.

use std::collections::BTreeMap;
use std::sync::{Arc, Mutex};

use tonic::{Request, Response, Status};
use tower::ServiceExt as _;

use crate::api::mirror_gate::Imports;
use crate::pb;
use crate::role::Role;

const CONSOLE_SEED: [u8; 32] = [53u8; 32];
const GUEST_SEED: [u8; 32] = [54u8; 32];

fn signer_of(seed: [u8; 32]) -> crate::signer::Signer {
    crate::signer::Signer::new(ssh_key::PrivateKey::from(
        ssh_key::private::Ed25519Keypair::from_seed(&seed),
    ))
    .expect("a fixed seed makes a signer")
}

fn console_signer() -> crate::signer::Signer {
    signer_of(CONSOLE_SEED)
}

/// Did the CONSOLE'S key sign this call? The fake answers that key with the
/// whole server, as gunnar answers a server-wide admin; anything else (the
/// guest, a tenant) is an outsider and is narrowed, as gunnar narrows it.
fn by_console<T>(request: &Request<T>) -> bool {
    let console = console_signer().fingerprint().to_owned();
    request
        .metadata()
        .get(crate::signer::CREDENTIAL_METADATA_KEY)
        .and_then(|v| v.to_str().ok())
        .and_then(|v| v.split_whitespace().nth(1).map(str::to_owned))
        .is_some_and(|fp| fp == console)
}

// ───────────────────────────────────────────────────────────────────────────
// The fake
// ───────────────────────────────────────────────────────────────────────────

#[derive(Clone, Default)]
struct Fake {
    /// Every change gunnar MADE, as `<ns>/<repo> -> <visibility>` and friends.
    heard: Arc<Mutex<Vec<String>>>,
    /// Every account-admin call, as `<Method> by <who>` — `console`, the
    /// principal whose key signed it, or `stranger`.
    asked: Arc<Mutex<Vec<String>>>,
    /// Tenant-key fingerprint → principal, as `Access.AddKey` enrolled them.
    keys: Arc<Mutex<BTreeMap<String, String>>>,
    /// Every `Access.Check`, as `Access.Check <ns> by <who>`.
    checks: Arc<Mutex<Vec<String>>>,
    grants: Arc<Mutex<Vec<pb::Grant>>>,
    /// id → kind.
    principals: Arc<Mutex<BTreeMap<String, i32>>>,
    /// The namespaces gunnar lists → (public, events public).
    namespaces: Arc<Mutex<BTreeMap<String, (bool, bool)>>>,
    /// Employee → account.
    employees: Arc<Mutex<BTreeMap<String, String>>>,
    /// Members the account's admin gave Admin over the account (round 5.3.1): gunnar's
    /// WhoAmI answers them `admin-appointed`.
    appointed: Arc<Mutex<std::collections::BTreeSet<String>>>,
    /// `<ns>/<repo>` → visibility.
    repos: Arc<Mutex<BTreeMap<String, i32>>>,
    /// `<ns>/<repo>` → the "encrypted" flag, as `Repos.SetEncrypted` set it.
    encrypted: Arc<Mutex<BTreeMap<String, bool>>>,
    /// Invites gunnar minted: (principal, server level, who made it).
    /// A separate invite call gunnar would have heard: the deleted
    /// `Access.CreateInvite` (IDENTITY-HOST) — there is none any more, and the
    /// rows assert it stays empty.
    invites: Arc<Mutex<Vec<(String, i32, String)>>>,
}

impl Fake {
    fn heard(&self) -> Vec<String> {
        self.heard.lock().expect("heard").clone()
    }
    fn asked(&self) -> Vec<String> {
        self.asked.lock().expect("asked").clone()
    }
    /// Whose key signed this request.
    fn who<T>(&self, request: &Request<T>) -> String {
        if by_console(request) {
            return "console".to_owned();
        }
        let fingerprint = request
            .metadata()
            .get(crate::signer::CREDENTIAL_METADATA_KEY)
            .and_then(|v| v.to_str().ok())
            .and_then(|v| v.split_whitespace().nth(1).map(str::to_owned))
            .unwrap_or_default();
        self.keys
            .lock()
            .expect("keys")
            .get(&fingerprint)
            .cloned()
            .unwrap_or_else(|| "stranger".to_owned())
    }
    /// gunnar's account-admin rule (`require(namespace, Admin)`): the console's
    /// server-wide Admin, the account's own principal, or a namespace Admin
    /// grant. Refused in gunnar's words, after noting who asked.
    /// Admin over `account`, by gunnar's rule.
    fn is_admin(&self, who: &str, account: &str) -> bool {
        who == "console"
            || who == account
            || self.grants.lock().expect("grants").iter().any(|g| {
                g.principal_id == who
                    // A namespace Admin grant, or a server-wide one (the box's
                    // administrator is every account's admin).
                    && (g.namespace == account || g.namespace.is_empty())
                    && g.repo.is_empty()
                    && g.level == pb::GrantLevel::Admin as i32
            })
    }
    fn account_admin<T>(&self, request: &Request<T>, method: &str, account: &str, what: &str) -> Result<(), Status> {
        let who = self.who(request);
        self.asked
            .lock()
            .expect("asked")
            .push(format!("{method} by {who}"));
        let admin = self.is_admin(&who, account);
        if admin {
            Ok(())
        } else {
            Err(Status::permission_denied(format!(
                "principal {who} has no admin grant on namespace:{account}: {what} {account} \
                 are the account admin's decision (admin of account {account})"
            )))
        }
    }
    fn user(&self, id: &str) {
        self.principals
            .lock()
            .expect("principals")
            .insert(id.to_owned(), pb::PrincipalKind::User as i32);
    }
    fn grant(&self, principal: &str, ns: &str, repo: &str, level: pb::GrantLevel) {
        self.grants.lock().expect("grants").push(pb::Grant {
            principal_id: principal.into(),
            namespace: ns.into(),
            repo: repo.into(),
            level: level as i32,
            granted_at_unix_ms: 1,
        });
    }
    fn namespace(&self, ns: &str, public: bool) {
        self.namespaces
            .lock()
            .expect("namespaces")
            .insert(ns.to_owned(), (public, false));
    }
    fn repo_with(&self, ns: &str, repo: &str, visibility: pb::Visibility) {
        self.repo(ns, repo);
        self.repos
            .lock()
            .expect("repos")
            .insert(format!("{ns}/{repo}"), visibility as i32);
    }
    fn repo(&self, ns: &str, repo: &str) {
        self.namespaces
            .lock()
            .expect("namespaces")
            .entry(ns.to_owned())
            .or_insert((false, false));
        self.repos
            .lock()
            .expect("repos")
            .insert(format!("{ns}/{repo}"), pb::Visibility::Private as i32);
    }
    fn repo_out(path: pb::RepoPath, visibility: i32) -> pb::Repo {
        pb::Repo {
            path: Some(path),
            visibility,
            ..Default::default()
        }
    }
}

// Only the calls this path makes; the generated default stubs answer every
// other one `UNIMPLEMENTED`.
#[tonic::async_trait]
impl pb::access_server::Access for Fake {
    async fn add_key(
        &self,
        request: Request<pb::AddKeyRequest>,
    ) -> Result<Response<pb::AddKeyResponse>, Status> {
        if !by_console(&request) {
            return Err(Status::permission_denied("only the console enrols here"));
        }
        let asked = request.into_inner();
        let parsed = ssh_key::PublicKey::from_openssh(&asked.openssh_public_key)
            .map_err(|e| Status::invalid_argument(format!("not an OpenSSH public key: {e}")))?;
        let fingerprint = parsed.fingerprint(ssh_key::HashAlg::Sha256).to_string();
        self.keys
            .lock()
            .expect("keys")
            .insert(fingerprint.clone(), asked.principal_id.clone());
        Ok(Response::new(pb::AddKeyResponse {
            key: Some(pb::PublicKey {
                fingerprint,
                algo: "ssh-ed25519".to_owned(),
                principal_id: asked.principal_id,
                ..Default::default()
            }),
        }))
    }
    async fn check(
        &self,
        request: Request<pb::CheckRequest>,
    ) -> Result<Response<pb::CheckResponse>, Status> {
        let who = self.who(&request);
        let asked = request.into_inner();
        self.checks
            .lock()
            .expect("checks")
            .push(format!("Access.Check {} by {who}", asked.namespace));
        // Only the account-admin question is modelled; it is the only one
        // this path asks.
        // A repository's Admin (the Danger zone's question, 2026-10-28): the account
        // admin's, or a repository Admin grant.
        let repo_admin = !asked.repo.is_empty()
            && self.grants.lock().expect("grants").iter().any(|g| {
                g.principal_id == who
                    && g.namespace == asked.namespace
                    && g.repo == asked.repo
                    && g.level == pb::GrantLevel::Admin as i32
            });
        let allowed = asked.level == pb::GrantLevel::Admin as i32
            && (self.is_admin(&who, &asked.namespace) || repo_admin);
        Ok(Response::new(pb::CheckResponse {
            allowed,
            reason: if allowed {
                String::new()
            } else {
                format!("principal {who} has no admin grant on namespace:{}", asked.namespace)
            },
        }))
    }
    async fn list_principals(
        &self,
        _: Request<pb::ListPrincipalsRequest>,
    ) -> Result<Response<pb::ListPrincipalsResponse>, Status> {
        let principals = self
            .principals
            .lock()
            .expect("principals")
            .iter()
            .map(|(id, kind)| pb::Principal {
                id: id.clone(),
                name: id.clone(),
                kind: *kind,
                created_at_unix_ms: 1,
            })
            .collect();
        Ok(Response::new(pb::ListPrincipalsResponse {
            principals,
            next_page_token: String::new(),
            ..Default::default()
        }))
    }
    /// gunnar's rule since 2026-10-07 for a request naming a namespace: that
    /// namespace's admin's, answered for that namespace only.
    async fn list_grants(
        &self,
        request: Request<pb::ListGrantsRequest>,
    ) -> Result<Response<pb::ListGrantsResponse>, Status> {
        let who = self.who(&request);
        let asked = request.into_inner();
        let grants = self.grants.lock().expect("grants").clone();
        // The un-narrowed listing answers as it always did in this fake (the
        // rows above lean on it); the namespace question is modelled exactly.
        if who == "console" || asked.namespace.is_empty() {
            return Ok(Response::new(pb::ListGrantsResponse { grants }));
        }
        if !self.is_admin(&who, &asked.namespace) {
            return Err(Status::permission_denied(format!(
                "principal {who} has no admin grant on namespace:{}",
                asked.namespace
            )));
        }
        Ok(Response::new(pb::ListGrantsResponse {
            grants: grants
                .into_iter()
                .filter(|g| g.namespace == asked.namespace && (asked.repo.is_empty() || g.repo == asked.repo))
                .collect(),
        }))
    }
    /// **Who am I** (2026-10-07: and which account do I act in). The fake
    /// keeps no server grants for persons, so it answers `Admin` and the
    /// console's role stays the cookie's (the console takes the lower of the
    /// two) — every row above reads as it did when this was unimplemented.
    /// The account: a member's employer, else a namespace of the person's own
    /// name — `admin` with members, `individual` without.
    async fn who_am_i(
        &self,
        request: Request<pb::WhoAmIRequest>,
    ) -> Result<Response<pb::WhoAmIResponse>, Status> {
        let who = self.who(&request);
        let employer = self.employees.lock().expect("employees").get(&who).cloned();
        let appointed = self.appointed.lock().expect("appointed").contains(&who);
        let (account, role) = match employer {
            Some(account) if appointed => (account, "admin-appointed"),
            Some(account) => (account, "employee"),
            None if self.namespaces.lock().expect("namespaces").contains_key(&who) => {
                let members = self.employees.lock().expect("employees").values().any(|a| *a == who);
                (who.clone(), if members { "admin" } else { "individual" })
            }
            None => (String::new(), ""),
        };
        Ok(Response::new(pb::WhoAmIResponse {
            principal: Some(pb::Principal { id: who.clone(), name: who.clone(), kind: pb::PrincipalKind::User as i32, created_at_unix_ms: 1 }),
            server_level: pb::GrantLevel::Admin as i32,
            account_admin: account.clone(),
            account,
            account_role: role.to_owned(),
            ..Default::default()
        }))
    }
    /// gunnar's rule: Admin over the repository (the account's admin, or the
    /// console) grants on it.
    async fn grant(
        &self,
        request: Request<pb::GrantRequest>,
    ) -> Result<Response<pb::GrantResponse>, Status> {
        let who = self.who(&request);
        let asked = request.into_inner();
        self.asked.lock().expect("asked").push(format!("Access.Grant by {who}"));
        if !self.is_admin(&who, &asked.namespace) {
            return Err(Status::permission_denied(format!(
                "principal {who} has no admin grant on repo:{}/{}",
                asked.namespace, asked.repo
            )));
        }
        let level = pb::GrantLevel::try_from(asked.level).unwrap_or(pb::GrantLevel::Unspecified);
        self.grants
            .lock()
            .expect("grants")
            .retain(|g| !(g.principal_id == asked.principal_id && g.namespace == asked.namespace && g.repo == asked.repo));
        self.grant(&asked.principal_id, &asked.namespace, &asked.repo, level);
        self.heard.lock().expect("heard").push(format!(
            "grant {} {} on {}/{}",
            asked.principal_id,
            level.as_str_name(),
            asked.namespace,
            asked.repo
        ));
        Ok(Response::new(pb::GrantResponse { grant: None }))
    }
    async fn revoke(
        &self,
        request: Request<pb::RevokeRequest>,
    ) -> Result<Response<pb::RevokeResponse>, Status> {
        let who = self.who(&request);
        let asked = request.into_inner();
        self.asked.lock().expect("asked").push(format!("Access.Revoke by {who}"));
        if !self.is_admin(&who, &asked.namespace) {
            return Err(Status::permission_denied(format!(
                "principal {who} has no admin grant on repo:{}/{}",
                asked.namespace, asked.repo
            )));
        }
        self.grants
            .lock()
            .expect("grants")
            .retain(|g| !(g.principal_id == asked.principal_id && g.namespace == asked.namespace && g.repo == asked.repo));
        self.heard
            .lock()
            .expect("heard")
            .push(format!("revoke {} on {}/{}", asked.principal_id, asked.namespace, asked.repo));
        Ok(Response::new(pb::RevokeResponse {}))
    }
    async fn list_group_members(
        &self,
        _: Request<pb::ListGroupMembersRequest>,
    ) -> Result<Response<pb::ListGroupMembersResponse>, Status> {
        Ok(Response::new(pb::ListGroupMembersResponse {
            members: Vec::new(),
        }))
    }
}

#[tonic::async_trait]
impl pb::repos_server::Repos for Fake {
    /// gunnar's rule for a new repository (`require(namespace, Admin)`): the
    /// account admin, in their own account or one they hold Admin over.
    async fn create(
        &self,
        request: Request<pb::CreateRepoRequest>,
    ) -> Result<Response<pb::CreateRepoResponse>, Status> {
        let path = request.get_ref().path.clone().unwrap_or_default();
        self.account_admin(&request, "Repos.Create", &path.namespace, "new repositories in account")?;
        let visibility = match request.get_ref().visibility {
            v if v == pb::Visibility::Public as i32 => v,
            _ => pb::Visibility::Private as i32,
        };
        let key = format!("{}/{}", path.namespace, path.repo);
        if self.repos.lock().expect("repos").contains_key(&key) {
            return Err(Status::already_exists(key));
        }
        self.heard.lock().expect("heard").push(format!("created {key} {}", crate::api::visibility_name(visibility)));
        self.repo_with(&path.namespace, &path.repo, if visibility == pb::Visibility::Public as i32 { pb::Visibility::Public } else { pb::Visibility::Private });
        Ok(Response::new(pb::CreateRepoResponse { repo: Some(Fake::repo_out(path, visibility)) }))
    }
    // Encrypted repositories (gunnar 2026-10-05): the flag is the ACCOUNT
    // admin's, the same rule as visibility (gunnar's `account_admin_only_for`).
    async fn set_encrypted(
        &self,
        request: tonic::Request<pb::SetEncryptedRequest>,
    ) -> Result<tonic::Response<pb::SetEncryptedResponse>, tonic::Status> {
        let account = request
            .get_ref()
            .path
            .as_ref()
            .map(|p| p.namespace.clone())
            .unwrap_or_default();
        self.account_admin(&request, "Repos.SetEncrypted", &account, "encrypted repositories in account")?;
        let asked = request.into_inner();
        let path = asked.path.unwrap_or_default();
        let key = format!("{}/{}", path.namespace, path.repo);
        self.heard.lock().expect("heard").push(format!(
            "{key} -> {}",
            if asked.encrypted { "encrypted" } else { "not encrypted" }
        ));
        self.encrypted.lock().expect("encrypted").insert(key.clone(), asked.encrypted);
        let visibility = self.repos.lock().expect("repos").get(&key).copied().unwrap_or_default();
        let mut repo = Fake::repo_out(path, visibility);
        repo.encrypted = asked.encrypted;
        Ok(Response::new(pb::SetEncryptedResponse { repo: Some(repo) }))
    }
    async fn recipient_keys(
        &self,
        _: tonic::Request<pb::RecipientKeysRequest>,
    ) -> Result<tonic::Response<pb::RecipientKeysResponse>, tonic::Status> {
        Err(tonic::Status::unimplemented("Repos.RecipientKeys"))
    }

    /// gunnar's rule for `Repos.Delete`: Admin over the repository or its
    /// namespace (the account admin, or a repository admin grant).
    async fn delete(
        &self,
        request: Request<pb::DeleteRepoRequest>,
    ) -> Result<Response<pb::DeleteRepoResponse>, Status> {
        let who = self.who(&request);
        let path = request.get_ref().path.clone().unwrap_or_default();
        self.asked.lock().expect("asked").push(format!("Repos.Delete by {who}"));
        let repo_admin = self.grants.lock().expect("grants").iter().any(|g| {
            g.principal_id == who
                && g.namespace == path.namespace
                && g.repo == path.repo
                && g.level == pb::GrantLevel::Admin as i32
        });
        if !(self.is_admin(&who, &path.namespace) || repo_admin) {
            return Err(Status::permission_denied(format!(
                "principal {who} has no admin grant on repo:{}/{}",
                path.namespace, path.repo
            )));
        }
        let key = format!("{}/{}", path.namespace, path.repo);
        self.heard.lock().expect("heard").push(format!("{key} deleted"));
        self.repos.lock().expect("repos").remove(&key);
        Ok(Response::new(pb::DeleteRepoResponse::default()))
    }

    /// gunnar's rule for `Repos.Undelete`: the delete's, Admin over the repository
    /// or its namespace.
    async fn undelete(
        &self,
        request: Request<pb::UndeleteRepoRequest>,
    ) -> Result<Response<pb::UndeleteRepoResponse>, Status> {
        let who = self.who(&request);
        let path = request.get_ref().path.clone().unwrap_or_default();
        self.asked.lock().expect("asked").push(format!("Repos.Undelete by {who}"));
        if !self.is_admin(&who, &path.namespace) {
            return Err(Status::permission_denied(format!(
                "principal {who} has no admin grant on repo:{}/{}",
                path.namespace, path.repo
            )));
        }
        let key = format!("{}/{}", path.namespace, path.repo);
        self.heard.lock().expect("heard").push(format!("{key} undeleted"));
        self.repos.lock().expect("repos").insert(key, pb::Visibility::Private as i32);
        Ok(Response::new(pb::UndeleteRepoResponse {
            repo: Some(Fake::repo_out(path, pb::Visibility::Private as i32)),
        }))
    }

    /// gunnar's rule for one namespace's `Repos.ListDeleted`: Read on it. The
    /// fake models the account admin's, and answers gunnar's default window.
    async fn list_deleted(
        &self,
        request: Request<pb::ListDeletedRequest>,
    ) -> Result<Response<pb::ListDeletedResponse>, Status> {
        let who = self.who(&request);
        let namespace = request.get_ref().namespace.clone();
        if namespace.is_empty() || !self.is_admin(&who, &namespace) {
            return Err(Status::permission_denied(format!("principal {who} may not list {namespace}")));
        }
        Ok(Response::new(pb::ListDeletedResponse {
            default_retention_days: 30,
            ..Default::default()
        }))
    }

    async fn get(
        &self,
        request: Request<pb::GetRepoRequest>,
    ) -> Result<Response<pb::GetRepoResponse>, Status> {
        let path = request.into_inner().path.unwrap_or_default();
        let key = format!("{}/{}", path.namespace, path.repo);
        let visibility = *self
            .repos
            .lock()
            .expect("repos")
            .get(&key)
            .ok_or_else(|| Status::not_found(key.clone()))?;
        Ok(Response::new(pb::GetRepoResponse {
            repo: Some(Fake::repo_out(path, visibility)),
        }))
    }
    async fn list_namespaces(
        &self,
        request: Request<pb::ListNamespacesRequest>,
    ) -> Result<Response<pb::ListNamespacesResponse>, Status> {
        let who = self.who(&request);
        let employees = self.employees.lock().expect("employees").clone();
        let namespaces = self
            .namespaces
            .lock()
            .expect("namespaces")
            .iter()
            // gunnar narrows: what the caller may read (admin over it, here),
            // and the public ones.
            .filter(|(name, (public, _))| *public || self.is_admin(&who, name))
            .map(|(name, (public, events_public))| pb::Namespace {
                name: name.clone(),
                public: *public,
                events_public: *events_public,
                employees: employees.values().filter(|a| *a == name).count() as u32,
                ..Default::default()
            })
            .collect();
        Ok(Response::new(pb::ListNamespacesResponse {
            namespaces,
            next_page_token: String::new(),
        }))
    }
    async fn list(
        &self,
        request: Request<pb::ListReposRequest>,
    ) -> Result<Response<pb::ListReposResponse>, Status> {
        let everything = by_console(&request);
        let namespace = request.into_inner().namespace;
        let repos = self
            .repos
            .lock()
            .expect("repos")
            .iter()
            .filter(|(key, _)| namespace.is_empty() || key.split('/').next() == Some(namespace.as_str()))
            .filter(|(_, visibility)| everything || **visibility == pb::Visibility::Public as i32)
            .map(|(key, visibility)| {
                let (ns, name) = key.split_once('/').expect("ns/name");
                Fake::repo_out(pb::RepoPath { namespace: ns.into(), repo: name.into() }, *visibility)
            })
            .collect();
        Ok(Response::new(pb::ListReposResponse {
            repos,
            next_page_token: String::new(),
        }))
    }
    async fn set_namespace_settings(
        &self,
        request: Request<pb::SetNamespaceSettingsRequest>,
    ) -> Result<Response<pb::SetNamespaceSettingsResponse>, Status> {
        let account = request.get_ref().name.clone();
        self.account_admin(&request, "Repos.SetNamespaceSettings", &account, "the settings of account")?;
        let asked = request.into_inner();
        let mut namespaces = self.namespaces.lock().expect("namespaces");
        let row = namespaces
            .get_mut(&asked.name)
            .ok_or_else(|| Status::not_found(asked.name.clone()))?;
        if let Some(public) = asked.public {
            row.0 = public;
        }
        if let Some(events_public) = asked.events_public {
            row.1 = events_public;
        }
        self.heard.lock().expect("heard").push(format!(
            "settings {} public={} events={}",
            asked.name, row.0, row.1
        ));
        Ok(Response::new(pb::SetNamespaceSettingsResponse {
            namespace: Some(pb::Namespace {
                name: asked.name,
                public: row.0,
                events_public: row.1,
                ..Default::default()
            }),
        }))
    }
    async fn set_default_branch(
        &self,
        request: Request<pb::SetDefaultBranchRequest>,
    ) -> Result<Response<pb::SetDefaultBranchResponse>, Status> {
        let path = request.into_inner().path.unwrap_or_default();
        Ok(Response::new(pb::SetDefaultBranchResponse {
            repo: Some(Fake::repo_out(path, pb::Visibility::Private as i32)),
        }))
    }
    async fn set_visibility(
        &self,
        request: Request<pb::SetVisibilityRequest>,
    ) -> Result<Response<pb::SetVisibilityResponse>, Status> {
        let account = request
            .get_ref()
            .path
            .as_ref()
            .map(|p| p.namespace.clone())
            .unwrap_or_default();
        self.account_admin(&request, "Repos.SetVisibility", &account, "the repositories of account")?;
        let asked = request.into_inner();
        let path = asked.path.unwrap_or_default();
        let key = format!("{}/{}", path.namespace, path.repo);
        self.heard.lock().expect("heard").push(format!(
            "{key} -> {}",
            crate::api::visibility_name(asked.visibility)
        ));
        self.repos
            .lock()
            .expect("repos")
            .insert(key, asked.visibility);
        Ok(Response::new(pb::SetVisibilityResponse {
            repo: Some(Fake::repo_out(path, asked.visibility)),
        }))
    }
}

/// gunnar's `Usage.Read`: on the namespace's own Read — its owner, a member
/// of the account (enrolment's Read), or the console.
#[tonic::async_trait]
impl pb::usage_server::Usage for Fake {
    async fn read(
        &self,
        request: Request<pb::ReadUsageRequest>,
    ) -> Result<Response<pb::ReadUsageResponse>, Status> {
        let who = self.who(&request);
        let ns = request.into_inner().namespace;
        self.asked.lock().expect("asked").push(format!("Usage.Read {ns} by {who}"));
        let member = self.employees.lock().expect("employees").get(&who) == Some(&ns);
        if !(self.is_admin(&who, &ns) || member) {
            return Err(Status::permission_denied(format!("principal {who} has no read grant on namespace:{ns}")));
        }
        if !self.namespaces.lock().expect("namespaces").contains_key(&ns) {
            return Err(Status::not_found(format!("namespace {ns} is not in the catalog")));
        }
        Ok(Response::new(pb::ReadUsageResponse {
            usage: Some(pb::NamespaceUsage { pack_bytes: 3 << 20, ..Default::default() }),
            ..Default::default()
        }))
    }
}

#[tonic::async_trait]
impl pb::accounts_server::Accounts for Fake {
    async fn enroll_employee(
        &self,
        request: Request<pb::EnrollEmployeeRequest>,
    ) -> Result<Response<pb::EnrollEmployeeResponse>, Status> {
        let account = request.get_ref().account.clone();
        self.account_admin(&request, "Accounts.EnrollEmployee", &account, "the employees of account")?;
        let asked = request.into_inner();
        if asked.count > 1 {
            // Seats: no names, nobody created — gunnar's rule, in miniature.
            self.heard.lock().expect("heard").push(format!(
                "enroll {} seats {} room {}",
                asked.account, asked.count, asked.room_bytes
            ));
            return Ok(Response::new(pb::EnrollEmployeeResponse {
                room_bytes: asked.room_bytes,
                seats: asked.count,
                seats_open: asked.count,
                need_bytes: asked.room_bytes * u64::from(asked.count),
                ..Default::default()
            }));
        }
        if self.principals.lock().expect("principals").contains_key(&asked.id) {
            return Err(Status::already_exists(format!(
                "principal {} already exists, and an existing principal can never become an \
                 employee",
                asked.id
            )));
        }
        self.user(&asked.id);
        self.employees
            .lock()
            .expect("employees")
            .insert(asked.id.clone(), asked.account.clone());
        self.heard.lock().expect("heard").push(match (asked.from_seat, asked.invite) {
            (true, true) => format!("enroll {}/{} from a seat, invited", asked.account, asked.id),
            (true, false) => format!("enroll {}/{} from a seat", asked.account, asked.id),
            (false, true) => format!("enroll {}/{}, invited", asked.account, asked.id),
            (false, false) => format!("enroll {}/{}", asked.account, asked.id),
        });
        let (invite_id, invite_secret) = if asked.invite {
            (format!("invite-of-{}", asked.id), format!("gunnars-secret-for-{}", asked.id))
        } else {
            (String::new(), String::new())
        };
        Ok(Response::new(pb::EnrollEmployeeResponse {
            invite_id,
            invite_secret,
            invite_expires_at_unix_ms: if asked.invite { 9_000_000_000_000 } else { 0 },
            principal: Some(pb::Principal {
                id: asked.id.clone(),
                name: asked.name,
                kind: pb::PrincipalKind::User as i32,
                created_at_unix_ms: 1,
            }),
            room_bytes: asked.room_bytes,
            ..Default::default()
        }))
    }
    async fn list_employees(
        &self,
        request: Request<pb::ListEmployeesRequest>,
    ) -> Result<Response<pb::ListEmployeesResponse>, Status> {
        let account = request.get_ref().account.clone();
        self.account_admin(&request, "Accounts.ListEmployees", &account, "the employees of account")?;
        let employees = self
            .employees
            .lock()
            .expect("employees")
            .iter()
            .filter(|(_, a)| **a == account)
            .map(|(id, _)| pb::Principal {
                id: id.clone(),
                name: id.clone(),
                kind: pb::PrincipalKind::User as i32,
                created_at_unix_ms: 1,
            })
            .collect();
        Ok(Response::new(pb::ListEmployeesResponse { employees, ..Default::default() }))
    }
}

// ───────────────────────────────────────────────────────────────────────────
// The console
// ───────────────────────────────────────────────────────────────────────────

struct Rig {
    router: axum::Router,
    webauthn: Arc<crate::auth::Webauthn>,
    fake: Fake,
}

impl Rig {
    /// A passkey session for `user` at `role`, bound to the principal of the
    /// same name.
    fn session(&self, user: &str, role: Role) -> String {
        self.fake.user(user);
        self.webauthn
            .plant_bound_credential_for_tests(user, role, user);
        self.webauthn.make_session(user, role, true)
    }

    async fn send(
        &self,
        method: &str,
        uri: &str,
        cookie: &str,
        body: Option<serde_json::Value>,
    ) -> (http::StatusCode, serde_json::Value) {
        let request = http::Request::builder()
            .method(method)
            .uri(uri)
            .header(http::header::CONTENT_TYPE, "application/json")
            .header(http::header::ORIGIN, "https://console.example")
            .header(
                http::header::COOKIE,
                format!("{}={cookie}", crate::auth::SESSION_COOKIE),
            )
            .body(axum::body::Body::from(
                body.map(|b| b.to_string()).unwrap_or_default(),
            ))
            .expect("a request");
        let response = self
            .router
            .clone()
            .oneshot(request)
            .await
            .expect("the console answers");
        let status = response.status();
        let bytes = axum::body::to_bytes(response.into_body(), 4 << 20)
            .await
            .expect("a body");
        let text = String::from_utf8_lossy(&bytes).into_owned();
        let json =
            serde_json::from_str(&text).unwrap_or_else(|_| serde_json::Value::String(text.clone()));
        (status, json)
    }

    async fn set(
        &self,
        cookie: &str,
        path: &str,
        to: &str,
    ) -> (http::StatusCode, serde_json::Value) {
        self.send(
            "PATCH",
            &format!("/api/repos/{path}"),
            cookie,
            Some(serde_json::json!({ "visibility": to })),
        )
        .await
    }

    /// A request with NO session at all — a visitor.
    async fn visit(&self, uri: &str) -> (http::StatusCode, serde_json::Value) {
        let request = http::Request::builder()
            .method("GET")
            .uri(uri)
            .body(axum::body::Body::empty())
            .expect("a request");
        let response = self
            .router
            .clone()
            .oneshot(request)
            .await
            .expect("the console answers");
        let status = response.status();
        let bytes = axum::body::to_bytes(response.into_body(), 4 << 20)
            .await
            .expect("a body");
        let text = String::from_utf8_lossy(&bytes).into_owned();
        (
            status,
            serde_json::from_str(&text).unwrap_or(serde_json::Value::String(text)),
        )
    }

    async fn authority(&self, cookie: &str, path: &str) -> serde_json::Value {
        let (status, body) = self
            .send(
                "GET",
                &format!("/api/repos/{path}/visibility"),
                cookie,
                None,
            )
            .await;
        assert_eq!(status, http::StatusCode::OK, "{body}");
        body
    }
}

async fn console(fake: Fake) -> Rig {
    console_selling(fake, None).await
}

/// [`console`], its monetize on loopback at `monetize_addr` (`None`: a console that sells
/// nothing, every row above).
#[cfg_attr(not(feature = "metered"), allow(unused_variables))]
async fn console_selling(fake: Fake, monetize_addr: Option<String>) -> Rig {
    let listener = tokio::net::TcpListener::bind("127.0.0.1:0")
        .await
        .expect("a loopback port");
    let addr = listener.local_addr().expect("its address");
    let serving = fake.clone();
    tokio::spawn(async move {
        tonic::transport::Server::builder()
            .add_service(pb::access_server::AccessServer::new(serving.clone()))
            .add_service(pb::accounts_server::AccountsServer::new(serving.clone()))
            .add_service(pb::usage_server::UsageServer::new(serving.clone()))
            .add_service(pb::repos_server::ReposServer::new(serving))
            .serve_with_incoming(tokio_stream::wrappers::TcpListenerStream::new(listener))
            .await
    });
    let dir = tempfile::tempdir().expect("a temp dir").keep();
    let store = crate::state::StateStore::open(dir.join("state.redb")).expect("a store");
    let webauthn = Arc::new(
        crate::auth::Webauthn::new(
            crate::auth::RelyingParty {
                rp_id: "console.example".to_owned(),
                origin: "https://console.example".to_owned(),
                rp_name: "gunnar".to_owned(),
            },
            store.clone(),
        )
        .expect("a subsystem"),
    );
    let endpoint = format!("http://{addr}");
    let router = crate::serve::router(Arc::new(crate::app::App {
        webauthn: Some(webauthn.clone()),
        admin_token: Some("break-glass".to_owned()),
        #[cfg(feature = "metered")]
        monetize: monetize_addr.map(|addr| {
            crate::monetize::Monetize::connect_lazy(&addr, crate::auth::signup::tests::BEARER, "gunnar").expect("a lazy monetize channel")
        }),
        events: crate::sse::EventHub::detached(),
        gunnar: crate::grpc::Gunnar::connect_lazy(&endpoint, Arc::new(console_signer()))
            .expect("a handle"),
        twin: None,
        guest: Some(
            crate::grpc::Gunnar::connect_lazy(&endpoint, Arc::new(signer_of(GUEST_SEED)))
                .expect("a guest handle"),
        ),
        reviewers: Arc::new(crate::reviewer::ReviewerKeys::new(
            endpoint.clone(),
            store.clone(),
        )),
        tenants: Arc::new(crate::tenant::TenantKeys::new(endpoint, store)),
        forges: Default::default(),
        imports: Arc::new(Imports::default()),
        forge_apps: Arc::default(),
        ui_dir: None,
        caches: crate::api::Caches::default(),
        objects: Default::default(),
        readmes: Default::default(),
    }));
    Rig {
        router,
        webauthn,
        fake,
    }
}

// ───────────────────────────────────────────────────────────────────────────
// The rows
// ───────────────────────────────────────────────────────────────────────────

/// ★ **A repository admin who is not the account admin is refused, by name,
/// and gunnar hears nothing.** Red before this module: `PATCH` passed the
/// visibility straight to `Repos.SetVisibility` on the console's key, so any
/// operator session published any repository.
#[tokio::test]
async fn an_operator_who_is_not_the_account_admin_cannot_change_visibility() {
    let fake = Fake::default();
    fake.repo("acme", "app");
    fake.grant("kim", "acme", "app", pb::GrantLevel::Admin);
    let b = console(fake).await;
    let kim = b.session("kim", Role::Operator);

    let (status, body) = b.set(&kim, "acme/app", "public").await;
    assert_eq!(status, http::StatusCode::FORBIDDEN, "{body}");
    let said = body["message"].as_str().unwrap_or_default();
    assert!(
        said.contains("account admin") && said.contains("acme"),
        "the refusal says whose decision it is: {body}"
    );
    assert!(
        b.fake.heard().is_empty(),
        "the console refused AFTER publishing: gunnar heard {:?}",
        b.fake.heard()
    );

    // …and the page is told the same thing, so it draws no control.
    let authority = b.authority(&kim, "acme/app").await;
    assert_eq!(authority["account_admin"], false, "{authority}");
    assert_eq!(authority["may_change"], false, "{authority}");
    assert!(
        authority["why"]
            .as_str()
            .unwrap_or_default()
            .contains("account admin"),
        "{authority}"
    );
    // ★ gunnar's answer, asked as kim (thin layer, owner 2026-10-04) — not the
    // console's copy of the rule over a grant table it read with its own key.
    assert_eq!(
        b.fake.checks.lock().expect("checks").clone(),
        vec!["Access.Check acme by kim".to_owned()],
    );
}

/// The account admin may: their own account, or a namespace admin grant.
#[tokio::test]
async fn the_account_admin_changes_visibility_from_the_console() {
    let fake = Fake::default();
    fake.repo("kim", "notes");
    fake.repo("acme", "app");
    fake.grant("kim", "acme", "", pb::GrantLevel::Admin);
    let b = console(fake).await;
    let kim = b.session("kim", Role::Operator);

    for path in ["kim/notes", "acme/app"] {
        let authority = b.authority(&kim, path).await;
        assert_eq!(authority["may_change"], true, "{path}: {authority}");
        let (status, body) = b.set(&kim, path, "public").await;
        assert_eq!(status, http::StatusCode::OK, "{path}: {body}");
        assert_eq!(body["visibility"], "public", "{body}");
    }
    assert_eq!(
        b.fake.heard(),
        vec![
            "kim/notes -> public".to_owned(),
            "acme/app -> public".to_owned()
        ]
    );

    // A namespace WRITE grant is not account admin.
    b.fake.repo("team", "tool");
    b.fake.grant("kim", "team", "", pb::GrantLevel::Write);
    let (status, body) = b.set(&kim, "team/tool", "public").await;
    assert_eq!(status, http::StatusCode::FORBIDDEN, "{body}");
}

/// The box's administrator is every account's admin (the console's key holds a
/// server-wide `Admin`), and the rule is the same both ways: the account admin
/// publishes and retracts; an operator who is not the account admin may do
/// neither — making private is as much the account's decision as making public.
#[tokio::test]
async fn the_boxs_administrator_turns_any_account_both_ways_and_nobody_else_does() {
    let fake = Fake::default();
    fake.repo("acme", "app");
    fake.grant("kim", "acme", "app", pb::GrantLevel::Admin);
    // The box's administrator: a server-wide Admin grant at gunnar (console
    // roles are grants, thin layer 2026-10-04) — and every act signed as boss.
    fake.grant("boss", "", "", pb::GrantLevel::Admin);
    let b = console(fake).await;
    let boss = b.session("boss", Role::Admin);
    let kim = b.session("kim", Role::Operator);

    let authority = b.authority(&boss, "acme/app").await;
    assert_eq!(authority["account_admin"], true, "{authority}");
    assert_eq!(authority["may_change"], true, "{authority}");
    for to in ["public", "private"] {
        let (status, body) = b.set(&boss, "acme/app", to).await;
        assert_eq!(status, http::StatusCode::OK, "{to}: {body}");
    }
    let (status, body) = b.set(&kim, "acme/app", "private").await;
    assert_eq!(status, http::StatusCode::FORBIDDEN, "{body}");
    assert_eq!(
        b.fake.heard(),
        vec!["acme/app -> public".to_owned(), "acme/app -> private".to_owned()],
        "only the box administrator's two turns reached gunnar"
    );
}

/// A viewer who IS the account admin is offered the control (2026-10-06: a
/// paying customer is a viewer, and gunnar decides as the person). And a
/// settings PATCH that does not touch visibility is not this rule's.
#[tokio::test]
async fn a_viewer_account_admin_is_offered_the_control_and_other_settings_are_untouched() {
    let fake = Fake::default();
    fake.repo("vera", "ledger");
    fake.repo("acme", "app");
    let b = console(fake).await;
    let vera = b.session("vera", Role::Viewer);
    let authority = b.authority(&vera, "vera/ledger").await;
    assert_eq!(authority["account_admin"], true, "{authority}");
    assert_eq!(authority["may_change"], true, "{authority}");
    assert!(authority["why"].is_null(), "{authority}");

    // An operator who is not acme's admin may still set acme/app's default
    // branch — that is not a visibility change and was never this rule's.
    let kim = b.session("kim", Role::Operator);
    let (status, body) = b
        .send(
            "PATCH",
            "/api/repos/acme/app",
            &kim,
            Some(serde_json::json!({ "default_branch": "refs/heads/trunk" })),
        )
        .await;
    assert_eq!(status, http::StatusCode::OK, "{body}");
    assert!(b.fake.heard().is_empty());
}

/// ★ **An encrypted repository is the account admin's decision at the
/// console too** (2026-10-06): an installed estate binds the control plane to
/// its WireGuard overlay, so the console is where an account admin turns the
/// flag — `PATCH {"encrypted": true}` signs `Repos.SetEncrypted` AS THE PERSON
/// and gunnar's rule decides; the repository read back says it. Anyone else
/// is refused in gunnar's words and gunnar changes nothing.
#[tokio::test]
async fn the_account_admin_turns_a_repository_encrypted_at_the_console_and_nobody_else_does() {
    let fake = Fake::default();
    fake.repo("kim", "vault");
    fake.repo("acme", "app");
    fake.grant("lee", "kim", "vault", pb::GrantLevel::Admin);
    let b = console(fake).await;
    let kim = b.session("kim", Role::Operator);
    let lee = b.session("lee", Role::Operator);

    let (status, body) = b
        .send("PATCH", "/api/repos/kim/vault", &kim, Some(serde_json::json!({ "encrypted": true })))
        .await;
    assert_eq!(status, http::StatusCode::OK, "{body}");
    assert_eq!(body["encrypted"], true, "the repository read back is encrypted: {body}");
    assert_eq!(b.fake.heard(), vec!["kim/vault -> encrypted".to_owned()]);
    assert!(
        b.fake.asked().contains(&"Repos.SetEncrypted by kim".to_owned()),
        "signed as the person, not the console: {:?}",
        b.fake.asked()
    );

    // A repository admin who is not the account admin: refused, nothing heard.
    let (status, body) = b
        .send("PATCH", "/api/repos/kim/vault", &lee, Some(serde_json::json!({ "encrypted": false })))
        .await;
    assert_eq!(status, http::StatusCode::FORBIDDEN, "{body}");
    assert!(
        body["message"].as_str().unwrap_or_default().contains("account admin"),
        "{body}"
    );
    assert_eq!(b.fake.heard(), vec!["kim/vault -> encrypted".to_owned()], "lee changed nothing");

    // Off again, by the account admin.
    let (status, body) = b
        .send("PATCH", "/api/repos/kim/vault", &kim, Some(serde_json::json!({ "encrypted": false })))
        .await;
    assert_eq!(status, http::StatusCode::OK, "{body}");
    assert_eq!(body["encrypted"], false, "{body}");
}

/// ★ **A paying customer is a viewer, and the admin of their own account**
/// (the owner's accounts spec: only the account admin makes a repository
/// public; clans: only the clan's admin publishes or deletes). Since the thin
/// layer every one of these acts is signed AS THE PERSON and gunnar decides,
/// so the console's floor must not stand in front of gunnar's rule: the
/// customer publishes, encrypts and deletes their own repository from the
/// console, and a viewer who writes to it but is not its account admin is
/// refused by gunnar, in gunnar's words, with nothing changed.
#[tokio::test]
async fn a_customer_publishes_encrypts_and_deletes_their_own_repository_and_a_writer_is_refused() {
    let fake = Fake::default();
    fake.repo("vera", "ledger");
    fake.grant("walt", "vera", "ledger", pb::GrantLevel::Write);
    let b = console(fake).await;
    let vera = b.session("vera", Role::Viewer);
    let walt = b.session("walt", Role::Viewer);

    let authority = b.authority(&vera, "vera/ledger").await;
    assert_eq!(authority["may_change"], true, "the account admin is offered the control: {authority}");
    let (status, body) = b.set(&vera, "vera/ledger", "public").await;
    assert_eq!(status, http::StatusCode::OK, "{body}");
    let (status, body) = b
        .send("PATCH", "/api/repos/vera/ledger", &vera, Some(serde_json::json!({ "encrypted": true })))
        .await;
    assert_eq!(status, http::StatusCode::OK, "{body}");

    // walt writes to it and is not its account admin: gunnar refuses.
    let authority = b.authority(&walt, "vera/ledger").await;
    assert_eq!(authority["may_change"], false, "{authority}");
    let (status, body) = b.set(&walt, "vera/ledger", "private").await;
    assert_eq!(status, http::StatusCode::FORBIDDEN, "{body}");
    assert!(body["message"].as_str().unwrap_or_default().contains("account admin"), "{body}");
    let (status, body) = b.send("DELETE", "/api/repos/vera/ledger", &walt, None).await;
    assert_eq!(status, http::StatusCode::FORBIDDEN, "{body}");
    assert_eq!(
        b.fake.heard(),
        vec!["vera/ledger -> public".to_owned(), "vera/ledger -> encrypted".to_owned()],
        "walt changed nothing"
    );
    assert!(b.fake.asked().contains(&"Repos.Delete by walt".to_owned()), "gunnar was asked, as walt: {:?}", b.fake.asked());

    // The account admin deletes it (a soft delete, undoable).
    let (status, body) = b.send("DELETE", "/api/repos/vera/ledger", &vera, None).await;
    assert_eq!(status, http::StatusCode::OK, "{body}");
    assert_eq!(b.fake.heard().last().map(String::as_str), Some("vera/ledger deleted"));
}

/// ★ **The Danger zone's answer** (owner spec 2026-10-28): `GET
/// /api/repos/{ns}/{name}/deletable` says whether THIS person may delete the
/// repository — gunnar's `Access.Check` at Admin on it, asked as them — and,
/// when they may, for how many days it can be restored: gunnar's window
/// (`ListDeletedResponse.default_retention_days`), never a number of the
/// console's. Whoever may not is given a sentence with gunnar's reason in it.
/// The list's Undo is the account admin's too, on a viewer session, and gunnar
/// decides it as the person.
#[tokio::test]
async fn the_danger_zone_is_gunnars_answer_with_gunnars_window_and_the_undo_is_the_account_admins() {
    let fake = Fake::default();
    fake.repo("vera", "ledger");
    fake.grant("walt", "vera", "ledger", pb::GrantLevel::Write);
    let b = console(fake).await;
    let vera = b.session("vera", Role::Viewer);
    let walt = b.session("walt", Role::Viewer);

    let (status, said) = b.send("GET", "/api/repos/vera/ledger/deletable", &vera, None).await;
    assert_eq!(status, http::StatusCode::OK, "{said}");
    assert_eq!(said["may_delete"], true, "the account admin may delete: {said}");
    assert_eq!(said["retention_days"], 30, "the window is gunnar's: {said}");
    assert!(said["why"].is_null(), "{said}");

    let (status, said) = b.send("GET", "/api/repos/vera/ledger/deletable", &walt, None).await;
    assert_eq!(status, http::StatusCode::OK, "{said}");
    assert_eq!(said["may_delete"], false, "a writer may not: {said}");
    let why = said["why"].as_str().unwrap_or_default();
    assert!(why.contains("Only an admin of vera/ledger or of its account may delete it."), "{why}");
    assert!(why.contains("walt has no admin grant"), "gunnar's own reason is in the sentence: {why}");
    assert_eq!(said["retention_days"], 0, "{said}");

    // A visitor is refused before it: not on the anonymous allow-list.
    let (status, _) = b.visit("/api/repos/vera/ledger/deletable").await;
    assert_eq!(status, http::StatusCode::UNAUTHORIZED);

    // Deleted, then undone from the list: walt is refused by gunnar, vera puts it back.
    let (status, body) = b.send("DELETE", "/api/repos/vera/ledger", &vera, None).await;
    assert_eq!(status, http::StatusCode::OK, "{body}");
    let (status, body) = b.send("POST", "/api/repos/vera/ledger/undelete", &walt, None).await;
    assert_eq!(status, http::StatusCode::FORBIDDEN, "{body}");
    assert!(b.fake.asked().contains(&"Repos.Undelete by walt".to_owned()), "gunnar was asked, as walt: {:?}", b.fake.asked());
    let (status, body) = b.send("POST", "/api/repos/vera/ledger/undelete", &vera, None).await;
    assert_eq!(status, http::StatusCode::OK, "the account admin's undo on a viewer session: {body}");
    assert_eq!(b.fake.heard().last().map(String::as_str), Some("vera/ledger undeleted"));
}

// ── rule 4: `/<ns>` — a private namespace shows a visitor only its public repos

fn repo_names(body: &serde_json::Value) -> Vec<String> {
    body["repos"]
        .as_array()
        .map(|repos| {
            repos
                .iter()
                .filter_map(|r| r["name"].as_str().map(str::to_owned))
                .collect()
        })
        .unwrap_or_default()
}

/// ★ **A visitor at `/<ns>` of a private namespace sees ONLY its public
/// repositories** — not the private ones, not even a count — and a private
/// namespace with no public repository answers exactly as one that does not
/// exist. A PUBLIC namespace is shown even while it holds nothing public.
#[tokio::test]
async fn a_visitor_sees_a_private_namespace_as_its_public_repositories_or_not_at_all() {
    let fake = Fake::default();
    fake.repo_with("acme", "open", pb::Visibility::Public);
    fake.repo_with("acme", "app", pb::Visibility::Private);
    fake.repo_with("quiet", "hidden", pb::Visibility::Private);
    fake.namespace("shown", true);
    let b = console(fake).await;

    let (status, body) = b.visit("/api/namespaces/acme/page").await;
    assert_eq!(status, http::StatusCode::OK, "{body}");
    assert_eq!(repo_names(&body), vec!["open".to_owned()], "{body}");
    let text = body.to_string();
    assert!(!text.contains("\"app\""), "a private repository reached a visitor: {text}");
    assert!(!text.contains("private"), "the page says something about what is private: {text}");

    let (quiet, quiet_body) = b.visit("/api/namespaces/quiet/page").await;
    let (ghost, ghost_body) = b.visit("/api/namespaces/ghost/page").await;
    assert_eq!(quiet, http::StatusCode::NOT_FOUND, "{quiet_body}");
    assert_eq!(ghost, http::StatusCode::NOT_FOUND, "{ghost_body}");
    assert_eq!(quiet_body, ghost_body, "a private namespace is told apart from a missing one");
    assert_eq!(quiet_body, crate::api::absent::body());

    let (status, body) = b.visit("/api/namespaces/shown/page").await;
    assert_eq!(status, http::StatusCode::OK, "a public namespace is shown: {body}");
    assert!(repo_names(&body).is_empty(), "{body}");
}

// ── rule 1: the account's settings are its admin's, private by default ─────

/// ★ **The namespace's settings are the account admin's**: an operator who
/// holds admin on one repository is refused before anything is signed; the
/// account admin may. A viewer's namespace list is asked as the viewer, so a
/// private namespace is not in it until its admin makes it public.
#[tokio::test]
async fn the_namespace_settings_are_the_account_admins_and_a_viewer_lists_only_public_ones() {
    let fake = Fake::default();
    fake.repo("acme", "app");
    fake.grant("kim", "acme", "app", pb::GrantLevel::Admin);
    let b = console(fake).await;
    let kim = b.session("kim", Role::Operator);
    let acme = b.session("acme", Role::Viewer);
    let vera = b.session("vera", Role::Viewer);

    let listed = |body: &serde_json::Value| -> Vec<String> {
        body["items"]
            .as_array()
            .map(|a| a.iter().filter_map(|n| n["name"].as_str().map(str::to_owned)).collect())
            .unwrap_or_default()
    };
    let (status, body) = b.send("GET", "/api/namespaces", &vera, None).await;
    assert_eq!(status, http::StatusCode::OK, "{body}");
    assert!(!listed(&body).contains(&"acme".to_owned()), "a private namespace listed to a viewer: {body}");

    let settings = "/api/account/namespaces/acme/settings";
    let (status, body) = b
        .send("PATCH", settings, &kim, Some(serde_json::json!({ "public": true })))
        .await;
    assert_eq!(status, http::StatusCode::FORBIDDEN, "{body}");
    assert!(
        body["message"].as_str().unwrap_or_default().contains("account admin"),
        "{body}"
    );
    assert!(b.fake.heard().is_empty(), "gunnar heard {:?}", b.fake.heard());

    let (status, body) = b
        .send("PATCH", settings, &acme, Some(serde_json::json!({ "public": true })))
        .await;
    assert_eq!(status, http::StatusCode::OK, "the account admin sets it: {body}");
    assert_eq!(body["public"], true, "{body}");
    assert_eq!(b.fake.heard(), vec!["settings acme public=true events=false".to_owned()]);

    let (status, body) = b.send("GET", "/api/namespaces", &vera, None).await;
    assert_eq!(status, http::StatusCode::OK, "{body}");
    assert!(listed(&body).contains(&"acme".to_owned()), "a public namespace is listed: {body}");
}

// ── rule 3: the account admin enrolls NEW employees ─────────────────────────

/// ★ **The account admin enrolls a NEW employee** — gunnar creates the
/// principal bound to the account, and gunnar mints the invite for it, asked
/// AS the account admin (thin layer L), so the employee's passkey lands on it.
/// Anybody else is refused before gunnar hears a thing; an existing principal
/// is refused by gunnar and no invite is minted for it.
#[tokio::test]
async fn the_account_admin_enrolls_a_new_employee_with_an_invite_pinned_to_it() {
    let fake = Fake::default();
    fake.repo("acme", "app");
    fake.user("carol");
    let b = console(fake).await;
    let kim = b.session("kim", Role::Operator);
    let acme = b.session("acme", Role::Viewer);
    let enroll = |name: &str| Some(serde_json::json!({ "account": "acme", "name": name }));

    let (status, body) = b.send("POST", "/api/account/employees", &kim, enroll("bob")).await;
    assert_eq!(status, http::StatusCode::FORBIDDEN, "{body}");
    assert!(b.fake.heard().is_empty(), "gunnar heard {:?}", b.fake.heard());

    let (status, body) = b.send("POST", "/api/account/employees", &acme, enroll("bob")).await;
    assert_eq!(status, http::StatusCode::OK, "{body}");
    assert_eq!(body["principal"]["id"], "bob", "{body}");
    assert!(
        body["url"].as_str().unwrap_or_default().contains("invite="),
        "the employee's invite link comes back: {body}"
    );
    // ONE gunnar call: bob and his invite (EnrollEmployee{invite}, Add users).
    assert_eq!(b.fake.heard(), vec!["enroll acme/bob, invited".to_owned()]);
    assert!(body["url"].as_str().unwrap_or_default().contains("gunnars-secret-for-bob"), "{body}");
    assert!(b.fake.invites.lock().expect("invites").is_empty(), "a second, separate invite call");

    let (status, body) = b.send("POST", "/api/account/employees", &acme, enroll("carol")).await;
    assert_ne!(status, http::StatusCode::OK, "an existing principal became an employee: {body}");
    assert!(
        b.fake.invites.lock().expect("invites").is_empty(),
        "an invite was minted for a refused enrolment"
    );

    let (status, body) = b
        .send("GET", "/api/account/employees?account=acme", &acme, None)
        .await;
    assert_eq!(status, http::StatusCode::OK, "{body}");
    assert_eq!(body["employees"][0]["id"], "bob", "{body}");
}

/// ★ **A reserved name is refused BEFORE gunnar enrolls it** (owner,
/// 2026-10-04). Red before this row: `Accounts.EnrollEmployee` was signed
/// first and the console's reserved-name rule ran only when the invite was
/// minted, so an account admin left a `console-<x>` principal at gunnar —
/// the name the reviewer provisioning used to adopt and grant server-wide
/// Write. gunnar must hear nothing, and no invite is minted.
#[tokio::test]
async fn a_reserved_name_is_refused_before_gunnar_enrolls_it() {
    let fake = Fake::default();
    fake.repo("acme", "app");
    let b = console(fake).await;
    let acme = b.session("acme", Role::Viewer);

    for name in ["console-x", "gunnar-ui"] {
        let (status, body) = b
            .send(
                "POST",
                "/api/account/employees",
                &acme,
                Some(serde_json::json!({ "account": "acme", "name": name })),
            )
            .await;
        assert_ne!(status, http::StatusCode::OK, "{name}: {body}");
        assert!(
            b.fake.heard().is_empty(),
            "{name}: gunnar enrolled a reserved name: {:?}",
            b.fake.heard()
        );
    }
    assert!(
        b.fake.invites.lock().expect("invites").is_empty(),
        "an invite was minted for a reserved name"
    );
}

/// ★ **The account admin's decisions reach gunnar signed AS THE PERSON**
/// (thin layer, owner 2026-10-04) — never with the console's key — and a
/// person who is not the account admin is refused BY GUNNAR, in gunnar's
/// words. Red before this row: the console decided with its own copy of the
/// rule (`visibility::require_may_set`, `account_admin::require_account_admin`)
/// and then signed with its server-wide key, so gunnar heard every one of these
/// from `console` and could refuse none of them.
#[tokio::test]
async fn the_account_admins_decisions_are_signed_by_the_person_and_gunnar_decides() {
    let fake = Fake::default();
    fake.repo("acme", "app");
    fake.repo("kim", "notes");
    fake.grant("kim", "acme", "app", pb::GrantLevel::Admin);
    let b = console(fake).await;
    let acme = b.session("acme", Role::Viewer);
    let kim = b.session("kim", Role::Operator);

    // The account admin (a viewer: a paying customer) — settings and employees.
    let (status, body) = b
        .send(
            "PATCH",
            "/api/account/namespaces/acme/settings",
            &acme,
            Some(serde_json::json!({ "events_public": true })),
        )
        .await;
    assert_eq!(status, http::StatusCode::OK, "{body}");
    let (status, body) = b
        .send(
            "POST",
            "/api/account/employees",
            &acme,
            Some(serde_json::json!({ "account": "acme", "name": "bob" })),
        )
        .await;
    assert_eq!(status, http::StatusCode::OK, "{body}");
    let (status, body) = b
        .send("GET", "/api/account/employees?account=acme", &acme, None)
        .await;
    assert_eq!(status, http::StatusCode::OK, "{body}");

    // An operator: their own account's repository, and someone else's.
    let (status, body) = b.set(&kim, "kim/notes", "public").await;
    assert_eq!(status, http::StatusCode::OK, "{body}");
    let (status, body) = b.set(&kim, "acme/app", "public").await;
    assert_eq!(status, http::StatusCode::FORBIDDEN, "{body}");
    assert!(
        body["message"].as_str().unwrap_or_default().contains("account admin"),
        "gunnar's refusal reaches the page: {body}"
    );
    let (status, body) = b
        .send(
            "PATCH",
            "/api/account/namespaces/acme/settings",
            &kim,
            Some(serde_json::json!({ "public": true })),
        )
        .await;
    assert_eq!(status, http::StatusCode::FORBIDDEN, "{body}");

    assert_eq!(
        b.fake.asked(),
        vec![
            "Repos.SetNamespaceSettings by acme".to_owned(),
            "Accounts.EnrollEmployee by acme".to_owned(),
            "Accounts.ListEmployees by acme".to_owned(),
            "Repos.SetVisibility by kim".to_owned(),
            "Repos.SetVisibility by kim".to_owned(),
            "Repos.SetNamespaceSettings by kim".to_owned(),
        ],
        "every account-admin call is signed by the person, and the refusals are gunnar's"
    );
    assert!(
        !b.fake.heard().iter().any(|h| h.starts_with("acme/app") || h.contains("public=true")),
        "a refused call changed something: {:?}",
        b.fake.heard()
    );
}

// ── "Add users" (owner, 2026-10-04): one press, one server call ────────────

/// ★ **More than one user is that many SEATS, with no names and no invite** —
/// sent as typed (the room in GiB becomes bytes, nothing else is decided
/// here), signed as the person, and gunnar's answer is what comes back.
#[tokio::test]
async fn the_account_admin_adds_ten_seats_with_no_names_and_no_invite() {
    let fake = Fake::default();
    fake.repo("acme", "app");
    let b = console(fake).await;
    let acme = b.session("acme", Role::Viewer);
    let kim = b.session("kim", Role::Operator);
    let ten = Some(serde_json::json!({ "account": "acme", "count": 10, "room_gib": 20 }));

    let (status, body) = b.send("POST", "/api/account/employees", &kim, ten.clone()).await;
    assert_eq!(status, http::StatusCode::FORBIDDEN, "{body}");
    assert!(b.fake.heard().is_empty(), "gunnar granted {:?}", b.fake.heard());

    let (status, body) = b.send("POST", "/api/account/employees", &acme, ten).await;
    assert_eq!(status, http::StatusCode::OK, "{body}");
    assert_eq!((body["seats"].as_u64(), body["seats_open"].as_u64()), (Some(10), Some(10)), "{body}");
    assert!(body["principal"].is_null(), "seats name nobody: {body}");
    assert_eq!(body["url"], "", "an invite for nobody: {body}");
    assert_eq!(b.fake.heard(), vec![format!("enroll acme seats 10 room {}", 20u64 << 30)]);
    assert!(b.fake.invites.lock().expect("invites").is_empty(), "an invite was minted for a seat");
}


/// ★ **A bought seat is filled from the console** (2026-10-04, the circle
/// analysis: nothing filled a seat): the account admin names the NEW user,
/// gunnar takes a seat for them and makes their invite in the SAME call, and
/// the console hands the link back. A seat claim names a room nobody types.
#[tokio::test]
async fn the_account_admin_fills_a_bought_seat_with_a_new_user_and_their_invite() {
    let fake = Fake::default();
    fake.repo("acme", "app");
    let b = console(fake).await;
    let acme = b.session("acme", Role::Viewer);
    let fill = Some(serde_json::json!({ "account": "acme", "name": "sam", "from_seat": true }));
    let (status, body) = b.send("POST", "/api/account/employees", &acme, fill).await;
    assert_eq!(status, http::StatusCode::OK, "{body}");
    assert_eq!(body["principal"]["id"], "sam", "{body}");
    assert!(body["url"].as_str().unwrap_or_default().contains("invite="), "no invite link: {body}");
    assert_eq!(b.fake.heard(), vec!["enroll acme/sam from a seat, invited".to_owned()], "not ONE gunnar call");
    assert!(b.fake.invites.lock().expect("invites").is_empty(), "a second, separate invite call");
}

// ───────────────────────────────────────────────────────────────────────────
// New repository: the account admin's (owner, 2026-10-06)
// ───────────────────────────────────────────────────────────────────────────

/// A `POST /api/repos` as `cookie`.
async fn new_repo(b: &Rig, cookie: &str, body: serde_json::Value) -> (http::StatusCode, serde_json::Value) {
    b.send("POST", "/api/repos", cookie, Some(body)).await
}

/// ★ **An account admin on a VIEWER session creates a repository in their own
/// account and in one they administer — gunnar decides, on the person's key.**
/// Red before: the console's role floor refused every viewer's `POST
/// /api/repos` with 403 before gunnar was asked, so a customer could create a
/// repository only by pushing one into existence.
#[tokio::test]
async fn an_account_admin_on_a_viewer_session_creates_a_repository_and_gunnar_decides() {
    let fake = Fake::default();
    fake.namespace("kim", false);
    fake.namespace("acme", false);
    fake.grant("kim", "acme", "", pb::GrantLevel::Admin);
    let b = console(fake).await;
    let kim = b.session("kim", Role::Viewer);
    for (ns, visibility) in [("kim", "private"), ("kim", "public"), ("acme", "private")] {
        let name = format!("made-{visibility}");
        let (status, body) = new_repo(&b, &kim, serde_json::json!({ "namespace": ns, "name": name, "visibility": visibility })).await;
        assert_eq!(status, http::StatusCode::OK, "{ns} {visibility}: {body}");
    }
    assert_eq!(
        b.fake.heard(),
        vec!["created kim/made-private private", "created kim/made-public public", "created acme/made-private private"],
        "gunnar made the three"
    );
    assert!(b.fake.asked().iter().all(|a| a == "Repos.Create by kim"), "every create signed as kim: {:?}", b.fake.asked());
}

/// ★ **A writer, a reader, and another account's namespace are refused — by
/// gunnar, on the person's key, and nothing is made.**
#[tokio::test]
async fn a_writer_a_reader_and_another_account_get_gunnars_refusal_and_nothing_is_made() {
    let fake = Fake::default();
    fake.namespace("acme", false);
    fake.namespace("beta", false);
    fake.grant("wendy", "acme", "", pb::GrantLevel::Write);
    fake.grant("vic", "acme", "", pb::GrantLevel::Read);
    let b = console(fake).await;
    for (who, ns) in [("wendy", "acme"), ("vic", "acme"), ("beta", "acme"), ("wendy", "beta")] {
        let cookie = b.session(who, Role::Viewer);
        let (status, body) = new_repo(&b, &cookie, serde_json::json!({ "namespace": ns, "name": "intruder" })).await;
        assert_eq!(status, http::StatusCode::FORBIDDEN, "{who} in {ns}: {body}");
        assert!(body.to_string().contains("admin"), "gunnar's words reach the person: {body}");
    }
    assert!(b.fake.heard().is_empty(), "a refused create made {:?}", b.fake.heard());
    assert_eq!(
        b.fake.asked(),
        vec!["Repos.Create by wendy", "Repos.Create by vic", "Repos.Create by beta", "Repos.Create by wendy"],
        "every refusal is gunnar's, asked as the person — not a wall the console put up"
    );
}

/// ★ **The encrypted choice** marks the new repository encrypted — the
/// existing `Repos.SetEncrypted`, signed as the same person, right after the
/// create.
#[tokio::test]
async fn the_encrypted_choice_marks_the_new_repository_encrypted_as_the_person() {
    let fake = Fake::default();
    fake.namespace("kim", false);
    let b = console(fake).await;
    let kim = b.session("kim", Role::Viewer);
    let (status, body) = new_repo(&b, &kim, serde_json::json!({ "namespace": "kim", "name": "vault", "encrypted": true })).await;
    assert_eq!(status, http::StatusCode::OK, "{body}");
    assert_eq!(body["encrypted"], true, "{body}");
    assert_eq!(b.fake.heard(), vec!["created kim/vault private", "kim/vault -> encrypted"]);
    assert_eq!(b.fake.asked(), vec!["Repos.Create by kim", "Repos.SetEncrypted by kim"]);
}

/// ★ **Where the button may create**, in gunnar's words: the namespaces this
/// session's person is the account admin of (their own, and one they hold
/// Admin over — asked of gunnar, `Access.Check`), each one publishable; and an
/// operator's session keeps the free namespace field it has today.
#[tokio::test]
async fn the_new_repository_button_offers_the_accounts_gunnar_says_the_person_administers() {
    let fake = Fake::default();
    fake.namespace("kim", false);
    fake.namespace("acme", false);
    fake.namespace("beta", false);
    fake.grant("kim", "acme", "", pb::GrantLevel::Admin);
    fake.grant("kim", "beta", "", pb::GrantLevel::Write);
    let b = console(fake).await;
    let kim = b.session("kim", Role::Viewer);
    let (status, body) = b.send("GET", "/api/repos/creatable", &kim, None).await;
    assert_eq!(status, http::StatusCode::OK, "{body}");
    assert_eq!(body["any_namespace"], false, "{body}");
    let offered: Vec<&str> = body["namespaces"].as_array().expect("namespaces").iter().filter_map(|n| n["name"].as_str()).collect();
    assert_eq!(offered, vec!["acme", "kim"], "kim's own and acme, not beta (write only): {body}");
    assert!(body["namespaces"].as_array().expect("namespaces").iter().all(|n| n["public"] == true), "{body}");

    let wendy = b.session("wendy", Role::Viewer);
    let (_, body) = b.send("GET", "/api/repos/creatable", &wendy, None).await;
    assert_eq!(body["namespaces"].as_array().map(|a| a.iter().filter_map(|n| n["name"].as_str()).collect::<Vec<_>>()), Some(vec!["wendy"]), "{body}");

    let boss = b.session("boss", Role::Operator);
    let (_, body) = b.send("GET", "/api/repos/creatable", &boss, None).await;
    assert_eq!(body["any_namespace"], true, "an operator keeps the free namespace field: {body}");
}

// ── members (owner 2026-10-07: a customer is an ACCOUNT that pays; MEMBERS
//    use its space, never buy, and get Write from its admin) ─────────────────

/// acme's account (its admin `acme`, the repository `acme/site`, the member
/// `ben` it enrolled) and `kim`, an individual with an account of her own.
async fn members_world() -> Rig {
    let fake = Fake::default();
    fake.repo("acme", "site");
    fake.namespace("kim", false);
    fake.user("ben");
    fake.employees.lock().expect("employees").insert("ben".into(), "acme".into());
    fake.grant("ben", "acme", "", pb::GrantLevel::Read);
    console(fake).await
}

/// ★ **A member is told whose space it is, and is shown no shop** —
/// `/auth/status` carries gunnar's word (`Access.WhoAmI`): the account, what
/// the person is to it, and the one line a member reads where the shop would
/// be. The account's admin and an individual may buy; a member may not.
#[tokio::test]
async fn a_member_is_told_whose_space_it_is_and_is_shown_no_shop() {
    let b = members_world().await;
    let ben = b.session("ben", Role::Viewer);
    let acme = b.session("acme", Role::Viewer);
    let kim = b.session("kim", Role::Viewer);

    let (status, me) = b.send("GET", "/auth/status", &ben, None).await;
    assert_eq!(status, http::StatusCode::OK, "{me}");
    assert_eq!((me["account"].as_str(), me["account_role"].as_str(), me["account_admin"].as_str()), (Some("acme"), Some("employee"), Some("acme")), "{me}");
    assert_eq!(me["account_line"], "Your space comes from acme. Ask acme for more room.", "{me}");
    #[cfg(feature = "metered")]
    {
        assert_eq!(me["may_buy"], false, "a member never reaches the shop: {me}");
        assert!(me["buy_url"].is_null() && me["private_url"].is_null(), "no door for a member: {me}");
    }

    for (who, cookie, role) in [("acme", &acme, "admin"), ("kim", &kim, "individual")] {
        let (_, me) = b.send("GET", "/auth/status", cookie, None).await;
        assert_eq!((me["account"].as_str(), me["account_role"].as_str()), (Some(who), Some(role)), "{me}");
        assert!(me["account_line"].is_null(), "{who} is no member: {me}");
        #[cfg(feature = "metered")]
        assert_eq!(me["may_buy"], true, "{who} buys for its own account: {me}");
    }
}

/// ★ **An appointed account admin administers the account's repositories and never buys**
/// (owner 2026-10-08, round 5.3.1). gunnar answers WhoAmI `admin-appointed` for a member the
/// account's admin gave Admin over the account: he publishes and encrypts the account's
/// repositories (gunnar's grant decides, asked as him), reads the account's room read only,
/// and is shown no shop — no buy, private or sign-up door, the members line naming the
/// account, and every purchase door and the sign-up checkout refuse him.
#[tokio::test]
async fn an_appointed_admin_administers_the_accounts_repositories_and_never_buys() {
    let fake = Fake::default();
    fake.repo("acme", "site");
    fake.user("dan");
    fake.employees.lock().expect("employees").insert("dan".into(), "acme".into());
    fake.appointed.lock().expect("appointed").insert("dan".into());
    fake.grant("dan", "acme", "", pb::GrantLevel::Admin);
    #[cfg(feature = "metered")]
    let (monetize, calls) = {
        let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.expect("a loopback port");
        let addr = listener.local_addr().expect("its address");
        let shop = crate::auth::signup::tests::FakeMonetize { checkout: Some("https://pay.example/c/cs_1".into()), ..Default::default() };
        let calls = shop.calls.clone();
        tokio::spawn(async move {
            tonic::transport::Server::builder()
                .add_service(crate::mpb::signup_server::SignupServer::new(shop))
                .serve_with_incoming(tokio_stream::wrappers::TcpListenerStream::new(listener))
                .await
        });
        (Some(format!("http://{addr}")), calls)
    };
    #[cfg(not(feature = "metered"))]
    let monetize = None;
    let b = console_selling(fake, monetize).await;
    let dan = b.session("dan", Role::Viewer);

    let (status, me) = b.send("GET", "/auth/status", &dan, None).await;
    assert_eq!(status, http::StatusCode::OK, "{me}");
    assert_eq!((me["account"].as_str(), me["account_role"].as_str()), (Some("acme"), Some("admin-appointed")), "{me}");
    assert_eq!(me["account_line"], "Your space comes from acme. Ask acme for more room.", "the members line names the account: {me}");
    #[cfg(feature = "metered")]
    {
        assert_eq!(me["may_buy"], false, "an appointed admin never reaches the shop: {me}");
        for door in ["buy_url", "private_url", "signup_url"] {
            assert!(me[door].is_null(), "{door} for an appointed admin: {me}");
        }
    }

    // The account's repository decisions are his: gunnar's grant, asked as him.
    let authority = b.authority(&dan, "acme/site").await;
    assert_eq!(authority["may_change"], true, "{authority}");
    let (status, body) = b.set(&dan, "acme/site", "public").await;
    assert_eq!(status, http::StatusCode::OK, "{body}");
    let (status, body) = b.send("PATCH", "/api/repos/acme/site", &dan, Some(serde_json::json!({ "encrypted": true }))).await;
    assert_eq!(status, http::StatusCode::OK, "{body}");

    // The account's room, read only: never a plan of his own.
    let (status, room) = b.send("GET", "/api/account/room", &dan, None).await;
    assert_eq!(status, http::StatusCode::OK, "{room}");
    assert_eq!((room["namespace"].as_str(), room["read_only"].as_bool()), (Some("acme"), Some(true)), "{room}");

    #[cfg(feature = "metered")]
    {
        let (status, body) = b.send("POST", "/api/account/subscription/buy-bundle", &dan, Some(serde_json::json!({ "bundle": "gunnar/10gb-1m" }))).await;
        assert_eq!(status, http::StatusCode::FORBIDDEN, "{body}");
        let order = serde_json::json!({ "username": "danco", "email": "dan@example.com", "plan": "gunnar/10gb-1m" });
        let (status, body) = b.send("POST", "/auth/signup/start", &dan, Some(order)).await;
        assert_eq!(status, http::StatusCode::FORBIDDEN, "{body}");
        assert!(crate::auth::signup::tests::asks(&calls).is_empty(), "monetize was asked for an appointed admin");
    }
}

/// ★ **A member is refused the sign-up checkout by the SERVER, not only by the page**
/// (wiring tracer B 2026-10-08). `/signup` draws no shop for a member (the page reads
/// `account_role`), but `POST /auth/signup/start` read no session: a signed-in member got a
/// checkout for a NEW account straight from the console. The member is told whom to ask, and
/// monetize hears nothing; the account's admin still reaches it.
#[cfg(feature = "metered")]
#[tokio::test]
async fn a_member_is_refused_the_signup_checkout_by_the_server_and_monetize_hears_nothing() {
    let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.expect("a loopback port");
    let addr = listener.local_addr().expect("its address");
    let shop = crate::auth::signup::tests::FakeMonetize { checkout: Some("https://pay.example/c/cs_1".into()), ..Default::default() };
    let calls = shop.calls.clone();
    tokio::spawn(async move {
        tonic::transport::Server::builder()
            .add_service(crate::mpb::signup_server::SignupServer::new(shop))
            .serve_with_incoming(tokio_stream::wrappers::TcpListenerStream::new(listener))
            .await
    });
    let fake = Fake::default();
    fake.repo("acme", "site");
    fake.user("ben");
    fake.employees.lock().expect("employees").insert("ben".into(), "acme".into());
    let b = console_selling(fake, Some(format!("http://{addr}"))).await;
    let ben = b.session("ben", Role::Viewer);
    let order = serde_json::json!({ "username": "benco", "email": "ben@example.com", "plan": "gunnar/10gb-1m" });
    let (status, body) = b.send("POST", "/auth/signup/start", &ben, Some(order.clone())).await;
    assert_eq!(status, http::StatusCode::FORBIDDEN, "a member reached the checkout: {body}");
    assert!(body.to_string().contains("Ask acme"), "the member is told whom to ask: {body}");
    assert!(crate::auth::signup::tests::asks(&calls).is_empty(), "monetize was asked for a member: {:?}", calls.lock().expect("the call log"));

    let acme = b.session("acme", Role::Viewer);
    let (status, body) = b.send("POST", "/auth/signup/start", &acme, Some(order)).await;
    assert_eq!(status, http::StatusCode::OK, "the account's admin is no member: {body}");
}

/// ★ **Every purchase door refuses a member on the server** (wiring tracer B 2026-10-08:
/// the UI test probed buy-bundle only). Renew, buy, buy-bundle, cancel, the bundles read and
/// Add users (`POST /api/account/employees`, a seat purchase) — a member posting straight at
/// them, past every hidden button, is refused, and monetize hears nothing.
#[cfg(feature = "metered")]
#[tokio::test]
async fn every_purchase_door_refuses_a_member_posting_past_the_page() {
    let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.expect("a loopback port");
    let addr = listener.local_addr().expect("its address");
    let shop = crate::auth::signup::tests::FakeMonetize::default();
    let calls = shop.calls.clone();
    tokio::spawn(async move {
        tonic::transport::Server::builder()
            .add_service(crate::mpb::signup_server::SignupServer::new(shop))
            .serve_with_incoming(tokio_stream::wrappers::TcpListenerStream::new(listener))
            .await
    });
    let fake = Fake::default();
    fake.repo("acme", "site");
    fake.user("ben");
    fake.employees.lock().expect("employees").insert("ben".into(), "acme".into());
    let b = console_selling(fake, Some(format!("http://{addr}"))).await;
    let ben = b.session("ben", Role::Viewer);
    for (method, path, body) in [
        ("POST", "/api/account/subscription/renew", serde_json::json!({})),
        ("POST", "/api/account/subscription/buy-bundle", serde_json::json!({ "bundle": "gunnar/10gb-1m" })),
        ("POST", "/api/account/subscription/cancel", serde_json::json!({ "reference": "gunnar/acme/2026-10-08" })),
        ("GET", "/api/account/subscription/bundles", serde_json::Value::Null),
        ("POST", "/api/account/employees", serde_json::json!({ "count": 5 })),
    ] {
        let (status, answer) = b.send(method, path, &ben, (!body.is_null()).then_some(body)).await;
        if path.starts_with("/api/account/subscription") {
            // The console's own wall (OwnSubscription), before any ticket or quote.
            assert_eq!(status, http::StatusCode::FORBIDDEN, "{method} {path} answered a member: {answer}");
            assert!(answer.to_string().contains("Ask acme"), "{method} {path}: the member is told whom to ask: {answer}");
        } else {
            // Add users: gunnar's (the account admin's door), never a success.
            assert!(status.is_client_error() || status.is_server_error(), "{method} {path} answered a member {status}: {answer}");
            assert_ne!(status, http::StatusCode::NOT_FOUND, "{method} {path} is mounted on this selling console: {answer}");
        }
    }
    assert!(crate::auth::signup::tests::asks(&calls).is_empty(), "monetize was asked for a member: {:?}", calls.lock().expect("the call log"));
}

/// ★ **A member's storage is the ACCOUNT's room, read only** — the same
/// `Usage.Read`, asked for the account as the member; never a namespace of
/// his own (the walkthrough's "namespace ben is not in the catalog").
#[tokio::test]
async fn a_members_storage_is_the_accounts_room_read_only() {
    let b = members_world().await;
    let ben = b.session("ben", Role::Viewer);
    let (status, room) = b.send("GET", "/api/account/room", &ben, None).await;
    assert_eq!(status, http::StatusCode::OK, "{room}");
    assert_eq!((room["namespace"].as_str(), room["read_only"].as_bool()), (Some("acme"), Some(true)), "{room}");
    assert!(b.fake.asked().contains(&"Usage.Read acme by ben".to_owned()), "asked as ben, for acme: {:?}", b.fake.asked());

    let acme = b.session("acme", Role::Viewer);
    let (status, room) = b.send("GET", "/api/account/room", &acme, None).await;
    assert_eq!(status, http::StatusCode::OK, "{room}");
    assert_eq!(room["namespace"], "acme");
    assert!(room.get("read_only").is_none(), "the admin's own room is no member's view: {room}");
}

/// ★ **The account's admin gives a member Write on one repository, sees it,
/// and takes it away; the member cannot** — every call signed as the person,
/// gunnar deciding (the fake keeps gunnar's rule: Admin over the namespace).
#[tokio::test]
async fn the_account_admin_gives_a_member_write_and_takes_it_away_and_a_member_cannot() {
    let b = members_world().await;
    let acme = b.session("acme", Role::Viewer);
    let ben = b.session("ben", Role::Viewer);
    let members = |cookie: String| {
        let b = &b;
        async move { b.send("GET", "/api/repos/acme/site/members", &cookie, None).await }
    };

    let (status, table) = members(acme.clone()).await;
    assert_eq!(status, http::StatusCode::OK, "{table}");
    assert_eq!(table["members"][0]["id"], "ben", "{table}");
    assert_eq!(table["members"][0]["account_level"], "read", "enrolment's Read on the account: {table}");
    assert!(table["members"][0]["repo_level"].is_null(), "{table}");

    // The member may neither read the table nor give himself Write.
    let (status, _) = members(ben.clone()).await;
    assert_eq!(status, http::StatusCode::FORBIDDEN);
    let (status, body) = b.send("PUT", "/api/repos/acme/site/members/ben", &ben, Some(serde_json::json!({ "level": "write" }))).await;
    assert_eq!(status, http::StatusCode::FORBIDDEN, "gunnar refuses the member: {body}");
    assert!(b.fake.heard().is_empty(), "gunnar changed nothing for a member: {:?}", b.fake.heard());
    assert!(b.fake.asked().contains(&"Access.Grant by ben".to_owned()), "signed as ben, refused by gunnar: {:?}", b.fake.asked());

    let (status, body) = b.send("PUT", "/api/repos/acme/site/members/ben", &acme, Some(serde_json::json!({ "level": "write" }))).await;
    assert_eq!(status, http::StatusCode::OK, "{body}");
    assert_eq!(b.fake.heard(), vec!["grant ben GRANT_LEVEL_WRITE on acme/site".to_owned()]);
    assert!(b.fake.asked().contains(&"Access.Grant by acme".to_owned()), "signed as the account's admin: {:?}", b.fake.asked());
    let (_, table) = members(acme.clone()).await;
    assert_eq!(table["members"][0]["repo_level"], "write", "{table}");

    let (status, body) = b.send("DELETE", "/api/repos/acme/site/members/ben", &acme, None).await;
    assert_eq!(status, http::StatusCode::OK, "{body}");
    assert_eq!(b.fake.heard().last().map(String::as_str), Some("revoke ben on acme/site"));
    let (_, table) = members(acme).await;
    assert!(table["members"][0]["repo_level"].is_null(), "back to the account's Read: {table}");

    let (status, body) = b.send("PUT", "/api/repos/acme/site/members/ben", &b.session("kim", Role::Viewer), Some(serde_json::json!({ "level": "admin" }))).await;
    assert_eq!(status, http::StatusCode::BAD_REQUEST, "a member is given Read or Write, nothing else: {body}");
}

/// ★ **The account admin's role follows their first member at once** (the
/// UI test of 2026-10-07 read acme as "individual" right after Add users: the
/// console believed gunnar's earlier answer for its TTL). Enrolling a member
/// forgets what this console believed about the admin, so the next
/// `/auth/status` asks gunnar again.
#[tokio::test]
async fn the_account_admins_role_follows_their_first_member_at_once() {
    let fake = Fake::default();
    fake.repo("acme", "site");
    let b = console(fake).await;
    let acme = b.session("acme", Role::Viewer);
    let (_, me) = b.send("GET", "/auth/status", &acme, None).await;
    assert_eq!(me["account_role"], "individual", "no member yet: {me}");
    let (status, body) = b
        .send("POST", "/api/account/employees", &acme, Some(serde_json::json!({ "account": "acme", "name": "ben" })))
        .await;
    assert_eq!(status, http::StatusCode::OK, "{body}");
    let (_, me) = b.send("GET", "/auth/status", &acme, None).await;
    assert_eq!(me["account_role"], "admin", "acme has a member now: {me}");
}