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/api/increase.rs

//! **More room for one namespace**, asked for by the namespace that wants it.
//!
//! Compiled only under `metered`, and even then the routes exist only when a
//! monetize bearer is configured. On a community appliance this file is not in
//! the binary; on a metered one without the credential there is no route, not a
//! disabled button and not an error — the console simply has nothing to say
//! about it, exactly as it has nothing to say about anonymous browsing without
//! a guest key.
//!
//! # The rule this module is written under
//!
//! **This console contains no price, no currency, no size and no menu of
//! bundles.** It renders what monetize answers. The same code has to draw
//! nothing at all when monetize is not configured, and a real offer when it is,
//! because the SERVER differs and the console does not. The rule, 2026-09-07:
//! hardcode nothing — monetize fills in both the "what" and the price.
//!
//! What that rule costs, stated plainly because it is a finding and not an
//! oversight: **the price is not sent to the browser at all.**
//! `QuoteResponse.price` is a `Money { minor, Currency }` — minor units and an
//! enum. Turning that into `10 €` needs a division by 100 and a map from
//! `CURRENCY_EUR` to a symbol, which is money arithmetic and a currency
//! literal, and both are the thing being forbidden. So a QUOTE still carries no
//! amount to the browser: [`render_offer`] drops `price` and `cost` on the
//! floor, and what a typed order costs is shown by the vendor on the page the
//! tenant is sent to, which is where the card is anyway.
//!
//! That last sentence used to end *"if a price is ever to appear here, monetize
//! must send it as words"*, and monetize now does — `QuoteResponse.price_words`
//! (2026-09-17) and [`Bundle::price_words`] (2026-09-19), both written by one
//! function upstream so a window, a quote and a receipt cannot be formatted
//! three ways. Words are carriable; `Money { minor, currency }` is not. The
//! rule did not move — this console still composes no amount — the wire did.
//!
//! # What is purchasable comes from monetize, not from a list written here
//!
//! [`sellable`] asks `Products.List` and keeps the meters monetize says are
//! **enforced and priced**: `Enforcement::ENFORCED` (the cap is a wall the
//! tenant actually hits) and a non-empty `MeterInfo.unit` (the price-list unit
//! it is billed by; empty means it costs nothing to promise). Both are the
//! product plugin's own words. A console that carried its own list would sell
//! `repos` — which is `MEASURED` in gunnar today, a limit nothing enforces —
//! and the tenant would have no way to tell.
//!
//! # The increment monetize could not supply, and now can
//!
//! This paragraph used to read: *"there is no message anywhere in `monetize.v1`
//! for '10 GiB for 10 €' as a purchasable thing … so the amount is a number the
//! tenant types."* That was the honest answer to the rule and it produced a
//! debugging form — two free-text boxes asking a customer for a meter name and
//! a count of bytes.
//!
//! `Purchase.Bundles` (vendored 2026-09-19) is the message that was missing. It
//! answers with offers, and every string on one is monetize's: the label it
//! composed from the delta, the amount ALREADY RENDERED as words, the period,
//! and `can_serve` asked through the same quote the order itself will run. So
//! the menu is still not invented here — it is read — and the rule is
//! unchanged: [`bundles`] carries those strings through untouched and
//! [`start_bundle`] echoes back an id it never parses.
//!
//! **The typed form stays.** A deployment that declared no bundles answers with
//! an empty list, and typing a size nobody declared is then the only way to ask
//! for one. Buttons when there are offers, boxes when there are not; the
//! console-side fork is `gunnar_ui_web::offers::room` and it is tested there.
//!
//! # `cost` is the provider's number and never leaves this process
//!
//! `QuoteResponse` carries two amounts. `price` is what the customer pays;
//! `cost` is what Vetra pays UpCloud for the iron. They are the two halves of a
//! margin, and a margin is not a thing a customer is shown. [`Offer`] has no
//! field for either, and `the_offer_carries_no_cost` drives a real request
//! through the router against a monetize that answers with a distinctive one
//! and asserts the number is nowhere in the response body — not the struct's
//! shape, the bytes on the wire.
//!
//! # The order of the three calls, and why `Quote` is not optional
//!
//! `Quote` prices an order and moves nothing. `Start` runs the same order and
//! reserves the money. The console always quotes first and shows what came
//! back, so the button a tenant presses is under the answer to the question
//! they asked. When `can_serve` is false there is no button at all and
//! monetize's `reason` is rendered verbatim — on `gunnar.rs` today that is the
//! usual state, because `Admin.Capacity` says the box is short, and a refusal
//! that reads as deliberate is the difference between an honest wall and a
//! console that looks broken.
//!
//! `Start` answers with a `redirect_url` when the vendor needs the customer —
//! a hosted Stripe Checkout session, since a 3-D Secure card confirmed in place
//! answers `requires_action` with no URL at all and under SCA that is the
//! ordinary European case. So this console never sees a card number, holds no
//! payment form and loads no vendor script: it sends the tenant to a page the
//! vendor hosts. The outcome comes back to MONETIZE by webhook, never to the
//! browser's return URL, so the console polls [`status`] and believes only
//! that.

use crate::api::ApiError;
use crate::mpb;

// ── The shop window ────────────────────────────────────────────────────────

/// **ONE THING THIS DEPLOYMENT WILL SELL, in monetize's own words.**
///
/// Written 2026-09-19 against the panel this module's own docs describe as the
/// thing it could not build:
///
/// > *"What monetize CANNOT supply is the increment. There is no message
/// > anywhere in `monetize.v1` for '10 GiB for 10 €' as a purchasable thing …
/// > so the amount is a number the tenant types."*
///
/// That is no longer true. `Purchase.Bundles` is the message, and every string
/// on this struct is monetize's, carried verbatim:
///
/// * [`Bundle::id`] is **opaque**. It is echoed back on the order and NEVER
///   parsed — an operator who names one `bigger` must break nothing;
/// * [`Bundle::label`] is what is being sold, composed by monetize from the
///   delta so no operator can write a window that disagrees with the order;
/// * [`Bundle::price_words`] is the amount ALREADY RENDERED. `Money { minor,
///   currency }` never reaches this process on this path, which is what makes a
///   price sayable here at all: this console may hold no currency literal and
///   do no money arithmetic, so a number it would have to divide by 100 is a
///   number it cannot show. `price_words` is named for the property rather than
///   for the field upstream, exactly as `Past::price_words` is;
/// * [`Bundle::can_serve`] and [`Bundle::reason`] are the same three questions
///   the order itself will ask, asked before the customer is shown a button.
///
/// Field by field and never `serde` over the proto message — the rule
/// [`render_offer`] was written under, and for the same reason.
pub use monetize_site_buy::purchase::{Bundle, Plan, Window, AWAITING_PROVIDER, NOTHING_FITS, PROVIDER_LATE};
/// The operator's doors' answers, moved to monetize-site-buy with the doors (G-REDS 4a).
pub use monetize_site_buy::purchase::{Offer, Placed, Purchasable, Sellable};


/// `PlanStatus` as the browser sees it.
pub fn render_plan(p: mpb::PlanStatus) -> Plan {
    monetize_site_buy::purchase::render_plan(crate::monetize::wire(&p))
}

/// `Bundle` as the browser sees it. One function, used by both consoles.
pub fn render_bundle(b: mpb::Bundle) -> Bundle {
    monetize_site_buy::purchase::render_bundle(crate::monetize::wire(&b))
}


/// **monetize's list, reduced to the rows it can serve.**
///
/// One function for both consoles, so the operator's window and the tenant's
/// cannot disagree about what is on sale. Each withheld row goes to the journal
/// with the sentence monetize attached to it — the operator's reason is already
/// in monetize's own journal, on the line that produced the verdict.
pub fn window(bundles: Vec<mpb::Bundle>) -> Window {
    monetize_site_buy::purchase::window(bundles.iter().map(crate::monetize::wire).collect())
}

