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

nordisk/holger

server/lib/src/room.rs

//! **Where more room for a repository is bought** (`holger.room.v1`, 2026-10-17).
//!
//! The console is a thin layer. It shows what a repository holds
//! (`UsageService.ReadUsage`, caps included) and the box's disk
//! (`AdminService.Capacity`), and for buying it links the person to THIS SITE's own buy pages
//! (A12, owner 2026-10-23: the buyer stays on the site; the shared buy flow, monetize-site-buy,
//! on holger's front relays every step to the ONE box). It never prices, orders or refuses an
//! order itself.
//!
//! **The relay's doors** ([`buy_me`], [`buy_ticket`], [`signup_account`]): the front holds the
//! site's bearer at the ONE box and asks this box who a request's person is, for that person's
//! own actor ticket (signed by this box's fleet key, as before), and whether a paid sign-up's
//! account holds its passkey yet.
//!
//! The shop is `HOLGER_SHOP_URL` when set. Without it, a metered build points at
//! the ONE shop (`ONE_SHOP`) and an open build sells nothing (an empty link).
//!
//! **The actor ticket** (metered): the ONE box sells only to a buyer the product vouched for.
//! The site's buy relay asks this box for the person's ticket ([`buy_ticket`]):
//! monetize-embed's `ActorTicket` (ten minutes, one use), signed by this box's own fleet key
//! (`fleet/box.ed25519`, the key the ONE box pinned for this main). Holger vouches for the
//! PERSON, not an amount: the plan and its price are the shop's. (The ONE box's own `/kop` page
//! that once carried it as `ticket=` is deleted: OD6, 2026-10-26.)
//!
//! **A private Holger** (`PrivateEstate`, 2026-10-21): any person signed in here may order an
//! estate of their own (Gunnar's rule, lane SU1: the order is the signed-in account's) — front, main and twin, laid by the ONE box — on
//! this site's own sign-up page (`/signup?plan=private`, `PRIVATE_CHOICE`; A12, owner 2026-10-23:
//! the buyer never sees monetize), which relays the order to the ONE box; the console follows no
//! other link (A28 M10, holger-ui `private_estate_link`). The relay names this site and the person, and carries
//! their `estate` ticket: the same `ActorTicket`, purpose `estate`, tenant the person's
//! principal, signed by the same fleet key. The ONE box checks it under the key it pinned for
//! this site, so the order is that person's and nobody else's.

use std::sync::Arc;

use tonic::{Request, Response, Status};

use crate::grpc::holger_room_proto as pb;
use crate::grpc::HolgerGrpc;

/// The ONE shop: the box that sells every product of the suite. Only the
/// metered build knows it; an open build links no shop unless told one.
#[cfg(feature = "metered")]
pub const ONE_SHOP: &str = "https://monetize.ignalina.dk";

/// The environment variable that names the shop (empty: sells nothing).
pub const SHOP_ENV: &str = "HOLGER_SHOP_URL";

/// What the console shows when this box sells nothing.
pub const SELLS_NOTHING: &str =
    "more room for this repository is set by the operator of this server";

/// What the console shows beside the shop link (the paid build's words; an open build sells
/// nothing and says none of them, A28 M13).
#[cfg(feature = "metered")]
pub const BUY_THERE: &str = "more room for this repository is bought on its plan page";

/// **The shop this build links to**: `HOLGER_SHOP_URL`, else [`default_shop`]. An open build
/// links none and does not read the variable (A28 M13): it sells nothing whatever it is told.
pub fn shop() -> String {
    #[cfg(feature = "metered")]
    return std::env::var(SHOP_ENV).unwrap_or_else(|_| default_shop().to_owned());
    #[cfg(not(feature = "metered"))]
    String::new()
}

/// The shop this build links to, before the environment is read.
pub fn default_shop() -> &'static str {
    #[cfg(feature = "metered")]
    return ONE_SHOP;
    #[cfg(not(feature = "metered"))]
    ""
}

/// What the console shows when the person may not buy (not an admin of the repository).
#[cfg(feature = "metered")]
pub const AN_ADMIN_BUYS: &str = "more room for this repository is bought by an admin of it";

/// What the console shows beside the private-Holger link.
#[cfg(feature = "metered")]
pub const ORDER_THERE: &str = "your own private Holger is ordered on this site";

/// **The site's own buy pages** (holger-ui's `/signup`, A12): the account (a free account, a
/// plan, the account's grow), the private choice, and an instance's grow.
#[cfg(feature = "metered")]
pub const ACCOUNT_PAGE: &str = "/signup?plan=account";
#[cfg(feature = "metered")]
pub const PRIVATE_CHOICE: &str = "/signup?plan=private";
#[cfg(feature = "metered")]
pub const GROW_PAGE: &str = "/signup?plan=grow";

/// What the console shows beside the grow link.
#[cfg(feature = "metered")]
pub const BUY_HERE: &str = "more room for this account is bought on its plan page";

/// What the console shows when this box sells no private Holger.
pub const ORDERS_NOTHING: &str = "a private Holger is ordered from the operator of this server";

/// Pure: **the private-Holger order link**: this site's own private choice, where a shop sells
/// here (`shop` non-empty, a site named); the words say whether the person must sign in first.
pub fn private_reply_for(shop: &str, site: &str, signed_in: bool) -> pb::PrivateEstateReply {
    if shop.trim().is_empty() || site.trim().is_empty() {
        return pb::PrivateEstateReply {
            shop_url: String::new(),
            words: ORDERS_NOTHING.to_owned(),
        };
    }
    #[cfg(not(feature = "metered"))]
    {
        // An open build orders nothing, whatever it is handed (A28 M13).
        let _ = signed_in;
        pb::PrivateEstateReply {
            shop_url: String::new(),
            words: ORDERS_NOTHING.to_owned(),
        }
    }
    #[cfg(feature = "metered")]
    pb::PrivateEstateReply {
        shop_url: PRIVATE_CHOICE.to_owned(),
        words: if signed_in {
            ORDER_THERE
        } else {
            SIGN_IN_TO_ORDER
        }
        .to_owned(),
    }
}

/// What the sign-up page shows a visitor beside the private-Holger choice.
#[cfg(feature = "metered")]
pub const SIGN_IN_TO_ORDER: &str =
    "a private Holger is ordered by a person signed in here: sign in, or buy an account first";

/// The plan word `/-/signup` answers for an account that holds no paid plan (lane G12): its
/// grow choice is "Buy a plan".
pub const PLAN_FREE: &str = "free";
/// The plan word for an account that holds one: its grow choice is "Grow your account".
#[cfg(feature = "metered")]
pub const PLAN_HELD: &str = "plan";

/// **The sign-up page's choices** (`GET /-/signup`, lanes SU1 and G12, Gunnar's flow): the ONE
/// shop's account sign-up for this site and its private-estate order — for a visitor the
/// pay-once page with no ticket, for a signed-in person with their ACCOUNT and its `estate`
/// ticket — and, signed in, the account's plan word with its grow link (the Room link) and, on
/// a sold private estate, the change of that estate's size. Empty links: this box sells
/// nothing. Every price, plan and payment is the shop's.
#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize)]
pub struct SignupLinks {
    pub account_url: String,
    pub private_offered: bool,
    pub private_url: String,
    pub signed_in: bool,
    /// The person's account: the repository a redeemed sign-up bound them to (`x14`), on a
    /// sold estate the buyer's account name, else their subject. Empty for a visitor.
    pub account: String,
    /// [`PLAN_FREE`] or [`PLAN_HELD`]; empty for a visitor.
    pub plan: String,
    /// "Buy a plan" / "Grow your account": the shop's page for more room for the account.
    pub grow_account_url: String,
    /// The company of the sold private estate this box is (`acme`); empty elsewhere.
    pub instance: String,
    /// "Grow <company>'s Holger": the shop's change of that estate's size.
    pub grow_instance_url: String,
    pub words: String,
}

/// **Who is signed in, as the sign-up page needs them**: their account, whether it holds a
/// paid plan, whether the account is theirs to grow (a sign-up bound it to them), and the
/// company of the sold estate this box is.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Signer<'a> {
    pub account: &'a str,
    pub holds_plan: bool,
    pub can_grow: bool,
    pub instance: Option<&'a str>,
}

/// Pure: the sign-up page's links at the shop `shop` (empty: none) for the site `site`, for
/// the person signed in when there is one.
#[cfg(feature = "metered")]
pub fn signup_links_for(shop: &str, site: &str, person: Option<&Signer<'_>>) -> SignupLinks {
    let mut links = SignupLinks {
        signed_in: person.is_some(),
        account: person.map(|p| p.account.to_owned()).unwrap_or_default(),
        plan: person
            .map(|p| if p.holds_plan { PLAN_HELD } else { PLAN_FREE }.to_owned())
            .unwrap_or_default(),
        ..SignupLinks::default()
    };
    let base = shop.trim().trim_end_matches('/');
    let site = site.trim();
    if base.is_empty() || site.is_empty() {
        links.words = ORDERS_NOTHING.to_owned();
        return links;
    }
    // The site's own pages (A12): the relay on the front settles the sign-up and the orders.
    links.account_url = ACCOUNT_PAGE.to_owned();
    links.private_offered = true;
    links.private_url = PRIVATE_CHOICE.to_owned();
    links.words = ORDER_THERE.to_owned();
    if let Some(p) = person {
        if p.account.is_empty() {
            links.words = SIGN_IN_TO_ORDER.to_owned();
        }
        // ★ BUY-ONE-2 (Gunnar's `may_buy`): the private tab is a buyer's; a signed-in person
        // with no account of their own (the operator) is offered none.
        if !p.can_grow {
            links.private_offered = false;
            links.private_url = String::new();
        }
        if p.can_grow {
            links.grow_account_url = ACCOUNT_PAGE.to_owned();
        }
        if let Some(company) = p.instance.filter(|c| !c.is_empty()) {
            links.instance = company.to_owned();
            links.grow_instance_url = format!("{GROW_PAGE}&instance={}", encode(company));
        }
    }
    links
}

/// The environment variable naming the site a sold private Holger was bought at
/// (`https://holger.rs`; Gunnar's `GUNNAR_UI_PROVIDER`).
pub const PROVIDER_ENV: &str = "HOLGER_PROVIDER";

/// Pure: **a provider as the operator names it** — an `https://` site with no path (Gunnar's
/// `provider_named`), else none.
pub fn provider_named(raw: Option<&str>) -> Option<String> {
    let site = raw?.trim().trim_end_matches('/');
    let host = site.strip_prefix("https://")?;
    (!host.is_empty()
        && host
            .chars()
            .all(|c| c.is_ascii_alphanumeric() || c == '.' || c == '-' || c == ':'))
    .then(|| site.to_owned())
}

/// Pure: **the provider of the sold estate `company` reached at `host`** — the estate's own name
/// less its company (`acme.holger.rs` → `https://holger.rs`); an own domain names none.
pub fn provider_from(company: &str, host: &str) -> Option<String> {
    let rest = host.trim().trim_end_matches('.').to_ascii_lowercase();
    let apex = rest.strip_prefix(&format!("{company}."))?;
    (apex.contains('.')
        && apex
            .chars()
            .all(|c| c.is_ascii_alphanumeric() || c == '.' || c == '-'))
    .then(|| format!("https://{apex}"))
}

/// Pure: **the sign-up page's links on a sold private Holger** (Gunnar's `auth::routes::shop`
/// for a provided console): nothing is bought here — no account, no grow of the account; "New
/// instance" and "Grow <company>'s Holger" are the provider's pages, and the line says where it
/// was bought. No provider known: nothing is offered.
#[cfg(feature = "metered")]
pub fn sold_links_for(
    provider: Option<&str>,
    company: &str,
    person: Option<&Signer<'_>>,
) -> SignupLinks {
    let mut links = SignupLinks {
        signed_in: person.is_some(),
        account: person.map(|p| p.account.to_owned()).unwrap_or_default(),
        plan: person
            .map(|p| if p.holds_plan { PLAN_HELD } else { PLAN_FREE }.to_owned())
            .unwrap_or_default(),
        ..SignupLinks::default()
    };
    let Some(p) = provider
        .map(|p| p.trim_end_matches('/'))
        .filter(|p| !p.is_empty())
    else {
        links.words = ORDERS_NOTHING.to_owned();
        return links;
    };
    links.private_offered = true;
    links.private_url = format!("{p}{PRIVATE_CHOICE}");
    links.words = ORDER_THERE.to_owned();
    if person.is_some() {
        links.grow_instance_url = format!("{p}{GROW_PAGE}&instance={}", encode(company));
        let site = p.strip_prefix("https://").unwrap_or(p);
        links.words =
            format!("This Holger was bought at {site}. More room for it is bought there.");
    }
    links
}