/// **monetize's whole answer to `Purchase.Bundles`, as the browser sees it**:
/// [`window`] over its rows, or — when monetize says the window is still being
/// read — no rows, nothing withheld, and the two lines the panel draws while
/// it asks again. One function for both consoles.
pub fn window_of(answer: mpb::ListBundlesResponse) -> Window {
    monetize_site_buy::purchase::window_of(crate::monetize::wire(&answer))
}

/// **A market word to monetize's enum** — the three this estate sells in, and
/// a refusal NAMING anything else before monetize is dialled (a typo is not
/// the default market).
pub fn market_of(word: Option<&str>) -> Result<mpb::Market, ApiError> {
    crate::auth::validate::market(word).map_err(|refusal| ApiError::from(tonic::Status::invalid_argument(refusal)))
}

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

    /// ★ G-REDS 4a: **the operator's orders for a tenant are the shared buy flow's ROUTER**
    /// (`monetize_site_buy::router_of`, its `operator` and `operator_orders` groups, mounted
    /// with the account's doors): what is for sale, the offer, the typed and the window order,
    /// the window and the follow. What this console supplies is `GunnarSite::operator` (an
    /// administrator, the open console on loopback, the operator's payer). The tests below drive
    /// the console's real router, so they hold wherever the doors are answered.
    #[test]
    fn the_operator_doors_are_the_shared_routers() {
        let here = include_str!("increase.rs");
        let serve = include_str!("../serve.rs");
        for door in ["purchasable", "offer", "start", "status", "bundles", "start_bundle"] {
            assert_eq!(here.matches(&["pub async fn ", door, "("].concat()).count(), 0, "{door} is the shared router's, not a handler here");
            assert_eq!(serve.matches(&["api::increase::", door, ")"].concat()).count(), 0, "{door}: no route by hand");
        }
        assert!(serve.contains("operator: true"), "serve.rs mounts the shared operator doors");
    }

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

    use tower::ServiceExt as _;

    use crate::mpb::products_server::{Products as ProductsService, ProductsServer};
    use crate::mpb::purchase_server::{Purchase as PurchaseService, PurchaseServer};

    /// **The bearer the fake monetize insists on**, so a test can prove the
    /// console presented THIS credential and not one of the other two it holds.
    const BEARER: &str = "the-monetize-bearer";

    /// **The provider's cost, and it is nothing like the price.**
    ///
    /// 133_700 against a price of 1_000: no digit of one is a prefix of the
    /// other, neither is a round multiple of the other, and neither is zero. A
    /// leak of `cost` under any name — a field called `price`, a stray
    /// serialisation of the whole message — puts these exact digits in the body,
    /// and a fixture where the two were equal (or where cost was 0, which is
    /// what it is on the box's own iron) would pass against a handler that sent
    /// the wrong one.
    const COST_MINOR: u64 = 133_700;
    const PRICE_MINOR: u64 = 1_000;

    /// **The vendor's own checkout page.** A `cs_` session id no template of
    /// ours could mint, which is the point: the console must carry the link the
    /// vendor invented, byte for byte, and not compose one.
    const CHECKOUT_URL: &str = "https://checkout.stripe.com/c/pay/cs_test_a1b2c3d4e5";

    /// **One offer, in monetize's words.** Every one of these four is a string
    /// the console must carry unchanged and could not have composed: the id is
    /// opaque, the label is monetize's rendering of the delta, and the amount is
    /// WORDS — this console holds no currency literal and does no arithmetic, so
    /// a body missing this exact text is a body it tried to compose one into.
    const BUNDLE_ID: &str = "10gb-1m";
    const BUNDLE_LABEL: &str = "10.0 GiB more room";
    const BUNDLE_PRICE_WORDS: &str = "220.00 SEK";
    const BUNDLE_REFUSAL: &str = "this box has no room left to promise right now";

    /// A monetize that quotes, and records every call it was asked to make.
    #[derive(Clone, Default)]
    struct FakeMonetize {
        /// Every RPC that reached this server, in order. The authorisation
        /// tests assert this is EMPTY: a refusal that still made the call has
        /// already told monetize about a tenant it should not have named.
        calls: Arc<Mutex<Vec<String>>>,
        /// What `Quote` says about serving it.
        can_serve: bool,
        reason: String,
        /// **What every `Purchase.Start` actually carried**: `(bundle, whether
        /// it named caps)`. The two fields are mutually exclusive on the wire
        /// and a real monetize refuses a request that sets both, so a console
        /// that sent the id AND a cap map would be refused in production and
        /// pass a test that only looked at the id.
        ordered: Arc<Mutex<Vec<(String, bool)>>>,
        /// The market every `Purchase.Start` / `Purchase.Bundles` named.
        markets: Arc<Mutex<Vec<i32>>>,
        /// **What `Purchase.Bundles` lists**, when a test wants something other
        /// than the usual two rows (one servable, one not): `Some(vec![])` is a
        /// deployment that declared nothing, `Some(all refused)` is a shop with
        /// rows and no room.
        declared: Option<Vec<mpb::Bundle>>,
        /// **`Purchase.Bundles` says the window is still being read**
        /// (`ListBundlesResponse.reading`, test2 2026-10-05): no rows.
        reading: bool,
        /// **The payer every `Purchase.Start` named** (g-receipts): the
        /// address Stripe mails the order's receipt to, or empty.
        payers: Arc<Mutex<Vec<String>>>,
        /// The vendor link `Purchase.Start` answers with.
        checkout_url: &'static str,
    }

    impl FakeMonetize {
        fn serving() -> FakeMonetize {
            FakeMonetize {
                can_serve: true,
                checkout_url: CHECKOUT_URL,
                ..Default::default()
            }
        }

        fn record(
            &self,
            request: &tonic::Request<impl Sized>,
            what: &str,
        ) -> Result<(), tonic::Status> {
            let offered = request
                .metadata()
                .get("authorization")
                .and_then(|v| v.to_str().ok())
                .unwrap_or_default()
                .to_owned();
            if offered != format!("Bearer {BEARER}") {
                // What a real monetize does with a caller it cannot name. The
                // console holds three credentials and only one of them opens
                // this door.
                return Err(tonic::Status::unauthenticated(format!(
                    "monetize was offered {offered:?}"
                )));
            }
            self.calls
                .lock()
                .expect("the call log")
                .push(what.to_owned());
            Ok(())
        }
    }

    #[tonic::async_trait]
    impl ProductsService for FakeMonetize {
        async fn list(
            &self,
            request: tonic::Request<mpb::ListProductsRequest>,
        ) -> Result<tonic::Response<mpb::ListProductsResponse>, tonic::Status> {
            self.record(&request, "Products.List")?;
            Ok(tonic::Response::new(mpb::ListProductsResponse {
                products: vec![
                    mpb::ProductInfo {
                        id: "gunnar".to_owned(),
                        meters: vec![
                            // Enforced AND priced: a wall that costs money.
                            mpb::MeterInfo {
                                name: "pack_bytes".to_owned(),
                                meaning: "objects on disk that are not re-derivable".to_owned(),
                                enforcement: mpb::Enforcement::Enforced as i32,
                                scope: mpb::Scope::Tenant as i32,
                                unit: "disk_gib_month".to_owned(),
                            },
                            // Enforced and NOT priced: a budget, free to promise.
                            mpb::MeterInfo {
                                name: "cache_bytes".to_owned(),
                                meaning: "the exploded read cache".to_owned(),
                                enforcement: mpb::Enforcement::Enforced as i32,
                                scope: mpb::Scope::Tenant as i32,
                                unit: String::new(),
                            },
                            // Priced but only MEASURED: a limit nothing polices.
                            // Selling this is selling a promise nobody keeps.
                            mpb::MeterInfo {
                                name: "repos".to_owned(),
                                meaning: "repositories in the namespace".to_owned(),
                                enforcement: mpb::Enforcement::Measured as i32,
                                scope: mpb::Scope::Tenant as i32,
                                unit: "disk_gib_month".to_owned(),
                            },
                        ],
                        reachable: None,
                        // This fake monetize does not verify tickets. The
                        // operator path below never presents one, so the flag
                        // decides nothing here — it is `api::subscription`'s
                        // gate, and its own tests set it both ways.
                        tenant_renewal: false,
                        metered_door: None,
                    },
                    // Another product's meters must never reach gunnar's console.
                    mpb::ProductInfo {
                        id: "holger".to_owned(),
                        meters: vec![mpb::MeterInfo {
                            name: "rows".to_owned(),
                            meaning: "holger's own".to_owned(),
                            enforcement: mpb::Enforcement::Enforced as i32,
                            scope: mpb::Scope::Tenant as i32,
                            unit: "disk_gib_month".to_owned(),
                        }],
                        reachable: None,
                        // This fake monetize does not verify tickets. The
                        // operator path below never presents one, so the flag
                        // decides nothing here — it is `api::subscription`'s
                        // gate, and its own tests set it both ways.
                        tenant_renewal: false,
                        metered_door: None,
                    },
                ],
                ..Default::default()
            }))
        }
    }

    #[tonic::async_trait]
    impl PurchaseService for FakeMonetize {
        async fn quote(
            &self,
            request: tonic::Request<mpb::QuoteRequest>,
        ) -> Result<tonic::Response<mpb::QuoteResponse>, tonic::Status> {
            self.record(&request, "Purchase.Quote")?;
            let asked = request.into_inner();
            Ok(tonic::Response::new(mpb::QuoteResponse {
                words: String::new(),
                reference: format!("gunnar/{}/2026-09-07", asked.tenant),
                price: Some(mpb::Money {
                    minor: PRICE_MINOR,
                    currency: mpb::Currency::Eur as i32,
                }),
                cost: Some(mpb::Money {
                    minor: COST_MINOR,
                    currency: mpb::Currency::Eur as i32,
                }),
                current: [("pack_bytes".to_owned(), 10_737_418_240u64)].into(),
                caps: asked.caps.clone(),
                delta: [("pack_bytes".to_owned(), 10_737_418_240u64)].into(),
                resources: Vec::new(),
                can_serve: self.can_serve,
                reason: self.reason.clone(),
                period: "month".to_owned(),
                // The OPERATOR path renders no amount at all — see
                // `the_offer_carries_no_cost` and this module's rule — so a
                // non-empty value here would fail that test rather than pass
                // unnoticed.
                price_words: String::new(),
            }))
        }

        /// **The shop window.** Two rows on purpose: one that can be served
        /// and one that cannot, so a single request exercises both halves of
        /// the console's per-row fork.
        async fn bundles(
            &self,
            request: tonic::Request<mpb::ListBundlesRequest>,
        ) -> Result<tonic::Response<mpb::ListBundlesResponse>, tonic::Status> {
            self.record(&request, "Purchase.Bundles")?;
            self.markets.lock().expect("the market log").push(request.get_ref().market);
            if self.reading {
                return Ok(tonic::Response::new(mpb::ListBundlesResponse { bundles: Vec::new(), reading: true, ..Default::default() }));
            }
            if let Some(declared) = &self.declared {
                return Ok(tonic::Response::new(mpb::ListBundlesResponse {
                    bundles: declared.clone(),
                    reading: false, ..Default::default()
                }));
            }
            Ok(tonic::Response::new(mpb::ListBundlesResponse {
                reading: false,
                bundles: vec![
                    mpb::Bundle {
                        id: BUNDLE_ID.to_owned(),
                        label: BUNDLE_LABEL.to_owned(),
                        // WORDS. The console may not divide by 100 or map a
                        // currency, so this is the only shape an amount can
                        // reach a browser in, and it must arrive unchanged.
                        price: BUNDLE_PRICE_WORDS.to_owned(),
                        period: "month".to_owned(),
                        delta: [("pack_bytes".to_owned(), 10_737_418_240u64)].into(),
                        can_serve: true,
                        reason: String::new(), ..Default::default()
                    },
                    mpb::Bundle {
                        id: "100gb-1m".to_owned(),
                        label: "100.0 GiB more room".to_owned(),
                        price: "2200.00 SEK".to_owned(),
                        period: "month".to_owned(),
                        delta: [("pack_bytes".to_owned(), 107_374_182_400u64)].into(),
                        can_serve: false,
                        reason: BUNDLE_REFUSAL.to_owned(), ..Default::default()
                    },
                ], ..Default::default()
            }))
        }

        async fn start(
            &self,
            request: tonic::Request<mpb::StartPurchaseRequest>,
        ) -> Result<tonic::Response<mpb::StartPurchaseResponse>, tonic::Status> {
            self.record(&request, "Purchase.Start")?;
            let asked = request.into_inner();
            self.markets.lock().expect("the market log").push(asked.market);
            self.payers.lock().expect("the payer log").push(asked.payer.clone());
            self.ordered
                .lock()
                .expect("the order log")
                .push((asked.bundle.clone(), !asked.caps.is_empty()));
            Ok(tonic::Response::new(mpb::StartPurchaseResponse {
                reference: format!("gunnar/{}/2026-09-07", asked.tenant),
                state: mpb::PurchaseState::Pending as i32,
                handle_vendor: "stripe".to_owned(),
                handle_id: "cs_test_a1b2c3d4e5".to_owned(),
                redirect_url: self.checkout_url.to_owned(),
                reason: "waiting for the customer at the vendor".to_owned(),
                fact: None,
                // PENDING gives nothing yet, so it says nothing.
                words: String::new(),
                users: None,
            }))
        }

        async fn status(
            &self,
            request: tonic::Request<mpb::PurchaseStatusRequest>,
        ) -> Result<tonic::Response<mpb::PurchaseStatusResponse>, tonic::Status> {
            self.record(&request, "Purchase.Status")?;
            let asked = request.into_inner();
            self.calls.lock().expect("the call log").push(format!(
                "scoped {} {} {}",
                asked.product, asked.tenant, asked.reference
            ));
            // monetize 3b44e7d: with a tenant named, only that tenant's
            // attempt of that product is answered — one uniform NOT_FOUND.
            let owner = asked
                .reference
                .split_once('/')
                .and_then(|(_, rest)| rest.rsplit_once('/'))
                .map(|(owner, _)| owner.to_owned())
                .unwrap_or_default();
            if !asked.tenant.is_empty() && owner != asked.tenant {
                return Err(tonic::Status::not_found(
                    "this tenant has no purchase by that reference",
                ));
            }
            Err(tonic::Status::unimplemented("not this test's"))
        }

        async fn abandon(
            &self,
            request: tonic::Request<mpb::AbandonPurchaseRequest>,
        ) -> Result<tonic::Response<mpb::AbandonPurchaseResponse>, tonic::Status> {
            self.record(&request, "Purchase.Abandon")?;
            Err(tonic::Status::unimplemented("not this test's"))
        }

        async fn cancel(
            &self,
            request: tonic::Request<mpb::CancelPurchaseRequest>,
        ) -> Result<tonic::Response<mpb::CancelPurchaseResponse>, tonic::Status> {
            self.record(&request, "Purchase.Cancel")?;
            Err(tonic::Status::unimplemented("not this test's"))
        }
    }

    /// Stand a monetize up on loopback and point a console at it.
    async fn console(fake: FakeMonetize) -> (axum::Router, Arc<Mutex<Vec<String>>>) {
        console_receipts_to(fake, None).await
    }

    /// [`console`], with the operator's receipt address fed (g-receipts).
    async fn console_receipts_to(fake: FakeMonetize, receipts_to: Option<&str>) -> (axum::Router, Arc<Mutex<Vec<String>>>) {
        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 calls = fake.calls.clone();
        tokio::spawn(async move {
            tonic::transport::Server::builder()
                .add_service(ProductsServer::new(fake.clone()))
                .add_service(PurchaseServer::new(fake))
                .serve_with_incoming(tokio_stream::wrappers::TcpListenerStream::new(listener))
                .await
        });

        let signer = Arc::new(
            crate::signer::Signer::new(ssh_key::PrivateKey::from(
                ssh_key::private::Ed25519Keypair::from_seed(&[9u8; 32]),
            ))
            .expect("a fixed seed makes a signer"),
        );
        // A gunnar that is not there. Nothing on these routes talks to gunnar,
        // and a handle pointing at a port nobody is listening on is how that is
        // held: a route that quietly grew a gunnar call would hang here rather
        // than pass.
        let gunnar =
            crate::grpc::Gunnar::connect_lazy("127.0.0.1:1", signer).expect("a lazy handle");
        let store = crate::state::StateStore::open(
            tempfile::Builder::new()
                .suffix(".redb")
                .tempfile()
                .expect("a temp file")
                .into_temp_path()
                .keep()
                .expect("keep the path"),
        )
        .expect("a state store");

        let router = crate::serve::router(Arc::new(crate::app::App {
            webauthn: None,
            admin_token: None,
            monetize: Some(
                crate::monetize::Monetize::connect_lazy(&format!("http://{addr}"), BEARER, "gunnar")
                    .expect("a lazy channel")
                    .with_receipts_to(receipts_to),
            ),
            events: crate::sse::EventHub::detached(),
            gunnar: gunnar.clone(),
            twin: None,
            guest: None,
            reviewers: Arc::new(crate::reviewer::ReviewerKeys::new(
                "127.0.0.1:1".to_owned(),
                store.clone(),
            )),
            tenants: Arc::new(crate::tenant::TenantKeys::new(
                "127.0.0.1:1".to_owned(),
                store,
            )),
            forges: Default::default(),
            imports: Default::default(),
            forge_apps: Default::default(),
            ui_dir: None,
            caches: crate::api::Caches::default(),
            objects: Default::default(),
            readmes: Default::default(),
        }));
        (router, calls)
    }

    async fn body_of(router: axum::Router, uri: &str) -> (http::StatusCode, String) {
        let response = router
            .oneshot(
                http::Request::builder()
                    .uri(uri)
                    .body(axum::body::Body::empty())
                    .expect("a request"),
            )
            .await
            .expect("the router answers");
        let status = response.status();
        let bytes = axum::body::to_bytes(response.into_body(), 1 << 20)
            .await
            .expect("a body");
        (status, String::from_utf8(bytes.to_vec()).expect("utf-8"))
    }

    /// **The provider's cost must never reach a tenant's browser.**
    ///
    /// `price` is what the customer pays; `cost` is what Vetra pays its cloud
    /// for the iron the order buys. Sending both puts the margin on the wire,
    /// where anyone who opens the network tab can read it — and a JSON field
    /// nobody renders is still a disclosure, because the browser received it.
    ///
    /// Asserted on the BYTES rather than on the struct: a field added to
    /// [`Offer`] tomorrow, or a handler that serialises the whole proto message
    /// because that was easier, both fail here. The struct's shape is a claim;
    /// the body is the evidence.
    #[tokio::test]
    async fn the_offer_carries_no_cost() {
        let (router, calls) = console(FakeMonetize::serving()).await;

        let (status, body) = body_of(
            router,
            "/api/namespaces/alpha/increase/offer?meter=pack_bytes&cap=21474836480",
        )
        .await;

        assert_eq!(status, http::StatusCode::OK, "{body}");
        assert!(
            !body.contains(&COST_MINOR.to_string()),
            "the provider's cost reached the browser. monetize answered cost={COST_MINOR} \
             beside price={PRICE_MINOR}, and the console sent it on:\n  {body}"
        );
        assert!(
            !body.to_lowercase().contains("cost"),
            "the response names a cost:\n  {body}"
        );
        assert_eq!(
            *calls.lock().expect("the call log"),
            vec!["Purchase.Quote".to_owned()],
            "one quote and nothing else; a quote moves no money and must not start anything"
        );
    }

    /// The quote's own words survive: the refusal, and the period.
    #[tokio::test]
    async fn a_refusal_is_monetizes_sentence_and_not_the_consoles() {
        let refusal = "the set cannot grow while gunnar is running and 35 GiB are short";
        let (router, _) = console(FakeMonetize {
            can_serve: false,
            reason: refusal.to_owned(),
            ..Default::default()
        })
        .await;

        let (status, body) = body_of(
            router,
            "/api/namespaces/alpha/increase/offer?meter=pack_bytes&cap=21474836480",
        )
        .await;

        assert_eq!(status, http::StatusCode::OK, "{body}");
        assert!(
            body.contains(refusal),
            "the refusal was reworded:\n  {body}"
        );
        assert!(body.contains("\"can_serve\":false"), "{body}");
        assert!(body.contains("\"period\":\"month\""), "{body}");
    }

    /// **What is purchasable is monetize's answer, filtered by monetize's own
    /// two words** — and nothing that came from a list written here.
    #[tokio::test]
    async fn only_enforced_and_priced_meters_of_this_product_are_offered() {
        let (router, _) = console(FakeMonetize::serving()).await;

        let (status, body) = body_of(router, "/api/namespaces/alpha/increase").await;

        assert_eq!(status, http::StatusCode::OK, "{body}");
        assert!(body.contains("pack_bytes"), "{body}");
        assert!(
            !body.contains("cache_bytes"),
            "cache_bytes is enforced but not priced: it costs nothing to promise, so it is not \
             for sale:\n  {body}"
        );
        assert!(
            !body.contains("repos"),
            "repos is priced but only MEASURED — gunnar's own plugin says it is not yet a wall. \
             Selling it would sell a limit nothing enforces:\n  {body}"
        );
        assert!(
            !body.contains("rows"),
            "that is holger's meter, on gunnar's console:\n  {body}"
        );
    }

    /// **Every word in the window is monetize's, carried unchanged** — for the
    /// rows that are IN the window.
    ///
    /// The three strings this asserts on are the three a console could be
    /// tempted to compose: the label (it could have built one from the delta),
    /// the amount (it could have divided a minor unit by 100 and picked a
    /// symbol) and the period. All three arrive as bytes and leave as bytes.
    ///
    /// **Rewritten 2026-09-20, knowingly.** Until then this test held that the
    /// row monetize could NOT serve reached the browser too, reason and all —
    /// *"so the panel would draw a row with no button and no explanation"*. The
    /// owner ruled the opposite after reading exactly that row for five days:
    /// a card for a size the box cannot hold is not offered at all. The
    /// assertion is inverted here on purpose and
    /// [`a_bundle_monetize_cannot_serve_is_not_in_the_window`] carries it.
    #[tokio::test]
    async fn the_window_is_monetizes_words_and_the_console_composes_none_of_them() {
        let (router, calls) = console(FakeMonetize::serving()).await;

        let (status, body) = body_of(router, "/api/namespaces/alpha/increase/bundles").await;

        assert_eq!(status, http::StatusCode::OK, "{body}");
        assert!(body.contains(BUNDLE_ID), "the id was not carried:\n  {body}");
        assert!(body.contains(BUNDLE_LABEL), "the label was reworded:\n  {body}");
        assert!(
            body.contains(BUNDLE_PRICE_WORDS),
            "the amount monetize rendered did not reach the browser; a console that drops it has \
             to invent one:\n  {body}"
        );
        assert!(body.contains("\"period\":\"month\""), "{body}");
        assert!(
            body.contains("\"delta\":{\"pack_bytes\":10737418240}"),
            "the delta is passed through as numbers for the console's own formatter:\n  {body}"
        );
        assert_eq!(
            *calls.lock().expect("the call log"),
            vec!["Purchase.Bundles".to_owned()],
            "one call, and no quote behind it: `can_serve` on the row IS the quote"
        );
    }

    /// ★ **A bundle the server would refuse is not rendered.** Not its id, not
    /// its label, not its price and not the sentence monetize put under it —
    /// nothing of it reaches the browser. The one thing the browser is told is
    /// that a row was withheld, so an empty window can be told from an
    /// undeclared one. The servable row beside it survives untouched: a fix
    /// that hid the dead cards and the live one would be worse than the bug.
    #[tokio::test]
    async fn a_bundle_monetize_cannot_serve_is_not_in_the_window() {
        let (router, _) = console(FakeMonetize::serving()).await;

        let (status, body) = body_of(router, "/api/namespaces/alpha/increase/bundles").await;

        assert_eq!(status, http::StatusCode::OK, "{body}");
        for dead in ["100gb-1m", "100.0 GiB more room", "2200.00 SEK", BUNDLE_REFUSAL] {
            assert!(
                !body.contains(dead),
                "a bundle this box cannot serve reached the browser ({dead:?}):\n  {body}"
            );
        }
        assert!(!body.contains("\"can_serve\":false"), "a refused row was sent:\n  {body}");
        assert!(body.contains("\"withheld\":1"), "the browser was not told a row was left out:\n  {body}");
        assert!(body.contains(BUNDLE_ID), "the servable row must survive the filter:\n  {body}");
        assert!(!body.contains("unavailable"), "a window with an offer in it has no closing line:\n  {body}");
    }

    /// ★ **A window monetize is still reading is `pending`, with the line for
    /// the spinner and the one for after a minute — never an empty answer.**
    ///
    /// test2, 2026-10-05: the owner's first visit after a night's idle drew
    /// the typed order, because `{"offers":[],"withheld":0}` is what an unread
    /// window and an undeclared one both looked like. The words are this
    /// server's, so the console writes none.
    #[tokio::test]
    async fn a_window_monetize_is_still_reading_is_pending_with_the_servers_lines() {
        let (router, _) = console(FakeMonetize { reading: true, ..FakeMonetize::serving() }).await;

        let (status, body) = body_of(router, "/api/namespaces/alpha/increase/bundles").await;

        assert_eq!(status, http::StatusCode::OK, "{body}");
        let w: serde_json::Value = serde_json::from_str(&body).expect("json");
        assert_eq!(w["pending"], AWAITING_PROVIDER, "{body}");
        assert_eq!(w["late"], PROVIDER_LATE, "{body}");
        assert_eq!(w["offers"], serde_json::json!([]), "{body}");
        assert!(w.get("unavailable").is_none(), "an unread window is not \"nothing fits\": {body}");
    }

    /// And an answer is not pending: the two lines appear ONLY when monetize
    /// says the window is still being read.
    #[tokio::test]
    async fn an_answered_window_carries_no_pending_line() {
        let (router, _) = console(FakeMonetize::serving()).await;
        let (_, body) = body_of(router, "/api/namespaces/alpha/increase/bundles").await;
        assert!(!body.contains("pending") && !body.contains("late"), "{body}");
    }

    /// ★ **A window whose every row is refused says so in one line** — and the
    /// line is not about maintenance. Two cards over a false sentence was the
    /// bug; a blank panel that looks like a loading failure would be the next
    /// one.
    #[tokio::test]
    async fn a_window_with_nothing_servable_says_so_in_one_line() {
        let refused = mpb::Bundle {
            id: "100gb-1m".to_owned(),
            label: "100.0 GiB more room".to_owned(),
            price: "2200.00 SEK".to_owned(),
            period: "month".to_owned(),
            delta: [("pack_bytes".to_owned(), 107_374_182_400u64)].into(),
            can_serve: false,
            reason: "We cannot add more space to your account right now while we carry out \
                     maintenance. Nothing has been charged — please try again later."
                .to_owned(), ..Default::default()
        };
        let (router, _) = console(FakeMonetize {
            can_serve: true,
            declared: Some(vec![refused.clone(), mpb::Bundle { id: "50gb-1m".to_owned(), ..refused }]),
            ..Default::default()
        })
        .await;

        let (status, body) = body_of(router, "/api/namespaces/alpha/increase/bundles").await;

        assert_eq!(status, http::StatusCode::OK, "{body}");
        assert!(body.contains("\"offers\":[]"), "{body}");
        assert!(body.contains("\"withheld\":2"), "{body}");
        assert!(body.contains(NOTHING_FITS), "the one line is missing:\n  {body}");
        assert!(
            !body.to_ascii_lowercase().contains("maintenance"),
            "monetize's masked sentence leaked past the filter:\n  {body}"
        );
    }

    /// A deployment that declared no bundles is not a shop with no room: the
    /// window is empty, nothing was withheld, and there is no line — the panel
    /// draws the typed order, exactly as before.
    #[tokio::test]
    async fn a_window_nobody_declared_is_empty_and_says_nothing() {
        let (router, _) = console(FakeMonetize {
            can_serve: true,
            declared: Some(Vec::new()),
            ..Default::default()
        })
        .await;

        let (status, body) = body_of(router, "/api/namespaces/alpha/increase/bundles").await;

        assert_eq!(status, http::StatusCode::OK, "{body}");
        assert_eq!(body, "{\"offers\":[],\"withheld\":0}", "{body}");
    }

    /// ★ **A pressed button is the typed order, one field over.**
    ///
    /// ★ **g-receipts: an order placed by the OPERATOR names the operator's
    /// address as its payer, so Stripe mails the order's receipt.** MEASURED by
    /// the release-3 proof runner 2026-09-29: every order's checkout asked
    /// `receipt_email = None` — this console sent `payer: ""` on every order
    /// (here, `start_bundle`, `start`, and the subscription's renew/buy/raise),
    /// and monetize's own fallback (`email_of`) knows only a namespace a paid
    /// signup created, never the operator's. A signed-in tenant still sends
    /// nothing: monetize names the address they signed up with.
    #[tokio::test]
    async fn an_operators_order_names_the_operators_receipt_address_and_nothing_is_invented() {
        let fake = FakeMonetize::serving();
        let payers = fake.payers.clone();
        let (router, _) = console_receipts_to(fake, Some("operator@example.com")).await;
        let (status, body) = request(router, http::Method::POST, "/api/namespaces/alpha/increase/bundle", None, &format!("{{\"bundle\":\"{BUNDLE_ID}\"}}")).await;
        assert_eq!(status, http::StatusCode::OK, "{body}");
        assert_eq!(*payers.lock().expect("the payer log"), vec!["operator@example.com".to_owned()], "the open console is the operator: the receipt goes to the operator");

        // No address fed: nothing invented — monetize's fallback decides.
        let fake = FakeMonetize::serving();
        let payers = fake.payers.clone();
        let (router, _) = console_receipts_to(fake, None).await;
        let (status, body) = request(router, http::Method::POST, "/api/namespaces/alpha/increase/bundle", None, &format!("{{\"bundle\":\"{BUNDLE_ID}\"}}")).await;
        assert_eq!(status, http::StatusCode::OK, "{body}");
        assert_eq!(*payers.lock().expect("the payer log"), vec![String::new()]);

        // A fed value that is not an address is not sent to a payment vendor.
        let fake = FakeMonetize::serving();
        let payers = fake.payers.clone();
        let (router, _) = console_receipts_to(fake, Some("not an address")).await;
        let _ = request(router, http::Method::POST, "/api/namespaces/alpha/increase/bundle", None, &format!("{{\"bundle\":\"{BUNDLE_ID}\"}}")).await;
        assert_eq!(*payers.lock().expect("the payer log"), vec![String::new()]);
    }

    /// ★ **A vendor link the browser may not be sent to never reaches it**
    /// (stinky #19): the operator's order is checked as the tenant's is.
    #[tokio::test]
    async fn a_checkout_link_that_is_not_https_never_reaches_the_browser() {
        for bad in ["http://checkout.example/pay", "javascript:alert(1)", "//checkout.example/pay"] {
            let (router, _) = console(FakeMonetize { checkout_url: bad, ..FakeMonetize::serving() }).await;
            let (status, body) = request(
                router,
                http::Method::POST,
                "/api/namespaces/alpha/increase/bundle",
                None,
                &format!("{{\"bundle\":\"{BUNDLE_ID}\"}}"),
            )
            .await;
            assert_eq!(status, http::StatusCode::BAD_GATEWAY, "{bad}: {body}");
            assert!(!body.contains(bad), "{bad}: {body}");
        }
    }

    /// `bundle` where `caps` would be and NOTHING ELSE changed: the same
    /// `Purchase.Start`, the same reference and the same vendor link. The caps assertion is the load-bearing one — monetize
    /// refuses a request naming both rather than letting one win silently, so a
    /// console that sent an id beside a cap map would be refused in production
    /// while passing any test that only checked the id.
    #[tokio::test]
    async fn a_pressed_offer_orders_by_id_and_names_no_caps() {
        let fake = FakeMonetize::serving();
        let ordered = fake.ordered.clone();
        let (router, calls) = console(fake).await;

        let (status, body) = request(
            router.clone(),
            http::Method::POST,
            "/api/namespaces/alpha/increase/bundle",
            None,
            &format!("{{\"bundle\":\"{BUNDLE_ID}\"}}"),
        )
        .await;

        assert_eq!(status, http::StatusCode::OK, "{body}");
        assert_eq!(
            *ordered.lock().expect("the order log"),
            vec![(BUNDLE_ID.to_owned(), false)],
            "the order must name the bundle and no caps"
        );
        assert!(
            body.contains(CHECKOUT_URL),
            "the vendor's own link did not reach the browser:\n  {body}"
        );
        assert_eq!(
            *calls.lock().expect("the call log"),
            vec!["Purchase.Start".to_owned()],
            "no second door and no extra quote"
        );

        // And it lands in the SAME poll the typed order uses: the reference was
        // remembered, so following it is allowed.
        let (status, followed) = body_of(
            router,
            "/api/namespaces/alpha/increase/status?reference=gunnar/alpha/2026-09-07",
        )
        .await;
        assert_ne!(
            status,
            http::StatusCode::NOT_FOUND,
            "an order placed from the window is not one this console admits placing:\n  {followed}"
        );
    }

    /// ★ **g-dk-market / g-currencies (owner 2026-09-28): the customer's
    /// market reaches monetize** — the window and the order carry `market`
    /// (SE, FI, DK), so a Dane is priced and charged in DKK and a Finn in
    /// EUR; none named is monetize's default, and a market nobody sells in is
    /// refused here by name before monetize is dialled.
    #[tokio::test]
    async fn the_customers_market_reaches_monetize_and_an_unknown_one_is_refused() {
        let fake = FakeMonetize::serving();
        let markets = fake.markets.clone();
        let (router, _calls) = console(fake).await;
        let (status, body) = body_of(router.clone(), "/api/namespaces/alpha/increase/bundles?market=FI").await;
        assert_eq!(status, http::StatusCode::OK, "{body}");
        let (status, body) = request(router.clone(), http::Method::POST, "/api/namespaces/alpha/increase/bundle", None, &format!("{{\"bundle\":\"{BUNDLE_ID}\",\"market\":\"DK\"}}")).await;
        assert_eq!(status, http::StatusCode::OK, "{body}");
        let (status, _) = request(router.clone(), http::Method::POST, "/api/namespaces/alpha/increase/bundle", None, &format!("{{\"bundle\":\"{BUNDLE_ID}\"}}")).await;
        assert_eq!(status, http::StatusCode::OK);
        assert_eq!(*markets.lock().unwrap(), vec![mpb::Market::Fi as i32, mpb::Market::Dk as i32, mpb::Market::Unspecified as i32]);
        // A TYPED order names its market too (2026-10-05: a 12-month plan is
        // raised by a typed size, in the buyer's market).
        let (status, body) = request(router.clone(), http::Method::POST, "/api/namespaces/alpha/increase", None, r#"{"meter":"pack_bytes","cap":21474836480,"market":"FI"}"#).await;
        assert_eq!(status, http::StatusCode::OK, "{body}");
        assert_eq!(markets.lock().unwrap().last(), Some(&(mpb::Market::Fi as i32)));
        let (status, body) = request(router, http::Method::POST, "/api/namespaces/alpha/increase/bundle", None, &format!("{{\"bundle\":\"{BUNDLE_ID}\",\"market\":\"US\"}}")).await;
        assert_eq!(status, http::StatusCode::BAD_REQUEST, "{body}");
        assert!(body.contains("US"), "{body}");
    }

    /// ★ **One rule from a browser to a market** (wiring tracer B 2026-10-08): where the page names none,
    /// the server reads the browser's languages, q-ranked (the sign-up ladder's rule), and the window says
    /// the market it was priced in, so the order echoes it; a page's own word still wins; a browser naming
    /// no market is monetize's default and the window names none.
    #[tokio::test]
    async fn the_browsers_languages_name_the_market_where_the_page_names_none() {
        let fake = FakeMonetize::serving();
        let markets = fake.markets.clone();
        let (router, _calls) = console(fake).await;
        let ask = |method: http::Method, uri: &str, languages: &str, body: String| {
            let mut b = http::Request::builder().method(method).uri(uri).header(http::header::CONTENT_TYPE, "application/json").header(http::header::ORIGIN, "http://127.0.0.1").header(http::header::ACCEPT_LANGUAGE, languages);
            if body.is_empty() {
                b = b.header(http::header::CONTENT_LENGTH, "0");
            }
            b.body(axum::body::Body::from(body)).expect("a request")
        };
        let read = |r: axum::response::Response| async move {
            let status = r.status();
            let bytes = axum::body::to_bytes(r.into_body(), 1 << 20).await.expect("a body");
            (status, String::from_utf8_lossy(&bytes).to_string())
        };
        // A Dane on an English browser: `da` is ranked below English and still names the market.
        let (status, body) = read(router.clone().oneshot(ask(http::Method::GET, "/api/namespaces/alpha/increase/bundles", "en-US,en;q=0.9,da;q=0.8", String::new())).await.expect("answers")).await;
        assert_eq!(status, http::StatusCode::OK, "{body}");
        assert!(body.contains(r#""market":"DK""#), "the window names the market it was priced in: {body}");
        // The order from that window, from a Swedish-speaking Finn's browser, the page naming none: FI.
        let (status, body) = read(router.clone().oneshot(ask(http::Method::POST, "/api/namespaces/alpha/increase/bundle", "sv-FI", format!("{{\"bundle\":\"{BUNDLE_ID}\"}}"))).await.expect("answers")).await;
        assert_eq!(status, http::StatusCode::OK, "{body}");
        // The page's own word wins over the browser's languages.
        let (status, _) = read(router.clone().oneshot(ask(http::Method::POST, "/api/namespaces/alpha/increase/bundle", "da-DK", format!("{{\"bundle\":\"{BUNDLE_ID}\",\"market\":\"SE\"}}"))).await.expect("answers")).await;
        assert_eq!(status, http::StatusCode::OK);
        // A browser that names no market: monetize's default, and the window names none.
        let (status, body) = read(router.clone().oneshot(ask(http::Method::GET, "/api/namespaces/alpha/increase/bundles", "en-GB,de;q=0.5", String::new())).await.expect("answers")).await;
        assert_eq!(status, http::StatusCode::OK, "{body}");
        assert!(!body.contains(r#""market""#), "{body}");
        assert_eq!(*markets.lock().unwrap(), vec![mpb::Market::Dk as i32, mpb::Market::Fi as i32, mpb::Market::Se as i32, mpb::Market::Unspecified as i32]);
    }

    /// A console with a passkey subsystem, so `require_auth` runs the real
    /// ladder instead of the open-on-loopback bypass, and sessions can be
    /// minted for it.
    async fn gated_console(
        fake: FakeMonetize,
    ) -> (
        axum::Router,
        Arc<crate::auth::Webauthn>,
        Arc<Mutex<Vec<String>>>,
    ) {
        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 calls = fake.calls.clone();
        tokio::spawn(async move {
            tonic::transport::Server::builder()
                .add_service(ProductsServer::new(fake.clone()))
                .add_service(PurchaseServer::new(fake))
                .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 signer = Arc::new(
            crate::signer::Signer::new(ssh_key::PrivateKey::from(
                ssh_key::private::Ed25519Keypair::from_seed(&[9u8; 32]),
            ))
            .expect("a fixed seed makes a signer"),
        );
        let router = crate::serve::router(Arc::new(crate::app::App {
            webauthn: Some(webauthn.clone()),
            admin_token: None,
            monetize: Some(
                crate::monetize::Monetize::connect_lazy(&format!("http://{addr}"), BEARER, "gunnar")
                    .expect("a lazy channel"),
            ),
            events: crate::sse::EventHub::detached(),
            gunnar: crate::grpc::Gunnar::connect_lazy("127.0.0.1:1", signer)
                .expect("a lazy handle"),
            twin: None,
            guest: None,
            reviewers: Arc::new(crate::reviewer::ReviewerKeys::new(
                "127.0.0.1:1".to_owned(),
                store.clone(),
            )),
            tenants: Arc::new(crate::tenant::TenantKeys::new(
                "127.0.0.1:1".to_owned(),
                store,
            )),
            forges: Default::default(),
            imports: Default::default(),
            forge_apps: Default::default(),
            ui_dir: None,
            caches: crate::api::Caches::default(),
            objects: Default::default(),
            readmes: Default::default(),
        }));
        (router, webauthn, calls)
    }

    /// One request, with an optional session cookie.
    async fn request(
        router: axum::Router,
        method: http::Method,
        uri: &str,
        cookie: Option<&str>,
        body: &str,
    ) -> (http::StatusCode, String) {
        let mut builder = http::Request::builder()
            .method(method)
            .uri(uri)
            .header(http::header::CONTENT_TYPE, "application/json")
            // The CSRF guard reads the origin on a mutation; the console's own
            // origin is what a real browser sends.
            .header(http::header::ORIGIN, "https://console.example");
        if let Some(cookie) = cookie {
            builder = builder.header(
                http::header::COOKIE,
                format!("{}={cookie}", crate::auth::SESSION_COOKIE),
            );
        }
        let response = router
            .oneshot(
                builder
                    .body(axum::body::Body::from(body.to_owned()))
                    .expect("a request"),
            )
            .await
            .expect("the router answers");
        let status = response.status();
        let bytes = axum::body::to_bytes(response.into_body(), 1 << 20)
            .await
            .expect("a body");
        (status, String::from_utf8_lossy(&bytes).into_owned())
    }

    /// **An order is an admin's, and the floor is the one every other route
    /// uses.**
    ///
    /// monetize's own console floors every money mutation at admin
    /// (`monetize-ui-server/src/auth.rs`: "every mutation under `/api/` is an
    /// admin's — an order, a refund … and all of them are money"), and this
    /// console must not be the softer door to the same transaction.
    ///
    /// The floor is `role_floor`'s and nothing bespoke: `/api/namespaces` is
    /// already an ADMIN surface there, so putting the route under it is what
    /// buys the rule. **ZERO calls** is the second half and the one that
    /// matters: a refusal that had already reached monetize would have named a
    /// tenant, and an attempt refused after the fact is still an attempt.
    #[tokio::test]
    async fn a_viewer_cannot_place_an_order_and_monetize_never_hears_of_it() {
        let (router, webauthn, calls) = gated_console(FakeMonetize::serving()).await;
        let viewer = webauthn.make_session("viewer-vera", crate::role::Role::Viewer, true);

        let (status, body) = request(
            router,
            http::Method::POST,
            "/api/namespaces/alpha/increase",
            Some(&viewer),
            r#"{"meter":"pack_bytes","cap":21474836480}"#,
        )
        .await;

        assert_eq!(
            status,
            http::StatusCode::FORBIDDEN,
            "a viewer placed an order: {body}"
        );
        assert!(
            body.contains("admin"),
            "the refusal must name the role it needs: {body}"
        );
        assert!(
            calls.lock().expect("the call log").is_empty(),
            "monetize was told about an order this console had already refused: {:?}",
            calls.lock().expect("the call log")
        );
    }

    /// **An OPERATOR is not enough either.** monetize floors every money
    /// mutation at admin on its own console; the softer door must not be here.
    #[tokio::test]
    async fn an_operator_cannot_place_an_order_either() {
        let (router, webauthn, calls) = gated_console(FakeMonetize::serving()).await;
        let operator = webauthn.make_session("operator-olof", crate::role::Role::Operator, true);

        let (status, body) = request(
            router,
            http::Method::POST,
            "/api/namespaces/alpha/increase",
            Some(&operator),
            r#"{"meter":"pack_bytes","cap":21474836480}"#,
        )
        .await;

        assert_eq!(
            status,
            http::StatusCode::FORBIDDEN,
            "an operator placed an order: {body}"
        );
        assert!(calls.lock().expect("the call log").is_empty());
    }

    /// The same door from the other side: an admin gets through, so the test
    /// above is a floor and not a wall.
    #[tokio::test]
    async fn an_admin_places_the_order_and_gets_the_vendors_own_link() {
        let (router, webauthn, calls) = gated_console(FakeMonetize::serving()).await;
        let admin = webauthn.make_session("admin-alva", crate::role::Role::Admin, true);

        let (status, body) = request(
            router,
            http::Method::POST,
            "/api/namespaces/alpha/increase",
            Some(&admin),
            r#"{"meter":"pack_bytes","cap":21474836480}"#,
        )
        .await;

        assert_eq!(status, http::StatusCode::OK, "{body}");
        assert!(
            body.contains(CHECKOUT_URL),
            "the console must carry the vendor's own link, unaltered: {body}"
        );
        assert!(body.contains("\"state\":\"pending\""), "{body}");
        assert!(
            !body.contains(&COST_MINOR.to_string()),
            "the provider's cost reached the browser through Start: {body}"
        );
        assert!(
            !body.contains("signature") && !body.contains("fact"),
            "the signed entitlement fact is monetize's and gunnar's, not the browser's: {body}"
        );
        assert_eq!(
            *calls.lock().expect("the call log"),
            vec!["Purchase.Start".to_owned()]
        );
    }

    /// **A visitor with no session reaches nothing, and monetize is not dialled
    /// on their behalf.**
    ///
    /// Both halves matter. The status is the middleware's; the empty call log is
    /// the promise that no credential of this console's was spent answering a
    /// stranger — the same property `crate::anonymous` holds for the gunnar
    /// side, now for the one that costs money.
    #[tokio::test]
    async fn an_anonymous_visitor_reaches_no_money_route() {
        for (method, uri) in [
            (http::Method::GET, "/api/namespaces/alpha/increase"),
            (
                http::Method::GET,
                "/api/namespaces/alpha/increase/offer?meter=pack_bytes&cap=1",
            ),
            (
                http::Method::GET,
                "/api/namespaces/alpha/increase/status?reference=gunnar/alpha/2026-09-07",
            ),
            (http::Method::POST, "/api/namespaces/alpha/increase"),
        ] {
            let (router, _, calls) = gated_console(FakeMonetize::serving()).await;
            let (status, body) = request(router, method.clone(), uri, None, "{}").await;
            assert_eq!(
                status,
                http::StatusCode::UNAUTHORIZED,
                "{method} {uri} answered a stranger: {body}"
            );
            assert!(
                calls.lock().expect("the call log").is_empty(),
                "{method} {uri} reached monetize for a caller with no session"
            );
        }
    }

    /// ★ **monetize scopes the follow, and nothing is remembered here** (thin
    /// layer, owner 2026-10-04). An order placed before a restart — or by
    /// monetize's own console — is asked about with the tenant in the path
    /// named, so monetize answers only that tenant's attempt. Red before: an
    /// in-memory ring of the orders this console placed was the gate, so this
    /// 404'd without asking and `Purchase.Status` went out unscoped.
    #[tokio::test]
    async fn an_operators_follow_is_scoped_by_monetize_to_the_namespace() {
        let (router, webauthn, calls) = gated_console(FakeMonetize::serving()).await;
        let admin = webauthn.make_session("admin-alva", crate::role::Role::Admin, true);

        let (status, body) = request(
            router,
            http::Method::GET,
            "/api/namespaces/alpha/increase/status?reference=gunnar%2Falpha%2F2019-01-01",
            Some(&admin),
            "",
        )
        .await;

        // The fake answers an in-scope Status UNIMPLEMENTED: the assertion is
        // that the console asked, scoped, and monetize decided.
        assert_eq!(status, http::StatusCode::NOT_IMPLEMENTED, "{body}");
        assert!(
            calls
                .lock()
                .expect("the call log")
                .contains(&"scoped gunnar alpha gunnar/alpha/2019-01-01".to_owned()),
            "monetize was not asked with the namespace named: {:?}",
            calls.lock().expect("the call log")
        );
    }

    /// And the order it DID place is followed, which is the half that keeps the
    /// check from being a route that always refuses.
    #[tokio::test]
    async fn the_order_this_console_placed_is_followed() {
        let (router, webauthn, calls) = gated_console(FakeMonetize::serving()).await;
        let admin = webauthn.make_session("admin-alva", crate::role::Role::Admin, true);

        let (status, placed) = request(
            router.clone(),
            http::Method::POST,
            "/api/namespaces/alpha/increase",
            Some(&admin),
            r#"{"meter":"pack_bytes","cap":21474836480}"#,
        )
        .await;
        assert_eq!(status, http::StatusCode::OK, "{placed}");
        let reference = serde_json::from_str::<serde_json::Value>(&placed).expect("JSON")
            ["reference"]
            .as_str()
            .expect("a reference")
            .to_owned();

        let (status, body) = request(
            router,
            http::Method::GET,
            &format!(
                "/api/namespaces/alpha/increase/status?reference={}",
                urlencoding(&reference)
            ),
            Some(&admin),
            "",
        )
        .await;

        // The fake does not implement `Status` — it records the call and
        // answers Unimplemented — so the assertion is that the console got past
        // its OWN gate and asked, which is the thing this commit changed. What
        // monetize then says is monetize's.
        assert_eq!(status, http::StatusCode::NOT_IMPLEMENTED, "{body}");
        assert!(
            calls
                .lock()
                .expect("the call log")
                .contains(&"Purchase.Status".to_owned()),
            "the order this console placed itself was not followed"
        );
    }

    /// ★ **A namespace's offer, bundle window and order status are an
    /// administrator's to READ too** (thin layer, owner 2026-10-04). Red before
    /// this row: the three GETs sat at the viewer floor with the tenant taken
    /// from the path, so any customer or employee asked monetize — on this
    /// console's one bearer — for ANOTHER tenant's caps, the operator's reason
    /// and the window priced for them: "a read-only enumeration of the whole
    /// customer list", in monetize's own words. A customer's own door is
    /// `/api/account/subscription`, which monetize scopes by the appliance's
    /// ticket. monetize must hear nothing for a viewer or an operator.
    #[tokio::test]
    async fn a_viewer_or_operator_reads_no_namespaces_offer_window_or_status() {
        let (router, webauthn, calls) = gated_console(FakeMonetize::serving()).await;
        let viewer = webauthn.make_session("viewer-vera", crate::role::Role::Viewer, true);
        let operator = webauthn.make_session("operator-olof", crate::role::Role::Operator, true);
        let admin = webauthn.make_session("admin-alva", crate::role::Role::Admin, true);
        let reads = [
            "/api/namespaces/alpha/increase/offer?meter=pack_bytes&cap=21474836480",
            "/api/namespaces/alpha/increase/bundles",
            "/api/namespaces/alpha/increase/status?reference=gunnar%2Falpha%2F2026-10-04",
        ];
        for (who, cookie) in [("viewer", &viewer), ("operator", &operator)] {
            for uri in reads {
                let (status, body) =
                    request(router.clone(), http::Method::GET, uri, Some(cookie), "").await;
                assert_eq!(status, http::StatusCode::FORBIDDEN, "{who} {uri}: {body}");
            }
        }
        assert!(
            calls.lock().expect("the call log").is_empty(),
            "monetize was asked on behalf of a viewer or an operator: {:?}",
            calls.lock().expect("the call log")
        );
        let (status, body) = request(router, http::Method::GET, reads[1], Some(&admin), "").await;
        assert_eq!(status, http::StatusCode::OK, "the administrator: {body}");
    }

    /// Percent-encode the slashes in a reference, which is what a browser does.
    fn urlencoding(reference: &str) -> String {
        reference.replace('/', "%2F")
    }

    /// **A reference belongs to the namespace in its own name, and monetize
    /// will not answer one that does not.**
    ///
    /// A reference is `<product>/<tenant>/<date>` — a guessable string. Since
    /// monetize 3b44e7d `Purchase.Status` takes the tenant, and the console
    /// names the one in the path: another namespace's attempt is monetize's
    /// uniform NOT_FOUND (thin layer, owner 2026-10-04).
    #[tokio::test]
    async fn a_reference_from_another_namespace_is_not_followed() {
        let (router, webauthn, calls) = gated_console(FakeMonetize::serving()).await;
        let admin = webauthn.make_session("admin-alva", crate::role::Role::Admin, true);

        let (status, body) = request(
            router,
            http::Method::GET,
            "/api/namespaces/alpha/increase/status?reference=gunnar%2Fbeta%2F2026-09-07",
            Some(&admin),
            "",
        )
        .await;

        // monetize's one uniform refusal: it names neither owner nor
        // reference, and the call carried the namespace that scopes it.
        assert_eq!(status, http::StatusCode::NOT_FOUND, "{body}");
        assert!(!body.contains("beta"), "{body}");
        assert!(
            calls
                .lock()
                .expect("the call log")
                .contains(&"scoped gunnar alpha gunnar/beta/2026-09-07".to_owned()),
            "{:?}",
            calls.lock().expect("the call log")
        );
    }
}