/// Pure: **an account name** as the shop's sign-up takes one (`^[a-z0-9][a-z0-9._-]{1,31}$`).
pub fn account_name_ok(name: &str) -> bool {
    let b = name.as_bytes();
    (2..=32).contains(&b.len())
        && (b[0].is_ascii_lowercase() || b[0].is_ascii_digit())
        && b.iter().all(|c| {
            c.is_ascii_lowercase() || c.is_ascii_digit() || matches!(c, b'.' | b'_' | b'-')
        })
}

/// Pure: **the person's account** (A10 row 12): the repository a redeemed sign-up bound
/// `subject` to (`sold`, the record the twin carries); on a sold estate, its buyer (the estate's
/// owners' group) by the account name the shop's first-login invite gave them (`display`);
/// anybody else by `subject`, as before.
pub fn account_for(
    sold: &std::collections::BTreeMap<String, crate::sold::SoldRepository>,
    subject: &str,
    display: &str,
    groups: &[String],
) -> String {
    if let Some((name, _)) = sold
        .iter()
        .find(|(_, s)| s.owner == subject && !s.signup.is_empty())
    {
        return name.clone();
    }
    if groups.iter().any(|g| g == crate::auth::ESTATE_OWNERS) && account_name_ok(display) {
        return display.to_owned();
    }
    subject.to_owned()
}

/// Pure: **the company of a sold private estate**, from the instance this box reports for: the
/// ONE box names a sold estate's boxes by its company (`fleet_instance = acme`), a site by its
/// host or label (`holger.rs`, `test3-holger-rs`). `None`: this box is a site.
pub fn sold_company(instance: &str) -> Option<&str> {
    let b = instance.as_bytes();
    ((1..=16).contains(&b.len())
        && b[0].is_ascii_lowercase()
        && b.iter()
            .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit()))
    .then_some(instance)
}

/// **Does `account` hold a paid plan here**: a stored fact for it that is not `Free`.
#[cfg(feature = "metered")]
pub fn account_holds_plan(data_root: &std::path::Path, account: &str) -> bool {
    let store = crate::metered::EntitlementStore::awaiting(
        crate::metered::store_dir(data_root),
        Arc::new(Default::default()),
    );
    matches!(store.get(account), Ok(Some(f)) if !matches!(f.state, monetize_product::State::Free))
}

/// **The account of the person signed in**, read from this box's sold record.
pub fn person_account(
    data_root: Option<&std::path::Path>,
    who: &crate::auth::AuthIdentity,
) -> String {
    let sold = data_root.map(crate::sold::read).unwrap_or_default();
    account_for(
        &sold,
        &who.subject,
        who.display.as_deref().unwrap_or_default(),
        &who.groups,
    )
}

/// **This site, as the shop knows it**, from what a door holds: the estate this box reports
/// for (its enrolment under `data_root`), else the passkey relying party's host.
#[cfg(feature = "metered")]
pub fn site_from(data_root: Option<&std::path::Path>, auth: &crate::auth::AuthConfig) -> String {
    let enrolled = data_root
        .and_then(|root| holger_fleet::config::FleetConfig::load(root).ok().flatten())
        .map(|c| c.instance.trim().to_owned())
        .filter(|i| !i.is_empty());
    enrolled
        .or_else(|| {
            origin_of(auth).map(|o| {
                o.trim_end_matches('/')
                    .rsplit("://")
                    .next()
                    .unwrap_or_default()
                    .to_owned()
            })
        })
        .unwrap_or_default()
}

/// The passkey relying party's origin, `https://host/`.
#[cfg(feature = "metered")]
fn origin_of(auth: &crate::auth::AuthConfig) -> Option<String> {
    auth.methods.iter().find_map(|m| match m {
        // WWW-CANON: the site's own origin (`https://www.<rp_id>`), not the kept bare one.
        crate::auth::AuthMethodConfig::Passkey {
            rp_id, rp_origin, ..
        } => Some(format!("{}/", crate::auth::site_origin(rp_id, rp_origin))),
        _ => None,
    })
}

/// **The sign-up page's links for the person asking** (`GET /-/signup`): the shop
/// (`HOLGER_SHOP_URL`, else this build's), this site, and — for somebody signed in (`who`) on a
/// metered box with a data root — their account, its plan, its grow link and its `estate`
/// ticket.
pub fn signup_links(
    data_root: Option<&std::path::Path>,
    auth: &crate::auth::AuthConfig,
    who: Option<&crate::auth::AuthIdentity>,
) -> SignupLinks {
    let shop = shop();
    #[cfg(feature = "metered")]
    {
        let site = site_from(data_root, auth);
        let person = match (who.filter(|i| !i.subject.trim().is_empty()), data_root) {
            (Some(id), Some(root)) => {
                let account = person_account(Some(root), id);
                let bound = crate::sold::read(root).contains_key(&account);
                let holds_plan = bound && account_holds_plan(root, &account);
                Some((account, holds_plan, bound))
            }
            _ => None,
        };
        let signer = person.as_ref().map(|(account, holds_plan, bound)| Signer {
            account,
            holds_plan: *holds_plan,
            can_grow: *bound,
            instance: sold_company(&site),
        });
        // A16: a SOLD private Holger's shop is its provider's (Gunnar's provided console).
        if let Some(company) = sold_company(&site) {
            let host = origin_of(auth)
                .map(|o| {
                    o.trim_end_matches('/')
                        .rsplit("://")
                        .next()
                        .unwrap_or_default()
                        .to_owned()
                })
                .unwrap_or_default();
            let provider = provider_named(std::env::var(PROVIDER_ENV).ok().as_deref())
                .or_else(|| provider_from(company, &host));
            return sold_links_for(provider.as_deref(), company, signer.as_ref());
        }
        signup_links_for(&shop, &site, signer.as_ref())
    }
    #[cfg(not(feature = "metered"))]
    {
        // An open build sells nothing from here.
        let _ = (data_root, auth, who, shop);
        SignupLinks {
            words: ORDERS_NOTHING.to_owned(),
            ..SignupLinks::default()
        }
    }
}

/// **Who asks, as the relay on the front needs them** (`GET /-/buy/me`): a visitor, or a person
/// with their account, the tenant its room is metered as (the account), whether the account
/// is theirs (a sign-up bound it), and the caps it holds now.
#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize)]
pub struct BuyMe {
    pub who: &'static str,
    pub account: String,
    pub tenant: String,
    pub bound: bool,
    pub caps: std::collections::BTreeMap<String, u64>,
}

/// **Who asks** (`GET /-/buy/me`).
pub fn buy_me(
    data_root: Option<&std::path::Path>,
    who: Option<&crate::auth::AuthIdentity>,
) -> BuyMe {
    let Some(id) = who.filter(|i| !i.subject.trim().is_empty()) else {
        return BuyMe {
            who: "visitor",
            ..BuyMe::default()
        };
    };
    let account = person_account(data_root, id);
    let bound = data_root.is_some_and(|root| crate::sold::read(root).contains_key(&account));
    #[cfg(feature = "metered")]
    let caps = match data_root.filter(|_| bound) {
        Some(root) => {
            let store = crate::metered::EntitlementStore::awaiting(
                crate::metered::store_dir(root),
                Arc::new(Default::default()),
            );
            store
                .get(&account)
                .ok()
                .flatten()
                .map(|f| f.caps)
                .unwrap_or_default()
        }
        None => Default::default(),
    };
    #[cfg(not(feature = "metered"))]
    let caps = Default::default();
    BuyMe {
        who: "person",
        tenant: account.clone(),
        account,
        bound,
        caps,
    }
}

/// What a buyer reads when the signed-in person has no account of their own here to buy for
/// (gunnar-ui-server `auth::private::NO_ACCOUNT_TO_BUY_FOR`, with Holger's name).
#[cfg(feature = "metered")]
pub const NO_ACCOUNT_TO_BUY_FOR: &str = "This sign-in has no account of its own on this Holger to buy a private Holger for. Sign in as the account that buys, or create one first.";

/// monetize-embed's `PURPOSE_CANCEL` (named here: a lone clone's crates.io monetize-embed
/// predates the constant).
#[cfg(feature = "metered")]
pub const PURPOSE_CANCEL: &str = "cancel";

/// **The person's own actor ticket for the relay** (`POST /-/buy/ticket {purpose, caps}`, hex of
/// its JSON bytes): `estate` for anyone signed in (their account orders a private Holger);
/// `order` (covering exactly `caps`), `read`, `renew` and `cancel` ("Cancel order", covering no
/// amount) only for the account that is theirs.
/// Refused as `(status, words)`.
#[cfg(feature = "metered")]
pub fn buy_ticket(
    data_root: Option<&std::path::Path>,
    who: Option<&crate::auth::AuthIdentity>,
    purpose: &str,
    caps: &std::collections::BTreeMap<String, u64>,
    now_unix_ms: i64,
) -> Result<String, (u16, String)> {
    use monetize_embed::ticket::{PURPOSE_ESTATE, PURPOSE_ORDER, PURPOSE_READ, PURPOSE_RENEW};
    let root = data_root.ok_or((503, "this box holds no fleet key to vouch with".to_owned()))?;
    let me = buy_me(Some(root), who);
    if me.who != "person" {
        return Err((401, "sign in to buy".to_owned()));
    }
    let caps = match purpose {
        PURPOSE_ESTATE => std::collections::BTreeMap::new(),
        PURPOSE_ORDER | PURPOSE_READ | PURPOSE_RENEW | PURPOSE_CANCEL if !me.bound => {
            return Err((404, NO_ACCOUNT_TO_BUY_FOR.to_owned()))
        }
        PURPOSE_ORDER => caps.clone(),
        // A cancel buys nothing, so it covers no amount (Gunnar's `cancel_ticket`).
        PURPOSE_READ | PURPOSE_RENEW | PURPOSE_CANCEL => std::collections::BTreeMap::new(),
        _ => {
            return Err((
                400,
                format!("{purpose:?} is not a ticket this site vouches for"),
            ))
        }
    };
    signed_ticket_covering(root, &me.account, purpose, caps, now_unix_ms).map_err(|why| (503, why))
}

/// **Whether the paid sign-up's account `user` holds its passkey here** (`GET /-/buy/account`,
/// the console's question): `none` (not made here yet), `no_passkey` (made, its sign-up not
/// redeemed), `has_passkey`.
pub fn signup_account(data_root: Option<&std::path::Path>, user: &str) -> &'static str {
    match data_root.and_then(|root| crate::sold::read(root).remove(user.trim())) {
        None => "none",
        Some(s) if s.owner.is_empty() => "no_passkey",
        Some(_) => "has_passkey",
    }
}

/// Pure: the reply for `repository` where the shop is `shop` (empty: none sells here): the
/// site's own grow page for a person who may buy, else the words that an admin buys.
pub fn reply_for(repository: &str, shop: &str, may_buy: bool) -> pb::RoomReply {
    if shop.trim().is_empty() || cfg!(not(feature = "metered")) {
        // No shop, or an open build, which sells nothing whatever it is handed (A28 M13).
        let _ = may_buy;
        return pb::RoomReply {
            repository: repository.to_owned(),
            shop_url: String::new(),
            words: SELLS_NOTHING.to_owned(),
        };
    }
    #[cfg(feature = "metered")]
    if !may_buy {
        return pb::RoomReply {
            repository: repository.to_owned(),
            shop_url: String::new(),
            words: AN_ADMIN_BUYS.to_owned(),
        };
    }
    #[cfg(feature = "metered")]
    return pb::RoomReply {
        repository: repository.to_owned(),
        shop_url: ACCOUNT_PAGE.to_owned(),
        words: BUY_HERE.to_owned(),
    };
    #[cfg(not(feature = "metered"))]
    unreachable!("an open build answered above")
}

/// **The actor ticket for `repository`** (metered): monetize-embed's `ActorTicket`,
/// purpose `order`, signed by this box's fleet key under `data_root`, as the hex of its
/// JSON bytes (what the buy relay's order carries as its actor ticket).
#[cfg(feature = "metered")]
pub fn ticket_for(
    data_root: &std::path::Path,
    repository: &str,
    now_unix_ms: i64,
) -> Result<String, String> {
    signed_ticket(
        data_root,
        repository,
        monetize_embed::ticket::PURPOSE_ORDER,
        now_unix_ms,
    )
}

/// **The `estate` ticket for `account`** (metered): the same ticket, purpose `estate`, the
/// tenant the person's principal — what the shop's private-estate page carries (`ticket=`).
#[cfg(feature = "metered")]
pub fn estate_ticket_for(
    data_root: &std::path::Path,
    account: &str,
    now_unix_ms: i64,
) -> Result<String, String> {
    signed_ticket(
        data_root,
        account,
        monetize_embed::ticket::PURPOSE_ESTATE,
        now_unix_ms,
    )
}

/// One ticket for `tenant` and `purpose`, ten minutes, a fresh nonce, signed by this box's
/// fleet key under `data_root`, as the hex of its JSON bytes.
#[cfg(feature = "metered")]
fn signed_ticket(
    data_root: &std::path::Path,
    tenant: &str,
    purpose: &str,
    now_unix_ms: i64,
) -> Result<String, String> {
    signed_ticket_covering(data_root, tenant, purpose, Default::default(), now_unix_ms)
}

/// [`signed_ticket`] covering `caps` (an `order` names the exact caps the order resolves to).
#[cfg(feature = "metered")]
fn signed_ticket_covering(
    data_root: &std::path::Path,
    tenant: &str,
    purpose: &str,
    caps: std::collections::BTreeMap<String, u64>,
    now_unix_ms: i64,
) -> Result<String, String> {
    use monetize_embed::ticket::{ticket_message, ActorTicket, MAX_LIFE_MS};
    let key = holger_fleet::BoxKey::load_or_mint(data_root)
        .map_err(|e| format!("this box's fleet key: {e}"))?;
    let mut raw = [0u8; 16];
    getrandom::fill(&mut raw).map_err(|e| format!("no randomness for the ticket's nonce: {e}"))?;
    let mut t = ActorTicket::unsigned(
        holger_fleet::PRODUCT,
        holger_fleet::PRODUCT,
        tenant,
        purpose,
        holger_fleet::hex(&raw),
        now_unix_ms,
        MAX_LIFE_MS,
    )
    .covering(caps);
    t.signature = monetize_embed::signing::base64_encode(&key.sign_bytes(&ticket_message(&t)));
    serde_json::to_vec(&t)
        .map(|b| holger_fleet::hex(&b))
        .map_err(|e| e.to_string())
}

/// **This site, as the shop knows it**: the estate this box reports for (its enrolment,
/// `fleet/fleet.json`), else the console's own host.
#[cfg(feature = "metered")]
fn this_site(g: &HolgerGrpc) -> String {
    site_from(g.data_root.as_deref(), &g.auth_config)
}

/// Percent-encode everything but the unreserved set.
#[cfg(feature = "metered")]
fn encode(s: &str) -> String {
    s.bytes()
        .map(|b| match b {
            b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'.' | b'_' | b'~' => {
                (b as char).to_string()
            }
            _ => format!("%{b:02X}"),
        })
        .collect()
}

#[tonic::async_trait]
impl pb::room_service_server::RoomService for Arc<HolgerGrpc> {
    async fn room(
        &self,
        request: Request<pb::RoomRequest>,
    ) -> Result<Response<pb::RoomReply>, Status> {
        let repository = request.get_ref().repository.clone();
        // READER on the repository, the gate ReadUsage passes.
        self.authorize_read(&request, &repository).await?;
        let _ = self.get_repo(&repository)?;
        let shop = shop();
        let may_buy = self.authorize_admin(&request, &repository).await.is_ok();
        Ok(Response::new(reply_for(&repository, &shop, may_buy)))
    }

    async fn private_estate(
        &self,
        request: Request<pb::PrivateEstateRequest>,
    ) -> Result<Response<pb::PrivateEstateReply>, Status> {
        let shop = shop();
        #[cfg(feature = "metered")]
        {
            let site = this_site(self);
            // ANY person signed in here orders (Gunnar's rule), on the site's private choice.
            let signed_in = matches!(
                self.authenticate(&request, "order a private Holger").await,
                Ok(Some(_))
            );
            Ok(Response::new(private_reply_for(&shop, &site, signed_in)))
        }
        #[cfg(not(feature = "metered"))]
        {
            let _ = request;
            Ok(Response::new(private_reply_for(&shop, "", false)))
        }
    }
}

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

    /// ★ A12: **the console's way to more room is the site's own grow page**, never the shop's:
    /// an admin of the repository is linked there, anyone else told an admin buys.
    #[cfg(feature = "metered")]
    #[test]
    fn the_room_link_is_the_sites_own_grow_page() {
        let r = reply_for("my repo", "https://monetize.ignalina.dk/", true);
        assert_eq!(
            (r.shop_url.as_str(), r.words.as_str(), r.repository.as_str()),
            (ACCOUNT_PAGE, BUY_HERE, "my repo")
        );
        let other = reply_for("my repo", "https://monetize.ignalina.dk/", false);
        assert_eq!(
            (other.shop_url.as_str(), other.words.as_str()),
            ("", AN_ADMIN_BUYS)
        );
    }

    #[test]
    fn no_shop_sells_nothing() {
        let r = reply_for("crates", "  ", true);
        assert!(r.shop_url.is_empty());
        assert_eq!(r.words, SELLS_NOTHING);
    }

    /// ★ **The ticket is one the ONE shop's own verifier takes**: monetize-embed's
    /// `verify_ticket`, under this box's fleet key, for exactly this repository and verb.
    #[cfg(feature = "metered")]
    #[test]
    fn the_minted_ticket_verifies_under_the_boxs_fleet_key_for_this_repository_only() {
        let dir = tempfile::tempdir().unwrap();
        let now = 1_800_000_000_000;
        let hex = ticket_for(dir.path(), "crates", now).unwrap();
        let bytes: Vec<u8> = (0..hex.len())
            .step_by(2)
            .map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap())
            .collect();
        let key = monetize_embed::VerifyingKey::from_bytes(
            &holger_fleet::BoxKey::load_or_mint(dir.path())
                .unwrap()
                .public(),
        )
        .unwrap();
        let none = std::collections::BTreeMap::new();
        let t = monetize_embed::ticket::verify_ticket(
            &bytes,
            &key,
            "holger",
            "crates",
            "order",
            &none,
            now + 1,
        )
        .expect("the shop's verifier takes it");
        assert_eq!((t.issuer.as_str(), t.tenant.as_str()), ("holger", "crates"));
        assert!(
            monetize_embed::ticket::verify_ticket(
                &bytes,
                &key,
                "holger",
                "other",
                "order",
                &none,
                now + 1
            )
            .is_err(),
            "another repository: refused"
        );
        assert!(
            monetize_embed::ticket::verify_ticket(
                &bytes,
                &key,
                "holger",
                "crates",
                "order",
                &none,
                now + 11 * 60_000
            )
            .is_err(),
            "ten minutes, no more"
        );
    }

    /// ★ **The sign-up page's choices are the site's own pages** (A12, owner 2026-10-23: the
    /// buyer stays on the site; the only outside page is the vendor's checkout). A visitor gets
    /// the account and the private choice; a signed-in person their ACCOUNT, its plan word, the
    /// grow-account page when the account is theirs, and on a sold private estate "Grow
    /// <company>'s Holger". No shop or no site: nothing offered. No ticket rides in a link.
    #[cfg(feature = "metered")]
    #[test]
    fn the_signup_page_offers_the_sites_own_pages_and_never_the_shops() {
        let visitor = signup_links_for("https://monetize.ignalina.dk/", "holger.rs", None);
        assert_eq!(
            (visitor.account_url.as_str(), visitor.private_url.as_str()),
            (ACCOUNT_PAGE, PRIVATE_CHOICE)
        );
        assert!(visitor.private_offered && !visitor.signed_in);
        assert_eq!(
            (
                visitor.account.as_str(),
                visitor.plan.as_str(),
                visitor.grow_account_url.as_str(),
                visitor.instance.as_str(),
                visitor.grow_instance_url.as_str()
            ),
            ("", "", "", "", "")
        );
        assert_eq!(visitor.words, ORDER_THERE);
        let free = Signer {
            account: "x14",
            holds_plan: false,
            can_grow: true,
            instance: None,
        };
        let x14 = signup_links_for("https://monetize.ignalina.dk", "holger.rs", Some(&free));
        assert_eq!(x14.private_url, PRIVATE_CHOICE);
        assert!(x14.private_offered && x14.signed_in);
        assert_eq!(
            (
                x14.account.as_str(),
                x14.plan.as_str(),
                x14.grow_account_url.as_str()
            ),
            ("x14", PLAN_FREE, ACCOUNT_PAGE)
        );
        assert_eq!(
            (x14.instance.as_str(), x14.grow_instance_url.as_str()),
            ("", ""),
            "no instance owned here: no grow-instance choice"
        );
        let unbound = Signer {
            can_grow: false,
            ..free.clone()
        };
        assert_eq!(
            signup_links_for("https://monetize.ignalina.dk", "holger.rs", Some(&unbound))
                .grow_account_url,
            "",
            "no account of their own: nothing to grow"
        );
        // ★ BUY-ONE-2 (owner GO 2026-10-27; Gunnar's `may_buy`, the server decides): the one buy
        // page's "New private instance" tab is a buyer's, never a signed-in person with no
        // account of their own (the operator).
        let none_here =
            signup_links_for("https://monetize.ignalina.dk", "holger.rs", Some(&unbound));
        assert_eq!(
            (none_here.private_offered, none_here.private_url.as_str()),
            (false, ""),
            "no private tab for the operator"
        );
        // (A sold private estate's tab is its provider's page, as Gunnar's provided console: not
        // this box's ladder; `a_sold_instance_points_at_its_provider_and_sells_nothing_itself`.)
        let paid = Signer {
            holds_plan: true,
            ..free.clone()
        };
        assert_eq!(
            signup_links_for("https://monetize.ignalina.dk", "holger.rs", Some(&paid)).plan,
            PLAN_HELD
        );
        for l in [&visitor, &x14] {
            assert!(
                !serde_json::to_string(l).unwrap().contains("monetize."),
                "no link leaves the site: {l:?}"
            );
        }
        for (shop, site) in [("", "holger.rs"), ("https://monetize.ignalina.dk", " ")] {
            let none = signup_links_for(shop, site, Some(&free));
            assert_eq!(
                (
                    none.account_url.as_str(),
                    none.private_offered,
                    none.private_url.as_str(),
                    none.grow_account_url.as_str()
                ),
                ("", false, "", ""),
                "{shop:?} {site:?}"
            );
            assert_eq!(none.words, ORDERS_NOTHING);
        }
    }

    /// ★ **A sold private Holger sells nothing itself: its provider does** (A16, Gunnar's
    /// `auth::routes::shop` for a provided console): no account and no grow of the account here,
    /// "New instance" and "Grow <company>'s Holger" are the provider's pages (`https://holger.rs/
    /// signup?plan=…`), and the line says where it was bought. The provider is the operator's
    /// word (`HOLGER_PROVIDER`), else the estate's own name less its company (`acme.holger.rs` →
    /// `holger.rs`); none known, nothing is offered.
    #[cfg(feature = "metered")]
    #[test]
    fn a_sold_instance_points_at_its_provider_and_sells_nothing_itself() {
        assert_eq!(
            provider_named(Some("https://holger.rs/")),
            Some("https://holger.rs".to_owned())
        );
        assert_eq!(provider_named(Some("http://holger.rs")), None);
        assert_eq!(
            provider_from("acme", "acme.holger.rs"),
            Some("https://holger.rs".to_owned())
        );
        assert_eq!(
            provider_from("acme", "git.acme.com"),
            None,
            "an own domain names no provider"
        );
        let owner = Signer {
            account: "x14",
            holds_plan: true,
            can_grow: false,
            instance: Some("acme"),
        };
        let acme = sold_links_for(Some("https://holger.rs"), "acme", Some(&owner));
        assert_eq!(
            (
                acme.account_url.as_str(),
                acme.grow_account_url.as_str(),
                acme.instance.as_str()
            ),
            ("", "", ""),
            "nothing is bought here"
        );
        assert!(acme.private_offered && acme.signed_in);
        assert_eq!(acme.private_url, "https://holger.rs/signup?plan=private");
        assert_eq!(
            acme.grow_instance_url,
            "https://holger.rs/signup?plan=grow&instance=acme"
        );
        assert_eq!(
            acme.words,
            "This Holger was bought at holger.rs. More room for it is bought there."
        );
        let visitor = sold_links_for(Some("https://holger.rs"), "acme", None);
        assert_eq!(
            (
                visitor.private_url.as_str(),
                visitor.grow_instance_url.as_str(),
                visitor.account_url.as_str()
            ),
            ("https://holger.rs/signup?plan=private", "", "")
        );
        let unknown = sold_links_for(None, "acme", Some(&owner));
        assert_eq!(
            (
                unknown.private_offered,
                unknown.private_url.as_str(),
                unknown.grow_instance_url.as_str(),
                unknown.words.as_str()
            ),
            (false, "", "", ORDERS_NOTHING)
        );
    }

    /// ★ **The person's account** (A10 row 12): the repository a redeemed sign-up bound their
    /// passkey to (its name, `x14`), never the opaque `passkey:<uuid>`; on a sold estate its
    /// buyer (the estate's owners' group) by the account name the shop's invite gave them; anybody
    /// else by their subject as before. **Its plan**: paid while a non-Free fact
    /// is stored for it, else free.
    #[test]
    fn a_person_is_named_by_the_account_their_passkey_is_bound_to() {
        let mut sold = std::collections::BTreeMap::new();
        sold.insert(
            "x14".to_string(),
            crate::sold::SoldRepository {
                template: "crates".into(),
                signup: "cs_1".into(),
                owner: "passkey:aa".into(),
                ..Default::default()
            },
        );
        sold.insert(
            "team".to_string(),
            crate::sold::SoldRepository {
                template: "crates".into(),
                signup: String::new(),
                owner: "passkey:aa".into(),
                ..Default::default()
            },
        );
        let owners = [crate::auth::ESTATE_OWNERS.to_string()];
        assert_eq!(
            account_for(&sold, "passkey:aa", "nina", &[]),
            "x14",
            "the signed-up repository names the account"
        );
        assert_eq!(
            account_for(&sold, "passkey:bb", "olle", &[]),
            "passkey:bb",
            "no bound repository: the subject"
        );
        assert_eq!(account_for(&sold, "console", "", &[]), "console");
        assert_eq!(
            account_for(&sold, "passkey:cc", "x14", &owners),
            "x14",
            "a sold estate's buyer is the account the shop's first-login invite named"
        );
        assert_eq!(
            account_for(&sold, "passkey:cc", "Not A Name", &owners),
            "passkey:cc",
            "a display that is no account name: the subject"
        );
        assert_eq!(
            sold_company("acme"),
            Some("acme"),
            "a sold private estate reports as its company"
        );
        for site in ["holger.rs", "test3.holger.rs", "", "Acme", "1acme"] {
            assert_eq!(
                sold_company(site),
                None,
                "{site:?} is a site, not a sold estate's company"
            );
        }
    }

    /// ★ A12: **a private Holger is ordered on the site's own private choice**; nobody signed in
    /// is told to sign in or buy an account first; no shop or no site, nothing.
    #[cfg(feature = "metered")]
    #[test]
    fn the_private_holger_link_is_the_sites_own_private_choice() {
        let r = private_reply_for("https://monetize.ignalina.dk/", "test3-holger-rs", true);
        assert_eq!(
            (r.shop_url.as_str(), r.words.as_str()),
            (PRIVATE_CHOICE, ORDER_THERE)
        );
        let anyone = private_reply_for("https://monetize.ignalina.dk", "test3.holger.rs", false);
        assert_eq!(
            (anyone.shop_url.as_str(), anyone.words.as_str()),
            (PRIVATE_CHOICE, SIGN_IN_TO_ORDER)
        );
        for (shop, site) in [
            ("", "test3-holger-rs"),
            ("https://monetize.ignalina.dk", " "),
        ] {
            let none = private_reply_for(shop, site, true);
            assert!(none.shop_url.is_empty(), "{shop:?} {site:?}");
            assert_eq!(none.words, ORDERS_NOTHING);
        }
    }

    /// ★ **The estate ticket is one the ONE box's site check takes**: purpose `estate`, the
    /// person's principal, under this box's fleet key — and no order ticket.
    #[cfg(feature = "metered")]
    #[test]
    fn the_estate_ticket_verifies_under_the_boxs_fleet_key_for_this_person_and_the_estate_verb_only(
    ) {
        let dir = tempfile::tempdir().unwrap();
        let now = 1_800_000_000_000;
        let hex = estate_ticket_for(dir.path(), "nina", now).unwrap();
        let bytes: Vec<u8> = (0..hex.len())
            .step_by(2)
            .map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap())
            .collect();
        let key = monetize_embed::VerifyingKey::from_bytes(
            &holger_fleet::BoxKey::load_or_mint(dir.path())
                .unwrap()
                .public(),
        )
        .unwrap();
        let none = std::collections::BTreeMap::new();
        let t = monetize_embed::ticket::verify_ticket(
            &bytes,
            &key,
            "holger",
            "nina",
            "estate",
            &none,
            now + 1,
        )
        .expect("the shop's verifier takes it");
        assert_eq!(
            (t.product.as_str(), t.tenant.as_str(), t.purpose.as_str()),
            ("holger", "nina", "estate")
        );
        assert!(
            monetize_embed::ticket::verify_ticket(
                &bytes,
                &key,
                "holger",
                "nina",
                "order",
                &none,
                now + 1
            )
            .is_err(),
            "not an order ticket"
        );
        assert!(
            monetize_embed::ticket::verify_ticket(
                &bytes,
                &key,
                "holger",
                "olle",
                "estate",
                &none,
                now + 1
            )
            .is_err(),
            "another person: refused"
        );
    }

    /// ★ A12: **the relay's doors on this box** — who asks, the product's ticket for them (an
    /// `order` ticket covers exactly the caps the order resolves to, and only for the account
    /// that is theirs), and whether a paid sign-up's account holds its passkey yet.
    #[cfg(feature = "metered")]
    #[test]
    fn the_buy_doors_vouch_for_the_person_and_their_own_account_only() {
        let dir = tempfile::tempdir().unwrap();
        let root = dir.path();
        let now = 1_800_000_000_000;
        crate::sold::update(root, |made| {
            made.insert(
                "x14".into(),
                crate::sold::SoldRepository {
                    template: "crates".into(),
                    signup: "stripe:cs_test_abcdefghij".into(),
                    owner: "passkey:aa".into(),
                    ..Default::default()
                },
            );
            made.insert(
                "fresh".into(),
                crate::sold::SoldRepository {
                    template: "crates".into(),
                    signup: "stripe:cs_test_klmnopqrst".into(),
                    ..Default::default()
                },
            );
            Ok(())
        })
        .unwrap();
        let x14 = crate::auth::AuthIdentity {
            subject: "passkey:aa".into(),
            ..Default::default()
        };
        let olle = crate::auth::AuthIdentity {
            subject: "olle".into(),
            ..Default::default()
        };
        let me = buy_me(Some(root), Some(&x14));
        assert_eq!(
            (me.who, me.account.as_str(), me.tenant.as_str(), me.bound),
            ("person", "x14", "x14", true)
        );
        assert_eq!(buy_me(Some(root), None).who, "visitor");
        assert!(
            !buy_me(Some(root), Some(&olle)).bound,
            "no account of their own here"
        );
        let key = monetize_embed::VerifyingKey::from_bytes(
            &holger_fleet::BoxKey::load_or_mint(root).unwrap().public(),
        )
        .unwrap();
        let unhex = |s: &str| -> Vec<u8> {
            (0..s.len())
                .step_by(2)
                .map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
                .collect()
        };
        let caps = std::collections::BTreeMap::from([("disk_bytes".to_string(), 15u64)]);
        let order = buy_ticket(Some(root), Some(&x14), "order", &caps, now)
            .expect("their own account's order");
        monetize_embed::ticket::verify_ticket(
            &unhex(&order),
            &key,
            "holger",
            "x14",
            "order",
            &caps,
            now + 1,
        )
        .expect("covers exactly the caps");
        assert!(
            monetize_embed::ticket::verify_ticket(
                &unhex(&order),
                &key,
                "holger",
                "x14",
                "order",
                &std::collections::BTreeMap::new(),
                now + 1
            )
            .is_err(),
            "other caps: refused"
        );
        let estate = buy_ticket(
            Some(root),
            Some(&olle),
            "estate",
            &std::collections::BTreeMap::new(),
            now,
        )
        .expect("anyone signed in orders a private Holger");
        monetize_embed::ticket::verify_ticket(
            &unhex(&estate),
            &key,
            "holger",
            "olle",
            "estate",
            &std::collections::BTreeMap::new(),
            now + 1,
        )
        .expect("their own estate ticket");
        // ★ A16: Gunnar's words and code for a sign-in with no account of its own to buy for
        // (gunnar-ui-server `auth::private::ticket_refusal`, NOT_FOUND).
        assert_eq!(
            buy_ticket(Some(root), Some(&olle), "order", &caps, now).unwrap_err(),
            (404, NO_ACCOUNT_TO_BUY_FOR.to_owned()),
            "no account of their own: nothing to grow"
        );
        assert_eq!(NO_ACCOUNT_TO_BUY_FOR, "This sign-in has no account of its own on this Holger to buy a private Holger for. Sign in as the account that buys, or create one first.");
        assert_eq!(
            buy_ticket(Some(root), None, "estate", &caps, now)
                .unwrap_err()
                .0,
            401
        );
        assert_eq!(
            buy_ticket(Some(root), Some(&x14), "refund", &caps, now)
                .unwrap_err()
                .0,
            400
        );
        // ★ A16: "Cancel order" (the shared relay's `Purchase.Cancel`) is vouched for with a
        // `cancel` ticket that covers no amount, for the account's own orders only.
        let cancel = buy_ticket(Some(root), Some(&x14), "cancel", &caps, now)
            .expect("their own account's cancel");
        monetize_embed::ticket::verify_ticket(
            &unhex(&cancel),
            &key,
            "holger",
            "x14",
            "cancel",
            &std::collections::BTreeMap::new(),
            now + 1,
        )
        .expect("a cancel covers no amount");
        assert_eq!(
            buy_ticket(Some(root), Some(&olle), "cancel", &caps, now)
                .unwrap_err()
                .0,
            404,
            "no account of their own: nothing to cancel"
        );
        assert_eq!(
            (
                signup_account(Some(root), "fresh"),
                signup_account(Some(root), "x14"),
                signup_account(Some(root), "nobody")
            ),
            ("no_passkey", "has_passkey", "none")
        );
    }

    /// ★ A16 (owner 2026-10-23: a buyer never sees monetize): **the words a Holger buyer reads
    /// name no shop** — room is bought on the account's plan page, a private Holger on this site.
    #[cfg(feature = "metered")]
    #[test]
    fn the_buyers_words_name_no_shop() {
        for w in [
            BUY_THERE,
            AN_ADMIN_BUYS,
            ORDER_THERE,
            BUY_HERE,
            SIGN_IN_TO_ORDER,
            ORDERS_NOTHING,
        ] {
            assert!(
                !w.contains("shop") && !w.contains("monetize") && !w.contains("ONE box"),
                "{w}"
            );
        }
    }

    #[test]
    fn the_open_build_links_no_shop_by_default() {
        assert_eq!(default_shop().is_empty(), !cfg!(feature = "metered"));
    }

    /// ★ A28 M13: **an open build sells nothing whatever it is handed**: a shop named, a site
    /// named, an admin asking, the words are the operator's and no link is given.
    #[cfg(not(feature = "metered"))]
    #[test]
    fn an_open_build_offers_no_room_and_no_private_holger_even_with_a_shop() {
        let r = reply_for("crates", "https://shop.mock.invalid/", true);
        assert_eq!((r.shop_url.as_str(), r.words.as_str()), ("", SELLS_NOTHING));
        let p = private_reply_for("https://shop.mock.invalid/", "holger.mock.invalid", true);
        assert_eq!(
            (p.shop_url.as_str(), p.words.as_str()),
            ("", ORDERS_NOTHING)
        );
        assert_eq!(shop(), "", "an open build reads no HOLGER_SHOP_URL");
    }
}