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

nordisk/holger-ui

crates/holger-ui-server/src/lib.rs

//! **The holger console's thin server** (thin layer, 2026-10-17).
//!
//! ```text
//! browser ──▶ this router ──┬─ /grpc/*        ──▶ holger's gRPC door (gRPC-web)
//!                           ├─ /-/*, /, marks ──▶ holger's HTTP door (passkeys, front page, sign-up links)
//!                           └─ everything else ─▶ the built bundle (SPA fallback)
//! ```
//!
//! It serves the bundle and passes calls through. It holds no users, no
//! permissions, no store and no business logic: the person's session is
//! holger's (minted by holger's passkey door, carried by the browser as
//! `authorization: Bearer`), and holger decides every call.
//!
//! One header is added, and only on the passkey door: holger partitions open
//! ceremonies per client, and every browser reaches holger from this process's
//! address. So the browser's address goes in `x-holger-ceremony-client`, proven
//! by this console's own bearer in `x-holger-proxy-authorization` (holger
//! trusts the hint only from an identity it resolves to admin). The browser can
//! never set either: both are stripped from what it sends.

use std::net::SocketAddr;
use std::path::{Path, PathBuf};
use std::sync::Arc;

use axum::body::Body;
use axum::extract::{ConnectInfo, Request, State};
use axum::response::{IntoResponse, Response};
use axum::routing::{any, get};
use axum::Router;
use http::{header, HeaderName, HeaderValue, StatusCode, Uri, Version};
use hyper_util::client::legacy::connect::HttpConnector;
use hyper_util::client::legacy::Client;
use hyper_util::rt::TokioExecutor;
use tower_http::compression::CompressionLayer;
use tower_http::services::{ServeDir, ServeFile};

/// The crate version, as the manifest spells it.
pub const VERSION: &str = env!("CARGO_PKG_VERSION");

/// The person's session as an HttpOnly cookie (A34-38).
pub mod session;
pub use session::with_session;

/// Where the browser's gRPC-web calls arrive (stripped before forwarding).
pub const GRPC_PREFIX: &str = "/grpc";
/// The front page's assets, served by holger's HTTP door beside `/` and `/-/*`.
/// **Where a private Holger's pay link lands** (lane P1): the shop feeds and mails
/// `https://<site>/private?renew=<company>`; it goes on to the sign-up page's manage view, as
/// Gunnar's `/private` does (A16).
#[cfg(feature = "metered")]
pub const RENEW_PATH: &str = "/private";

/// Pure (Gunnar's `private_signup_address`): **the sign-up page's private choice, with only the
/// fields it reads** — `company` (an order to watch) and `renew` (lowercased), each the FIRST of
/// its name and each only when it is a company's name. Nothing else rides along.
#[cfg(feature = "metered")]
pub fn private_signup_address(query: Option<&str>) -> String {
    let (mut company, mut renew) = (None, None);
    for kv in query.unwrap_or_default().split('&').filter(|kv| !kv.is_empty()) {
        let (key, value) = kv.split_once('=').unwrap_or((kv, ""));
        match key {
            "company" if company.is_none() => company = Some(value.to_owned()),
            "renew" if renew.is_none() => renew = Some(value.to_ascii_lowercase()),
            _ => {}
        }
    }
    let is_company = |c: &str| !c.is_empty() && c.len() <= 16 && c.starts_with(|ch: char| ch.is_ascii_lowercase()) && c.chars().all(|ch| ch.is_ascii_lowercase() || ch.is_ascii_digit());
    let mut to = format!("{SIGNUP_PATH}?plan=private");
    for (key, value) in [("company", company), ("renew", renew)] {
        if let Some(value) = value.filter(|v| is_company(v)) {
            to.push_str(&format!("&{key}={value}"));
        }
    }
    to
}
/// **The sign-up page** (lane SU1, A12): Gunnar's page, screen for screen; the bundle's
/// `signup.html` answers it, asks holger's door `GET /-/signup` who is here and the site's buy
/// doors (the shared monetize-site-buy relay, merged in front by holger's front) the rest.
pub const SIGNUP_PATH: &str = "/signup";
/// **Where a paid sign-up comes back** (A12): Gunnar's welcome page, `welcome.html`.
pub const WELCOME_PATH: &str = "/welcome";
/// **The buy page** (A16, Gunnar's `PAY_PAGE` → `storage_goes_to`): where the pay link in
/// every refusal (`/billing/{tenant}`) and `/account/storage` lead — the sign-up page's account
/// choice, which signed in is "Buy a plan" or "Grow your account".
#[cfg(feature = "metered")]
pub const BUY_PAGE: &str = "/signup?plan=account";
/// A vendor's way back on this site (A12): the bundle's `paid.html` follows the order there.
#[cfg(feature = "metered")]
pub const VENDOR_RETURNS: [&str; 2] = ["/paid/", "/cancel/"];
/// Who answers a buy route ([`BUY_ROUTES`]). Never the SPA fallback.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BuyOwner {
    /// One of the bundle's own pages (a file trunk copies into the bundle).
    Page(&'static str),
    /// A buy script the front hands in ([`BuyScripts`]); a 404 where none is handed in.
    #[cfg(feature = "metered")]
    Script(BuyScript),
    /// The pay link: on to the sign-up page's manage view ([`private_signup_address`]).
    #[cfg(feature = "metered")]
    Renew,
    /// A redirect to [`BUY_PAGE`].
    #[cfg(feature = "metered")]
    ToBuyPage,
    /// The shared relay's door, merged in front by holger's front; a 404 with
    /// [`NOTHING_SOLD_HERE`] where it is not merged.
    #[cfg(feature = "metered")]
    Relay,
}

/// Which of [`BuyScripts`] a [`BuyOwner::Script`] row serves.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BuyScript {
    Signup,
    Welcome,
    /// The order page's (`/paid/{reference}`, `/cancel/{reference}`; T16).
    Paid,
}

/// One buy route: its axum path and its owner.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BuyRoute {
    pub path: &'static str,
    pub owner: BuyOwner,
}

/// **Every buy route the console serves, and who answers it** (CI lane, 2026-10-23; A11 fix 3
/// failed live because `/paid` fell through to the SPA). The bundle's router is BUILT from this
/// table, and `every_buy_route_answers_from_its_owner_never_the_spa` asks each row, so a new
/// buy route is a row here and is covered by that test the moment it exists.
#[cfg(feature = "metered")]
pub const BUY_ROUTES: &[BuyRoute] = &[
    BuyRoute { path: "/login", owner: BuyOwner::Page("login.html") },
    BuyRoute { path: SIGNUP_PATH, owner: BuyOwner::Page("signup.html") },
    BuyRoute { path: WELCOME_PATH, owner: BuyOwner::Page("welcome.html") },
    BuyRoute { path: "/paid/{*reference}", owner: BuyOwner::Page("paid.html") },
    BuyRoute { path: "/cancel/{*reference}", owner: BuyOwner::Page("paid.html") },
    BuyRoute { path: RENEW_PATH, owner: BuyOwner::Renew },
    BuyRoute { path: "/billing/{*tenant}", owner: BuyOwner::ToBuyPage },
    BuyRoute { path: "/account/storage", owner: BuyOwner::ToBuyPage },
    BuyRoute { path: "/auth/{*rest}", owner: BuyOwner::Relay },
    BuyRoute { path: "/api/account/{*rest}", owner: BuyOwner::Relay },
    BuyRoute { path: "/signup.js", owner: BuyOwner::Script(BuyScript::Signup) },
    BuyRoute { path: "/welcome.js", owner: BuyOwner::Script(BuyScript::Welcome) },
    BuyRoute { path: "/paid.js", owner: BuyOwner::Script(BuyScript::Paid) },
];

/// **The open console's one page** (A28 M13): the passkey login. An open build has no buy route.
#[cfg(not(feature = "metered"))]
pub const BUY_ROUTES: &[BuyRoute] = &[BuyRoute { path: "/login", owner: BuyOwner::Page("login.html") }];

/// **What an open console refuses by name** (A28 M13): the paid half's pages, their files in
/// the bundle and its doors. Each answers 404, never the console's `index.html`, so an open
/// build behind a public name is never a sign-up page it cannot answer.
#[cfg(not(feature = "metered"))]
pub const OPEN_REFUSES: &[&str] = &[
    "/signup", "/signup.html", "/signup.js", "/welcome", "/welcome.html", "/welcome.js", "/paid.html", "/paid.js",
    "/paid/{*reference}", "/cancel/{*reference}", "/private", "/billing/{*tenant}", "/account/storage",
    "/auth/{*rest}", "/api/account/{*rest}",
];

pub const FRONT_ASSETS: [&str; 3] = ["/holger.webp", "/vetra.svg", "/ignalina.png"];
/// The ceremony hint holger reads, and the proof behind it.
pub const CEREMONY_CLIENT: &str = "x-holger-ceremony-client";
pub const PROXY_AUTHORIZATION: &str = "x-holger-proxy-authorization";

/// Where holger is.
#[derive(Clone, Debug)]
pub struct Upstreams {
    /// holger's gRPC door, `http://host:port` (gRPC-web is accepted there).
    pub grpc: String,
    /// holger's HTTP door, `http://host:port` (`ron_http_url`).
    pub door: String,
    /// This console's own bearer — sent ONLY as the proof behind a ceremony's
    /// client hint, never as anyone's credential.
    pub proof: Option<String>,
}

impl Upstreams {
    /// Both doors, as `host:port` or full `http://` URLs.
    pub fn new(grpc: &str, door: &str, proof: Option<String>) -> Upstreams {
        Upstreams { grpc: base(grpc), door: base(door), proof: proof.map(|p| p.trim().to_owned()).filter(|p| !p.is_empty()) }
    }
}

fn base(address: &str) -> String {
    let a = address.trim().trim_end_matches('/');
    if a.starts_with("http://") || a.starts_with("https://") {
        a.to_owned()
    } else {
        format!("http://{a}")
    }
}

#[derive(Clone)]
struct Shared {
    up: Upstreams,
    client: Client<HttpConnector, Body>,
}

/// Which upstream a path goes to, if any.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Route {
    /// holger's gRPC door; the path loses its `/grpc` prefix.
    Grpc,
    /// holger's HTTP door: the passkey door, the front page and its assets.
    Door,
    /// The bundle.
    Bundle,
}

/// Pure: where `path` goes.
pub fn route_of(path: &str) -> Route {
    if path == GRPC_PREFIX || path.starts_with("/grpc/") {
        Route::Grpc
    } else if path == "/" || path.starts_with("/-/") || FRONT_ASSETS.contains(&path) {
        Route::Door
    } else {
        Route::Bundle
    }
}

/// Pure: the upstream path and query for a gRPC-web call.
pub fn grpc_path(uri: &Uri) -> String {
    let pq = uri.path_and_query().map(|p| p.as_str()).unwrap_or("/");
    let rest = pq.strip_prefix(GRPC_PREFIX).unwrap_or(pq);
    if rest.starts_with('/') {
        rest.to_owned()
    } else {
        format!("/{rest}")
    }
}

/// **The buy pages' scripts** (A16, owner 2026-10-23): ONE copy for Gunnar and Holger, in
/// monetize-site-buy `assets`, filled with Holger's settings by the front that links it (this
/// crate builds from a lone clone and links no sibling). Served at `/signup.js`,
/// `/welcome.js` and `/paid.js` (the order page's, T16), under the bundle's CSP.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct BuyScripts {
    pub signup: String,
    pub welcome: String,
    pub paid: String,
}

/// **The console's router**: the bundle in `ui_dir` (none: the pass-through
/// alone), holger at `up`.
pub fn router(ui_dir: Option<PathBuf>, up: Upstreams) -> Router {
    router_with(ui_dir, up, None)
}

/// **What a console that sells nothing answers at its sign-up page** (LOW-PRIV): a private
/// Holger has no sign-up; a visitor is told so and who lets them in.
pub const PRIVATE_SIGNUP: &str = "This Holger is private. Ask its admin for an invite.";

/// [`router_with`] for a front that says whether this site `sells` (the shared relay merged in
/// front). A console that sells nothing answers [`SIGNUP_PATH`] with [`PRIVATE_SIGNUP`].
pub fn router_selling(ui_dir: Option<PathBuf>, up: Upstreams, scripts: Option<BuyScripts>, sells: bool) -> Router {
    let mut connector = HttpConnector::new();
    connector.set_connect_timeout(Some(std::time::Duration::from_secs(5)));
    let shared = Arc::new(Shared { up, client: Client::builder(TokioExecutor::new()).build(connector) });
    let pass = Router::new()
        .route("/", any(pass_through))
        .route("/-/{*rest}", any(pass_through))
        .route("/grpc/{*rest}", any(pass_through))
        .route(session::TOKEN_DOOR, axum::routing::post(session::token_door))
        .route("/health", get(|| async { "ok" }));
    let pass = FRONT_ASSETS.iter().fold(pass, |r, p| r.route(p, any(pass_through)));
    #[cfg(feature = "public-site")]
    let pass = {
        switch::follow(shared.clone());
        pass.route(holger_public_site::setting::ROUTE, get(switch::read).put(switch::flip))
    };
    let pass = pass.with_state(shared);
    with_session(mount_public_site(match ui_dir {
        Some(dir) => pass.merge(bundle(&dir, scripts, sells)),
        None => pass,
    }))
}

/// [`router`], serving the buy pages' `scripts` beside the bundle, on a site that sells.
pub fn router_with(ui_dir: Option<PathBuf>, up: Upstreams, scripts: Option<BuyScripts>) -> Router {
    router_selling(ui_dir, up, scripts, true)
}

/// The sign-up page of a console that sells nothing ([`PRIVATE_SIGNUP`], a 404: there is none).
async fn private_signup() -> Response {
    (StatusCode::NOT_FOUND, [(header::CACHE_CONTROL, "no-store"), (header::CONTENT_TYPE, "text/plain; charset=utf-8")], PRIVATE_SIGNUP).into_response()
}

/// The public site's layer around the whole router (`public-site`).
#[cfg(feature = "public-site")]
fn mount_public_site(app: Router) -> Router {
    app.layer(axum::middleware::from_fn_with_state(public_site(), holger_public_site::layer))
}

/// The default build: the router as it is.
#[cfg(not(feature = "public-site"))]
fn mount_public_site(app: Router) -> Router {
    app
}

/// True when this binary carries holger.rs's public face (`public-site`).
pub const PUBLIC_SITE: bool = cfg!(feature = "public-site");

/// **The administrator's switch for the public site** (`public-site` only; owner decision
/// 2026-10-26, item 5): `GET /api/public-site` says the position, `PUT /api/public-site
/// {"on": bool}` flips it. The position is holger's generic setting `console.public-site`
/// (`holger_public_site::setting`): the flip is written there with the asker's OWN bearer, so
/// holger decides that only an administrator may and audits who did it (old -> new); the
/// console applies it only once holger has kept it, and audits the flip on its own log too.
///
/// **The stored position is the authority, and the switch fails closed** (RA2-1): the site is
/// OFF until holger has answered what it stores, whatever `HOLGER_UI_PUBLIC_SITE` or the front's
/// feed says; those are only the INITIAL position, taken where nothing is stored. A damaged
/// store (holger answers 503 `damaged`) is OFF, said as an ALERT. The console reads the stored
/// position again every [`switch::RECHECK`] for its whole life, so a flip made elsewhere is
/// seen. It asks with its own bearer sent ONLY as the proof (`x-holger-proxy-authorization`),
/// never as a credential (RA2-2): holger lets that proof read and never write. A default build
/// has none of this: no route, no switch.
#[cfg(feature = "public-site")]
mod switch {
    use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
    use std::sync::{Arc, Mutex};
    use std::time::Duration;

    use axum::body::Body;
    use axum::extract::{Request, State};
    use axum::response::{IntoResponse, Response};
    use http::{header, HeaderValue, Method, StatusCode, Uri};
    use holger_public_site::setting;

    use super::Shared;

    // The rules and the follower are the shared crate's (RA2-15), through holger-public-site.
    pub use setting::{READ_DEADLINE, RECHECK};
    use setting::{effective, Follow, Said, Stored};

    struct Held {
        initial: Option<bool>,
        last: Option<Stored>,
        shared: Option<Arc<Shared>>,
    }

    static STATE: Mutex<Held> = Mutex::new(Held { initial: None, last: None, shared: None });
    static WATCHING: AtomicBool = AtomicBool::new(false);
    static FLIPS: AtomicU64 = AtomicU64::new(0);
    static RECHECK_MS: AtomicU64 = AtomicU64::new(RECHECK.as_millis() as u64);

    fn state() -> std::sync::MutexGuard<'static, Held> {
        STATE.lock().unwrap_or_else(|p| p.into_inner())
    }

    pub fn recheck_every(every: Duration) {
        RECHECK_MS.store(u64::try_from(every.as_millis()).unwrap_or(u64::MAX).max(1), Ordering::SeqCst);
    }

    fn recheck() -> Duration {
        Duration::from_millis(RECHECK_MS.load(Ordering::SeqCst))
    }

    /// The initial position: the front's feed where it fed one, else the environment's.
    fn initial(s: &Held) -> bool {
        s.initial.unwrap_or_else(|| holger_public_site::starts_on(std::env::var(holger_public_site::ENV_SWITCH).ok().as_deref()))
    }

    /// Show the position the state says.
    fn show(s: &Held) {
        let on = effective(initial(s), s.last);
        let site = super::public_site();
        if site.switch().is_on() != on {
            site.switch().set(on);
            tracing::info!(on, stored = ?s.last, initial = initial(s), "console: the public site's switch");
        }
    }

    /// Whether holger has answered a usable position since start.
    pub fn stored() -> bool {
        matches!(state().last, Some(Stored::At(_) | Stored::Unset))
    }

    /// The front's feed, the initial position.
    pub fn fed(on: bool) {
        let mut s = state();
        s.initial = Some(on);
        show(&s);
    }

    fn json(status: StatusCode, body: String) -> Response {
        (status, [(header::CONTENT_TYPE, "application/json"), (header::CACHE_CONTROL, "no-store")], body).into_response()
    }

    /// One call to holger's setting door: `bearer` is the person's own credential, `proof` the
    /// console's (never both, never the proof as a credential).
    async fn ask(s: &Shared, method: Method, bearer: Option<HeaderValue>, proof: Option<HeaderValue>, body: Vec<u8>) -> Result<(StatusCode, Vec<u8>), String> {
        let uri: Uri = format!("{}{}", s.up.door, setting::DOOR).parse().map_err(|e| format!("{e}"))?;
        let mut req = http::Request::builder().method(method).uri(uri);
        if let Some(b) = bearer {
            req = req.header(header::AUTHORIZATION, b);
        }
        if let Some(p) = proof {
            req = req.header(super::PROXY_AUTHORIZATION, p);
        }
        let req = req.body(Body::from(body)).map_err(|e| e.to_string())?;
        let res = s.client.request(req).await.map_err(|e| e.to_string())?;
        let status = res.status();
        let bytes = axum::body::to_bytes(Body::new(res.into_body()), 64 * 1024).await.map_err(|e| e.to_string())?;
        Ok((status, bytes.to_vec()))
    }

    /// One read of the stored position, with the console's proof.
    async fn read_once(s: &Shared) -> Result<Stored, String> {
        let Some(proof) = s.up.proof.as_deref().and_then(|p| HeaderValue::from_str(&format!("Bearer {p}")).ok()) else {
            return Err("this console has no bearer (HOLGER_UI_TOKEN), so it cannot ask holger".to_owned());
        };
        let (status, body) = match tokio::time::timeout(READ_DEADLINE, ask(s, Method::GET, None, Some(proof), Vec::new())).await {
            Ok(r) => r?,
            Err(_) => return Err(format!("holger did not answer within {READ_DEADLINE:?}")),
        };
        setting::stored_in(status.as_u16(), &body).ok_or_else(|| format!("holger answered {status}: {}", String::from_utf8_lossy(&body)))
    }

    /// **Follow the stored position for the life of the process**, over the newest wiring
    /// (the front rebuilds the router when the primary or the token moves; one watcher).
    pub fn follow(s: Arc<Shared>) {
        state().shared = Some(s);
        let Ok(runtime) = tokio::runtime::Handle::try_current() else { return };
        if WATCHING.swap(true, Ordering::SeqCst) {
            return;
        }
        runtime.spawn(async {
            let mut follow: Follow<Stored> = Follow::default();
            loop {
                let flips = FLIPS.load(Ordering::SeqCst);
                let Some(shared) = state().shared.clone() else { return };
                let answer = read_once(&shared).await;
                // A flip on this console while the read was on its way is newer than it.
                let older = flips != FLIPS.load(Ordering::SeqCst);
                let step = follow.answered(answer, older, |s| *s == Stored::Damaged, recheck());
                for said in &step.said {
                    match said {
                        Said::Damaged => tracing::error!("ALERT [console-switch-damaged] holger's settings are damaged: the public site is OFF until an administrator flips it"),
                        Said::Unread(why) => tracing::error!(%why, "ALERT [console-switch-unread] the public site's stored switch could not be read: it stays OFF until it is"),
                        Said::Kept(why) => tracing::warn!(%why, "console: the public site's stored switch could not be read again: it keeps the last one read"),
                        Said::Again(failures) => tracing::info!(failures, "console: the public site's stored switch is read again"),
                    }
                }
                if !older {
                    let mut s = state();
                    s.last = follow.last().copied();
                    show(&s);
                }
                let pause = step.pause;
                tokio::time::sleep(pause).await;
            }
        });
    }

    pub async fn read() -> Response {
        json(StatusCode::OK, setting::answer(super::public_site().switch().is_on()))
    }

    /// A flip, written with the asker's OWN credential (never the console's proof).
    pub async fn flip(State(s): State<Arc<Shared>>, req: Request) -> Response {
        let bearer = req.headers().get(header::AUTHORIZATION).cloned();
        let Ok(body) = axum::body::to_bytes(req.into_body(), 1024).await else {
            return json(StatusCode::PAYLOAD_TOO_LARGE, setting::refusal("send {\"on\": true} or {\"on\": false}"));
        };
        let Some(on) = setting::asked(&body) else {
            return json(StatusCode::BAD_REQUEST, setting::refusal("send {\"on\": true} or {\"on\": false}"));
        };
        let Some(bearer) = bearer else {
            return json(StatusCode::UNAUTHORIZED, setting::refusal("only an administrator switches the public site: sign in"));
        };
        match ask(&s, Method::PUT, Some(bearer), None, setting::stored(on).to_vec()).await {
            Ok((StatusCode::OK, _)) => {
                FLIPS.fetch_add(1, Ordering::SeqCst);
                let mut st = state();
                st.last = Some(Stored::At(on));
                show(&st);
                tracing::info!(target: "holger_ui::audit", event = "console.switch", name = setting::NAME, on, "the public site's switch was flipped (holger audited who)");
                json(StatusCode::OK, setting::answer(on))
            }
            Ok((status @ (StatusCode::UNAUTHORIZED | StatusCode::FORBIDDEN), _)) => json(status, setting::refusal("only an administrator switches the public site")),
            Ok((status, answer)) => json(StatusCode::BAD_GATEWAY, setting::refusal(&format!("Holger did not keep the switch ({status}: {}), so it was not flipped", String::from_utf8_lossy(&answer)))),
            Err(why) => json(StatusCode::BAD_GATEWAY, setting::refusal(&format!("Holger is not answering, so the switch was not flipped: {why}"))),
        }
    }

}

/// Whether the public site's switch holds holger's stored position (`public-site` builds):
/// the front's feed then no longer moves it. Always false in a default build.
pub fn public_site_switch_stored() -> bool {
    #[cfg(feature = "public-site")]
    {
        switch::stored()
    }
    #[cfg(not(feature = "public-site"))]
    {
        false
    }
}

/// **The front's feed position for the public site** (`console_public_site`): the INITIAL
/// position, shown only where holger stores none (RA2-1). A default build ignores it.
pub fn public_site_fed(on: bool) {
    #[cfg(feature = "public-site")]
    switch::fed(on);
    #[cfg(not(feature = "public-site"))]
    let _ = on;
}

/// Tests only: how often the stored switch is read again.
#[doc(hidden)]
pub fn recheck_every(every: std::time::Duration) {
    #[cfg(feature = "public-site")]
    switch::recheck_every(every);
    #[cfg(not(feature = "public-site"))]
    let _ = every;
}

/// **holger.rs's public face** (`public-site`): ONE site for the process, so a
/// router rebuilt when the primary or the token moves keeps its pages and its
/// rate limits. It is OFF unless `HOLGER_UI_PUBLIC_SITE=on` (robots.txt then says
/// `Disallow: /`; only holger.rs's front is fed `console_public_site = on`),
/// `HOLGER_UI_PUBLIC_AI_CRAWLERS=deny` turns the AI crawlers away and
/// `HOLGER_UI_PUBLIC_ORIGIN` names the public origin (unset: each request's host).
#[cfg(feature = "public-site")]
pub fn public_site() -> Arc<holger_public_site::Site> {
    static SITE: std::sync::OnceLock<Arc<holger_public_site::Site>> = std::sync::OnceLock::new();
    SITE.get_or_init(|| {
        let mut config = holger_public_site::Config::holger_from_env();
        config.signup = cfg!(feature = "metered");
        // OFF until holger has said what it stores (RA2-1: fail closed); the environment's and
        // the feed's position is only the initial one (`switch`).
        let on = false;
        let client = Arc::new(|r: &Request| r.extensions().get::<ConnectInfo<SocketAddr>>().map(|c| c.0.ip().to_string()).unwrap_or_else(|| "unknown".to_owned()));
        holger_public_site::Site::with_client(config, on, client)
    })
    .clone()
}

#[cfg(feature = "metered")]
fn script(body: String) -> axum::routing::MethodRouter {
    get(move || {
        let body = body.clone();
        async move { ([(header::CONTENT_TYPE, "text/javascript; charset=utf-8"), (header::CACHE_CONTROL, "no-cache")], body).into_response() }
    })
}

/// The bundle: files as they are, `/login` the static passkey page, any other
/// path the console's `index.html` (the console routes its own deep links).
/// The content-security-policy covers the bundle only: holger's own pages carry
/// holger's headers.
fn bundle(dir: &Path, scripts: Option<BuyScripts>, sells: bool) -> Router {
    let csp = HeaderValue::from_str(&build_csp(&inline_hashes(dir))).unwrap_or_else(|_| HeaderValue::from_static("default-src 'self'"));
    let files = ServeDir::new(dir).append_index_html_on_directories(false).fallback(ServeFile::new(dir.join("index.html")));
    // The buy routes are BUY_ROUTES', every one (CI lane): a buy route is a row there.
    let pages = BUY_ROUTES.iter().fold(Router::new(), |r, row| match row.owner {
        // LOW-PRIV: a console that sells nothing has no sign-up page; it says this Holger is private.
        BuyOwner::Page(_) if row.path == SIGNUP_PATH && !sells => r.route(row.path, get(private_signup)),
        BuyOwner::Page(file) => r.route_service(row.path, ServeFile::new(dir.join(file))),
        // The buy pages' scripts are the front's; where none is handed in, a 404, never index.html.
        #[cfg(feature = "metered")]
        BuyOwner::Script(which) => match &scripts {
            Some(s) => r.route(row.path, script(match which { BuyScript::Signup => s.signup.clone(), BuyScript::Welcome => s.welcome.clone(), BuyScript::Paid => s.paid.clone() })),
            None => r.route(row.path, get(|| async { StatusCode::NOT_FOUND })),
        },
        // A private Holger's pay link (lane P1): the sign-up page's manage view (A16, Gunnar's).
        #[cfg(feature = "metered")]
        BuyOwner::Renew => r.route(row.path, get(|uri: Uri| async move { axum::response::Redirect::permanent(&private_signup_address(uri.query())) })),
        #[cfg(feature = "metered")]
        BuyOwner::ToBuyPage => r.route(row.path, get(|| async { axum::response::Redirect::to(BUY_PAGE) })),
        // A16: the buy doors are the relay's (merged in front by holger's front where the site
        // sells); where it is not merged they are a 404 the buy pages read, never `index.html`.
        #[cfg(feature = "metered")]
        BuyOwner::Relay => r.route(row.path, any(no_buy_door)),
    });
    #[cfg(not(feature = "metered"))]
    let pages = {
        let _ = (&scripts, sells);
        // An open build sells nothing: its /signup says this Holger is private (LOW-PRIV).
        OPEN_REFUSES.iter().fold(pages, |r, path| if *path == SIGNUP_PATH { r.route(path, any(private_signup)) } else { r.route(path, any(|| async { StatusCode::NOT_FOUND })) })
    };
    // A34-51: the login page helpers, edda's one copy for both consoles, beside the bundle.
    let pages = pages.route(nornir_identity_web::login_js::PATH, get(|| async {
        ([(header::CONTENT_TYPE, "text/javascript; charset=utf-8"), (header::CACHE_CONTROL, "no-cache")], nornir_identity_web::login_js::HELPERS).into_response()
    }));
    pages
        .fallback_service(files)
        .layer(axum::middleware::from_fn(move |req: Request, next: axum::middleware::Next| {
            let csp = csp.clone();
            async move {
                let mut res = next.run(req).await;
                let h = res.headers_mut();
                h.insert(header::CONTENT_SECURITY_POLICY, csp);
                h.insert(header::X_CONTENT_TYPE_OPTIONS, HeaderValue::from_static("nosniff"));
                h.insert(header::REFERRER_POLICY, HeaderValue::from_static("no-referrer"));
                res
            }
        }))
        .layer(CompressionLayer::new())
}

/// What a buy page reads where this site's front sells nothing (the shared relay is not merged):
/// the words of Gunnar's sign-up page for a console that sells nothing.
#[cfg(feature = "metered")]
pub const NOTHING_SOLD_HERE: &str = "Nothing is sold by this site.";

#[cfg(feature = "metered")]
async fn no_buy_door() -> Response {
    (StatusCode::NOT_FOUND, [(header::CACHE_CONTROL, "no-store"), (header::CONTENT_TYPE, "application/json")], format!("{{\"error\":\"{NOTHING_SOLD_HERE}\"}}")).into_response()
}

async fn pass_through(State(s): State<Arc<Shared>>, mut req: Request) -> Response {
    let path = req.uri().path().to_owned();
    let method = req.method().clone();
    let (base, upstream_path) = match route_of(&path) {
        Route::Grpc => (&s.up.grpc, grpc_path(req.uri())),
        _ => (&s.up.door, req.uri().path_and_query().map(|p| p.as_str().to_owned()).unwrap_or_else(|| path.clone())),
    };
    let Ok(uri) = format!("{base}{upstream_path}").parse::<Uri>() else {
        return (StatusCode::BAD_REQUEST, "not a path holger can be asked").into_response();
    };
    let peer = req.extensions().get::<ConnectInfo<SocketAddr>>().map(|c| c.0.ip().to_string());
    let h = req.headers_mut();
    for hop in ["connection", "keep-alive", "proxy-connection", "transfer-encoding", "upgrade", CEREMONY_CLIENT, PROXY_AUTHORIZATION] {
        h.remove(hop);
    }
    h.remove(header::HOST);
    if path.starts_with("/-/auth/passkey/") {
        if let (Some(peer), Some(proof)) = (peer, s.up.proof.as_deref()) {
            if let (Ok(c), Ok(p)) = (HeaderValue::from_str(&peer), HeaderValue::from_str(&format!("Bearer {proof}"))) {
                h.insert(HeaderName::from_static(CEREMONY_CLIENT), c);
                h.insert(HeaderName::from_static(PROXY_AUTHORIZATION), p);
            }
        }
    }
    *req.uri_mut() = uri;
    *req.version_mut() = Version::HTTP_11;
    match s.client.request(req).await {
        Ok(res) => session::answer(&method, &path, res.map(Body::new).into_response()).await,
        Err(e) => {
            tracing::warn!(upstream = %base, error = %e, "holger did not answer");
            (StatusCode::BAD_GATEWAY, "holger is not answering right now; try again in a moment").into_response()
        }
    }
}

/// Every inline script the bundle serves (trunk's bootstrap in `index.html`).
fn inline_hashes(dir: &Path) -> Vec<String> {
    match std::fs::read_to_string(dir.join("index.html")) {
        Ok(index) => inline_script_hashes(&index),
        Err(why) => {
            tracing::warn!(dir = %dir.display(), error = %why, "the console's index.html could not be read; its inline script is not in the content-security-policy");
            Vec::new()
        }
    }
}

/// The `sha256-…` tokens for every inline `<script>` in `html` — what lets
/// `script-src` ship without `'unsafe-inline'`.
pub fn inline_script_hashes(html: &str) -> Vec<String> {
    use base64::Engine as _;
    use sha2::{Digest, Sha256};
    let lower = html.to_ascii_lowercase();
    let mut hashes = Vec::new();
    let mut at = 0usize;
    while let Some(offset) = lower[at..].find("<script") {
        let tag_start = at + offset;
        let Some(gt) = lower[tag_start..].find('>') else { break };
        let open_end = tag_start + gt + 1;
        let Some(close_offset) = lower[open_end..].find("</script>") else { break };
        let body_end = open_end + close_offset;
        let body = &html[open_end..body_end];
        if !lower[tag_start..open_end].contains("src=") && !body.trim().is_empty() {
            hashes.push(format!("sha256-{}", base64::engine::general_purpose::STANDARD.encode(Sha256::digest(body.as_bytes()))));
        }
        at = body_end + "</script>".len();
    }
    hashes.sort();
    hashes.dedup();
    hashes
}

/// The bundle's policy, given its inline scripts.
pub fn build_csp(script_hashes: &[String]) -> String {
    let mut script = String::from("'self' 'wasm-unsafe-eval'");
    for hash in script_hashes {
        script.push_str(&format!(" '{hash}'"));
    }
    format!(
        "default-src 'none'; base-uri 'none'; frame-src 'none'; frame-ancestors 'none'; \
         form-action 'self'; object-src 'none'; img-src 'self' data:; font-src 'self'; \
         style-src 'self' 'unsafe-inline'; connect-src 'self'; script-src {script}"
    )
}

#[cfg(test)]
mod tests {
    use super::*;
    use http_body_util::BodyExt;
    use tower::ServiceExt;

    #[test]
    fn every_path_has_one_home() {
        assert_eq!(route_of("/grpc/holger.v1.AdminService/Health"), Route::Grpc);
        assert_eq!(route_of("/-/auth/passkey/login/start"), Route::Door);
        assert_eq!(route_of("/-/front"), Route::Door);
        assert_eq!(route_of("/"), Route::Door, "the front page is holger's");
        assert_eq!(route_of("/holger.webp"), Route::Door);
        assert_eq!(route_of("/login"), Route::Bundle, "the login page is the bundle's");
        assert_eq!(route_of("/overview"), Route::Bundle);
        assert_eq!(route_of("/grpcx"), Route::Bundle);
        assert_eq!(grpc_path(&"/grpc/holger.v1.AdminService/Health?x=1".parse().unwrap()), "/holger.v1.AdminService/Health?x=1");
    }

    /// ★ A16 (item d): **a buy door is never answered by the console's page.** Where holger's
    /// front merges the shared relay, the relay answers every buy door; where it does not (a
    /// sold instance, a site that sells nothing), the door answers a 404 the buy pages read as
    /// "Nothing is sold by this site." (Gunnar's console registers no buy door there), never the
    /// SPA's `index.html` that a page would fail to read as JSON.
    #[cfg(feature = "metered")]
    #[tokio::test]
    async fn a_buy_door_is_the_relays_or_a_404_never_the_spa() {
        let dir = tempfile::tempdir().unwrap();
        for (f, body) in [("index.html", "console"), ("login.html", "passkey page"), ("signup.html", "signup page"), ("welcome.html", "welcome page"), ("paid.html", "order page")] {
            std::fs::write(dir.path().join(f), body).unwrap();
        }
        let get = |p: &str| http::Request::get(p).body(Body::empty()).unwrap();
        let alone = router(Some(dir.path().to_path_buf()), Upstreams::new("127.0.0.1:1", "127.0.0.1:1", None));
        for door in ["/auth/signup/ladder", "/auth/signup/status?session=cs_test_abcdefghij", "/auth/private/status?company=acme", "/auth/buy/offer", "/api/account/purchases", "/api/account/subscription/status?reference=p-1"] {
            let (st, body) = ask(alone.clone(), get(door)).await;
            assert_eq!(st, StatusCode::NOT_FOUND, "{door}: {body}");
            assert!(body.contains("Nothing is sold by this site."), "{door}: {body}");
        }
        for (page, body) in [("/signup", "signup page"), ("/welcome", "welcome page"), ("/paid/p-1", "order page"), ("/cancel/p-1", "order page")] {
            assert_eq!(ask(alone.clone(), get(page)).await.1, body, "{page}");
        }
        let relay = Router::new().route("/auth/signup/ladder", axum::routing::get(|| async { "relay ladder" }));
        let merged = relay.merge(router(Some(dir.path().to_path_buf()), Upstreams::new("127.0.0.1:1", "127.0.0.1:1", None)));
        assert_eq!(ask(merged, get("/auth/signup/ladder")).await.1, "relay ladder", "the relay answers where it is merged");
    }

    /// ★ A16 (owner 2026-10-23: a buyer never sees monetize): **the console's buy button names
    /// no shop**; it is the site's own page.
    #[test]
    fn the_consoles_buy_button_names_no_shop() {
        let src = std::fs::read_to_string(std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../src/pages/repository_detail.rs")).unwrap();
        assert!(!src.contains("in the shop\"") && src.contains("\"Buy more room\""), "the button");
    }

    /// ★ A16 (B61, B81; gunnar-ui-web `purchase::vendor_address`, `storage_goes_to`): **the pay
    /// link in every refusal (`/billing/{tenant}`) and `/account/storage` lead to the buy page**,
    /// the sign-up page's account choice (signed in: "Buy a plan" / "Grow your account").
    #[cfg(feature = "metered")]
    #[tokio::test]
    async fn the_pay_link_and_the_storage_page_lead_to_the_buy_page() {
        let dir = tempfile::tempdir().unwrap();
        std::fs::write(dir.path().join("index.html"), "console").unwrap();
        let app = router(Some(dir.path().to_path_buf()), Upstreams::new("127.0.0.1:1", "127.0.0.1:1", None));
        for from in ["/billing/x14", "/billing/holger/x14", "/account/storage"] {
            let res = app.clone().oneshot(http::Request::get(from).body(Body::empty()).unwrap()).await.unwrap();
            assert!(res.status().is_redirection(), "{from}: {}", res.status());
            assert_eq!(res.headers().get(header::LOCATION).unwrap(), BUY_PAGE, "{from}");
        }
        assert_eq!(BUY_PAGE, "/signup?plan=account");
    }

    /// ★ A16 (owner 2026-10-23): **the buy pages' scripts are the shared crate's**, handed in by
    /// the front that links monetize-site-buy (this lone-cloneable crate cannot): `/signup.js`
    /// and `/welcome.js` are served as given, under the bundle's CSP; the bundle holds no copy.
    #[cfg(feature = "metered")]
    #[tokio::test]
    async fn the_buy_scripts_are_the_ones_the_front_hands_in() {
        let dir = tempfile::tempdir().unwrap();
        for (f, body) in [("index.html", "console"), ("signup.html", "signup page")] {
            std::fs::write(dir.path().join(f), body).unwrap();
        }
        let scripts = BuyScripts { signup: "// the crate's sign-up script".into(), welcome: "// the crate's welcome script".into(), paid: "// the crate's order page script".into() };
        let app = router_with(Some(dir.path().to_path_buf()), Upstreams::new("127.0.0.1:1", "127.0.0.1:1", None), Some(scripts));
        let get = |p: &str| http::Request::get(p).body(Body::empty()).unwrap();
        let res = app.clone().oneshot(get("/signup.js")).await.unwrap();
        assert_eq!(res.headers().get(header::CONTENT_TYPE).unwrap(), "text/javascript; charset=utf-8");
        assert!(res.headers().get(header::CONTENT_SECURITY_POLICY).is_some(), "the bundle's CSP");
        assert_eq!(ask(app.clone(), get("/signup.js")).await.1, "// the crate's sign-up script");
        assert_eq!(ask(app.clone(), get("/welcome.js")).await.1, "// the crate's welcome script");
        assert_eq!(ask(app, get("/paid.js")).await.1, "// the crate's order page script");
        let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../assets");
        assert!(!root.join("signup.js").exists() && !root.join("welcome.js").exists(), "no copy in the bundle");
    }

    /// ★ T16 (A28 M11): **the order page's script is the shared crate's too** (monetize-site-buy
    /// `assets::paid_js`, ONE copy for Gunnar and Holger; Holger's hand port had drifted: it
    /// ignored `captured`). `/paid.js` is a script the front hands in, and the bundle holds no
    /// copy of it.
    #[cfg(feature = "metered")]
    #[test]
    fn the_order_pages_script_is_handed_in_and_the_bundle_holds_no_copy() {
        let row = BUY_ROUTES.iter().find(|r| r.path == "/paid.js").expect("/paid.js is a buy route");
        assert!(matches!(row.owner, BuyOwner::Script(_)), "/paid.js is the front's script, not a bundle file: {:?}", row.owner);
        let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
        assert!(!root.join("assets/paid.js").exists(), "no copy of the order page's script in the bundle");
        let index = std::fs::read_to_string(root.join("index-metered.html")).unwrap();
        assert!(!index.contains("assets/paid.js"), "trunk copies no paid.js");
        assert!(index.contains("assets/paid.html"), "the order page itself stays Holger's");
    }

    /// ★ A12 (owner 2026-10-23: the buyer stays on the site): **a vendor's way back is the
    /// site's own page** — `/paid/{reference}` and `/cancel/{reference}` are the bundle's
    /// `paid.html` (Gunnar's PaidReturn / CancelledReturn panes), `/welcome` is `welcome.html`
    /// (Gunnar's welcome page); a private order's way back (`/paid/estate/<company>`) stays the
    /// relay's, merged in front by holger's front; never holger's door, never the shop.
    #[cfg(feature = "metered")]
    #[tokio::test]
    async fn a_vendors_way_back_is_the_sites_own_page() {
        assert_eq!(route_of("/paid/holger/acme/7"), Route::Bundle);
        assert_eq!(route_of("/cancel/holger/acme/7"), Route::Bundle);
        assert_eq!(route_of("/welcome"), Route::Bundle);
        let dir = tempfile::tempdir().unwrap();
        for (f, body) in [("index.html", "console"), ("login.html", "passkey page"), ("paid.html", "order page"), ("welcome.html", "welcome page")] {
            std::fs::write(dir.path().join(f), body).unwrap();
        }
        let relay = Router::new().route("/paid/estate/{company}", axum::routing::get(|axum::extract::Path(c): axum::extract::Path<String>| async move { format!("relay {c}") }));
        let app = relay.merge(router(Some(dir.path().to_path_buf()), Upstreams::new("127.0.0.1:1", "127.0.0.1:1", None)));
        let get = |p: &str| http::Request::get(p).body(Body::empty()).unwrap();
        assert_eq!(ask(app.clone(), get("/paid/holger/x14/1")).await.1, "order page");
        assert_eq!(ask(app.clone(), get("/cancel/holger/x14/1")).await.1, "order page");
        assert_eq!(ask(app.clone(), get("/welcome?session=cs_test_abcdefghij")).await.1, "welcome page");
        assert_eq!(ask(app, get("/paid/estate/acme")).await.1, "relay acme", "the private order's way back is the relay's");
    }

    /// The METERED bundle as trunk lays it (A28 M13: the paid pages are this bundle's only):
    /// every file `index-metered.html` copies (`data-trunk rel="copy-file"`), with `index.html`
    /// itself the SPA fallback's marker.
    const SPA: &str = "<!-- the SPA fallback -->";
    fn trunk_bundle() -> (tempfile::TempDir, Vec<String>) {
        let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
        let index = std::fs::read_to_string(root.join("index-metered.html")).unwrap();
        let dir = tempfile::tempdir().unwrap();
        let mut copied = Vec::new();
        for line in index.lines().filter(|l| l.contains("data-trunk") && l.contains("copy-file")) {
            let href = line.split("href=\"").nth(1).and_then(|r| r.split('"').next()).unwrap();
            let name = std::path::Path::new(href).file_name().unwrap().to_str().unwrap().to_owned();
            std::fs::copy(root.join(href), dir.path().join(&name)).unwrap();
            copied.push(name);
        }
        std::fs::write(dir.path().join("index.html"), SPA).unwrap();
        (dir, copied)
    }

    /// A row's path as a request (`{*x}` filled in).
    fn sample(path: &str) -> String {
        match path.find("/{") {
            Some(at) => format!("{}/holger/acme/7", &path[..at]),
            None => path.to_owned(),
        }
    }

    /// ★ CI lane (A11 fix 3 failed live: holger-ui-server did not route `/paid`, so the shop's
    /// way back drew the console): **every buy route answers from its owner, never from the SPA
    /// fallback**, over the bundle exactly as trunk lays it, both where the front hands in the
    /// buy scripts and where it does not. Every inbound buy address (the shop's vendor returns,
    /// the mailed pay link, the buy page) is a row's, and every script a buy page loads is a file
    /// trunk copies or a row's.
    #[cfg(feature = "metered")]
    #[tokio::test]
    async fn every_buy_route_answers_from_its_owner_never_the_spa() {
        let (dir, copied) = trunk_bundle();
        let up = || Upstreams::new("127.0.0.1:1", "127.0.0.1:1", None);
        let scripts = BuyScripts { signup: "// signup script".into(), welcome: "// welcome script".into(), paid: "// order page script".into() };
        let get = |p: &str| http::Request::get(p).body(Body::empty()).unwrap();
        for (handed, app) in [(true, router_with(Some(dir.path().to_path_buf()), up(), Some(scripts.clone()))), (false, router(Some(dir.path().to_path_buf()), up()))] {
            for row in BUY_ROUTES {
                let path = sample(row.path);
                let res = app.clone().oneshot(get(&path)).await.unwrap();
                let (status, location) = (res.status(), res.headers().get(header::LOCATION).map(|l| l.to_str().unwrap().to_owned()));
                let body = String::from_utf8(res.into_body().collect().await.unwrap().to_bytes().to_vec()).unwrap();
                assert_ne!(body, SPA, "{path} (scripts handed in: {handed}) is answered by the SPA fallback, not {:?}", row.owner);
                match row.owner {
                    BuyOwner::Page(file) => {
                        assert!(copied.iter().any(|c| c == file), "{path}: {file} is not copied by trunk (index.html copy-file)");
                        assert_eq!((status, body), (StatusCode::OK, std::fs::read_to_string(dir.path().join(file)).unwrap()), "{path}");
                    }
                    BuyOwner::Script(which) if handed => {
                        let want = match which { BuyScript::Signup => &scripts.signup, BuyScript::Welcome => &scripts.welcome, BuyScript::Paid => &scripts.paid };
                        assert_eq!((status, &body), (StatusCode::OK, want), "{path}");
                    }
                    BuyOwner::Script(_) => assert_eq!(status, StatusCode::NOT_FOUND, "{path}: no script handed in"),
                    BuyOwner::Renew | BuyOwner::ToBuyPage => assert!(status.is_redirection() && location.as_deref().is_some_and(|l| l.starts_with(SIGNUP_PATH)), "{path}: {status} {location:?}"),
                    BuyOwner::Relay => assert!(status == StatusCode::NOT_FOUND && body.contains(NOTHING_SOLD_HERE), "{path}: {status} {body}"),
                }
            }
        }
        // Every inbound buy address is a row's (A11 fix 3: the shop's way back was not). The
        // shop's vendor returns are monetize's contract, spelled here on their own.
        assert_eq!(VENDOR_RETURNS, ["/paid/", "/cancel/"], "the shop sends a buyer back to /paid/ and /cancel/");
        let row_of = |p: &str| {
            let p = p.split('?').next().unwrap();
            BUY_ROUTES.iter().find(|r| match r.path.find("/{") { Some(at) => p.starts_with(&r.path[..=at]), None => p == r.path })
        };
        for inbound in ["/paid/", "/cancel/"].iter().map(|r| format!("{r}holger/acme/7")).chain([RENEW_PATH.to_owned(), BUY_PAGE.to_owned(), "/billing/x14".to_owned()]) {
            assert!(row_of(&inbound).is_some(), "{inbound} is an inbound buy address no buy route answers");
        }
        // Every buy address the console and its pages link to is a row's, and every script a
        // page loads is a file trunk copies or a row's.
        let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
        let mut sources = vec![root.join("index.html"), root.join("index-metered.html")];
        for sub in ["assets", "src", "src/pages", "src/components"] {
            for e in std::fs::read_dir(root.join(sub)).unwrap().flatten() {
                let p = e.path();
                if p.extension().is_some_and(|x| x == "rs" || x == "html" || x == "js") {
                    sources.push(p);
                }
            }
        }
        const BUY_WORDS: [&str; 8] = ["paid", "cancel", "signup", "welcome", "private", "billing", "auth", "login"];
        for file in &sources {
            let text = std::fs::read_to_string(file).unwrap();
            for lit in text.split('"').skip(1).step_by(2).filter(|l| l.starts_with('/') && !l.starts_with("//")) {
                let first = lit.trim_start_matches('/').split(['/', '?', '.']).next().unwrap_or("");
                let script = lit.ends_with(".js") && lit.matches('/').count() == 1;
                if script {
                    let name = &lit[1..];
                    // The login helpers are edda's script, served by the bundle itself (A34-51).
                    let served = lit == nornir_identity_web::login_js::PATH;
                    assert!(served || copied.iter().any(|c| c == name) || row_of(lit).is_some(), "{}: {lit} is loaded but neither copied by trunk nor a buy route", file.display());
                } else if BUY_WORDS.contains(&first) || lit.starts_with("/api/account/") || lit.starts_with("/account/storage") {
                    assert!(row_of(lit).is_some(), "{}: {lit} is a buy address no buy route answers", file.display());
                }
            }
        }
    }

    #[test]
    fn the_policy_hashes_trunks_bootstrap_and_never_allows_inline() {
        let h = inline_script_hashes("<script type=\"module\">init()</script><script src=\"/x.js\"></script>");
        assert_eq!(h.len(), 1);
        let csp = build_csp(&h);
        let script = csp.split("script-src").nth(1).unwrap();
        assert!(script.contains(&format!("'{}'", h[0])) && !script.contains("unsafe-inline") && csp.contains("img-src 'self' data:"), "{csp}");
    }

    /// A stand-in for holger: answers with what it was sent.
    async fn echo() -> String {
        let l = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
        let at = l.local_addr().unwrap();
        let app = Router::new().fallback(|req: Request| async move {
            let h = req.headers();
            let seen = |k: &str| h.get(k).and_then(|v| v.to_str().ok()).unwrap_or("-").to_owned();
            format!("{} {} auth={} client={} proof={}", req.method(), req.uri(), seen("authorization"), seen(CEREMONY_CLIENT), seen(PROXY_AUTHORIZATION))
        });
        tokio::spawn(async move { axum::serve(l, app).await.unwrap() });
        at.to_string()
    }

    async fn ask(app: Router, req: http::Request<Body>) -> (StatusCode, String) {
        let res = app.oneshot(req).await.unwrap();
        let status = res.status();
        (status, String::from_utf8(res.into_body().collect().await.unwrap().to_bytes().to_vec()).unwrap())
    }

    /// ★ **A34-38: the session reaches the browser only as an HttpOnly cookie, and rides every
    /// call.** holger's login answer leaves this server without its token and with
    /// `__Host-holger_session`; a call with that cookie reaches holger as the person's bearer,
    /// on the door and on gRPC-web; a caller's own bearer stands; logout clears the cookie; a
    /// pasted bearer is taken as JSON only.
    #[tokio::test]
    async fn the_session_is_an_httponly_cookie_that_rides_every_call() {
        let l = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
        let at = l.local_addr().unwrap().to_string();
        let fake = Router::new()
            .route("/-/auth/passkey/login/finish", axum::routing::post(|| async { r#"{"token":"tok-1","display":"anna"}"# }))
            .fallback(|req: Request| async move {
                format!("{} auth={}", req.uri().path(), req.headers().get("authorization").and_then(|v| v.to_str().ok()).unwrap_or("-"))
            });
        tokio::spawn(async move { axum::serve(l, fake).await.unwrap() });
        let app = router(None, Upstreams::new(&at, &at, None));

        let res = app.clone().oneshot(http::Request::post("/-/auth/passkey/login/finish").body(Body::empty()).unwrap()).await.unwrap();
        let set = res.headers().get(header::SET_COOKIE).and_then(|v| v.to_str().ok()).unwrap_or_default().to_owned();
        assert!(set.starts_with(&session::set_line("tok-1")), "edda's one session cookie: {set}");
        let page = String::from_utf8(res.into_body().collect().await.unwrap().to_bytes().to_vec()).unwrap();
        assert!(!page.contains("tok-1") && page.contains("\"signed_in\":true"), "the page never reads the token: {page}");

        // The browser's own page: its Origin is this site's (RA5-5).
        let with = |p: &str| http::Request::post(p).header(header::HOST, "holger.mock.invalid").header(header::ORIGIN, "https://holger.mock.invalid").header(header::COOKIE, "__Host-holger_session=tok-1").body(Body::empty()).unwrap();
        assert_eq!(ask(app.clone(), with("/-/auth/passkey/session")).await.1, "/-/auth/passkey/session auth=Bearer tok-1");
        assert_eq!(ask(app.clone(), with("/grpc/holger.v1.AdminService/Health")).await.1, "/holger.v1.AdminService/Health auth=Bearer tok-1");
        let own = http::Request::post("/-/front").header(header::COOKIE, "__Host-holger_session=tok-1").header("authorization", "Bearer machine").body(Body::empty()).unwrap();
        assert_eq!(ask(app.clone(), own).await.1, "/-/front auth=Bearer machine", "a caller's own bearer stands");

        let res = app.clone().oneshot(with(session::SESSION_ENDS)).await.unwrap();
        assert!(res.headers().get_all(header::SET_COOKIE).iter().any(|v| v.to_str().unwrap_or("").ends_with("Max-Age=0")), "logout clears the cookie");

        let form = http::Request::post(session::TOKEN_DOOR).header(header::CONTENT_TYPE, "application/x-www-form-urlencoded").body(Body::from("token=evil")).unwrap();
        assert_eq!(app.clone().oneshot(form).await.unwrap().status(), StatusCode::UNSUPPORTED_MEDIA_TYPE, "no cross-site form sets a session");
        let pasted = http::Request::post(session::TOKEN_DOOR).header(header::CONTENT_TYPE, "application/json").body(Body::from(r#"{"token":"operator-token"}"#)).unwrap();
        let res = app.oneshot(pasted).await.unwrap();
        assert_eq!(res.status(), StatusCode::NO_CONTENT);
        assert!(res.headers().get(header::SET_COOKIE).unwrap().to_str().unwrap().starts_with(&session::set_line("operator-token")));
    }

    /// ★ **RA5-11: a logout holger did not do keeps the cookie, and says holger's words.**
    /// holger answers its logout with a refusal (the core did not end the session): the
    /// browser gets that status and those words, and its cookie is NOT cleared, so the page
    /// cannot say "logged out" while the session lives on. holger down: a 502, cookie kept.
    /// A logout holger did is still the cookie's end.
    #[tokio::test]
    async fn a_logout_holger_refused_keeps_the_cookie_and_says_why() {
        let l = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
        let at = l.local_addr().unwrap().to_string();
        let fake = Router::new().route(
            session::SESSION_ENDS,
            axum::routing::post(|| async { (StatusCode::SERVICE_UNAVAILABLE, r#"{"error":"the identity core did not end the session"}"#) }),
        );
        tokio::spawn(async move { axum::serve(l, fake).await.unwrap() });
        let app = router(None, Upstreams::new(&at, &at, None));
        // The browser's own page: its Origin is this site's (RA5-5), so the logout reaches holger.
        let with = |p: &str| http::Request::post(p).header(header::HOST, "holger.mock.invalid").header(header::ORIGIN, "https://holger.mock.invalid").header(header::COOKIE, "__Host-holger_session=tok-1").body(Body::empty()).unwrap();
        let res = app.clone().oneshot(with(session::SESSION_ENDS)).await.unwrap();
        assert_eq!(res.status(), StatusCode::SERVICE_UNAVAILABLE, "holger's own answer");
        assert!(res.headers().get(header::SET_COOKIE).is_none(), "the cookie stays: the session did not end");
        let words = String::from_utf8(res.into_body().collect().await.unwrap().to_bytes().to_vec()).unwrap();
        assert!(words.contains("did not end the session"), "{words}");

        let down = {
            let l = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
            let at = l.local_addr().unwrap().to_string();
            drop(l);
            at
        };
        let app = router(None, Upstreams::new(&down, &down, None));
        let res = app.oneshot(with(session::SESSION_ENDS)).await.unwrap();
        assert_eq!(res.status(), StatusCode::BAD_GATEWAY);
        assert!(res.headers().get(header::SET_COOKIE).is_none(), "holger down: the cookie stays");
    }

    /// ★ A34-38: **no script of the console keeps a bearer** (the page reads who it is from
    /// holger; the cookie is out of its reach).
    #[test]
    fn no_script_of_the_console_keeps_a_bearer() {
        let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
        for f in ["assets/login.js", "src/grpc.rs", "src/session.rs", "src/step_up.rs", "src/app.rs", "src/pages/login.rs", "src/pages/admin.rs"] {
            let text = std::fs::read_to_string(root.join(f)).unwrap();
            assert!(!text.contains("holger-bearer-token") && !text.contains("session_storage()") && !text.contains("sessionStorage.setItem"), "{f} keeps a bearer in the page's reach");
        }
    }

    #[tokio::test]
    async fn calls_go_through_with_the_persons_bearer_and_the_hint_only_on_the_passkey_door() {
        let holger = echo().await;
        let app = router(None, Upstreams::new(&holger, &holger, Some("console-proof".into())));
        let mut req = http::Request::post("/-/auth/passkey/login/start")
            .header("authorization", "Bearer person")
            .header(CEREMONY_CLIENT, "forged")
            .body(Body::empty())
            .unwrap();
        req.extensions_mut().insert(ConnectInfo(SocketAddr::from(([192, 0, 2, 7], 4444))));
        let (status, said) = ask(app.clone(), req).await;
        assert_eq!(status, StatusCode::OK);
        assert_eq!(said, "POST /-/auth/passkey/login/start auth=Bearer person client=192.0.2.7 proof=Bearer console-proof");

        let req = http::Request::post("/grpc/holger.v1.AdminService/Health").header("authorization", "Bearer person").header(PROXY_AUTHORIZATION, "Bearer forged").body(Body::empty()).unwrap();
        let (_, said) = ask(app.clone(), req).await;
        assert_eq!(said, "POST /holger.v1.AdminService/Health auth=Bearer person client=- proof=-", "a browser can never send the proof");

        let (_, said) = ask(app, http::Request::get("/-/front").body(Body::empty()).unwrap()).await;
        assert_eq!(said, "GET /-/front auth=- client=- proof=-");
    }

    /// ★ A28 M13 (Gunnar's `the_open_console_serves_no_money_route`): **an open build's router
    /// has no money route at all**. Over the bundle exactly as trunk lays it (the paid pages'
    /// files are in it), every page, file and door of the paid half answers 404 and no document,
    /// never the console's `index.html`; the login page is still served.
    #[cfg(not(feature = "metered"))]
    #[tokio::test]
    async fn the_open_console_serves_no_money_route() {
        let (dir, copied) = trunk_bundle();
        assert!(copied.iter().any(|c| c == "paid.html") && copied.iter().any(|c| c == "signup.html"), "the bundle carries the paid pages' files: {copied:?}");
        let app = router(Some(dir.path().to_path_buf()), Upstreams::new("127.0.0.1:1", "127.0.0.1:1", None));
        let get = |p: &str| http::Request::get(p).body(Body::empty()).unwrap();
        let mut paths: Vec<String> = OPEN_REFUSES.iter().map(|p| sample(p)).collect();
        paths.extend(["/signup?plan=account", "/welcome?session=cs_test_a1b2c3d4e5", "/auth/signup/status?session=cs_test_a1b2c3d4e5", "/auth/buy/offer", "/api/account/purchases", "/api/account/subscription/status?reference=p-1", "/private?renew=acme", "/billing/holger/x14"].map(String::from));
        for path in &paths {
            let (status, body) = ask(app.clone(), get(path)).await;
            assert_eq!(status, StatusCode::NOT_FOUND, "the OPEN console answered {path}: a build without `metered` has no such route");
            assert!(body != SPA && !body.to_ascii_lowercase().contains("<html"), "{path} was answered with a document: {body}");
        }
        assert_eq!(ask(app.clone(), get("/login")).await, (StatusCode::OK, std::fs::read_to_string(dir.path().join("login.html")).unwrap()));
        assert_eq!(ask(app, get("/overview")).await.1, SPA, "the console's own deep links are still the console's");
    }

    #[tokio::test]
    async fn the_bundle_serves_files_the_login_page_and_falls_back_to_the_console() {
        let dir = tempfile::tempdir().unwrap();
        std::fs::write(dir.path().join("index.html"), "<script type=\"module\">boot()</script>console").unwrap();
        std::fs::write(dir.path().join("login.html"), "passkey page").unwrap();
        std::fs::write(dir.path().join("app.js"), "js").unwrap();
        let app = router(Some(dir.path().to_path_buf()), Upstreams::new("127.0.0.1:1", "127.0.0.1:1", None));
        let get = |p: &str| http::Request::get(p).body(Body::empty()).unwrap();
        assert_eq!(ask(app.clone(), get("/login")).await.1, "passkey page");
        assert_eq!(ask(app.clone(), get("/app.js")).await.1, "js");
        assert!(ask(app.clone(), get("/repo/crates")).await.1.ends_with("console"));
        let res = app.clone().oneshot(get("/overview")).await.unwrap();
        assert!(res.headers()[header::CONTENT_SECURITY_POLICY].to_str().unwrap().contains("'sha256-"));
        assert_eq!(ask(app.clone(), get("/health")).await.1, "ok");
        assert_eq!(ask(app, get("/-/front")).await.0, StatusCode::BAD_GATEWAY, "holger down is a 502, never a decision here");
    }

    /// ★ A16 (Gunnar's `/private`, auth/private.rs `private_page`): **the old buy page's address,
    /// and the renewal mail's link, go on to the ONE sign-up page** — its private choice, with
    /// only the fields it reads (`company`, `renew` lowercased), each the first of its name and
    /// only when it is a company's name. Nothing else rides along.
    #[cfg(feature = "metered")]
    #[tokio::test]
    async fn the_pay_link_goes_on_to_the_sign_up_pages_manage_view() {
        let dir = tempfile::tempdir().unwrap();
        std::fs::write(dir.path().join("index.html"), "console").unwrap();
        let holger = echo().await;
        let app = router(Some(dir.path().to_path_buf()), Upstreams::new(&holger, &holger, None));
        for (asked, to) in [
            ("/private?renew=acme", "/signup?plan=private&renew=acme"),
            ("/private?renew=Acme&x=1&company=acme&renew=bolt", "/signup?plan=private&company=acme&renew=acme"),
            ("/private?renew=../x", "/signup?plan=private"),
            ("/private", "/signup?plan=private"),
        ] {
            let res = app.clone().oneshot(http::Request::get(asked).body(Body::empty()).unwrap()).await.unwrap();
            assert_eq!(res.status(), StatusCode::PERMANENT_REDIRECT, "{asked}");
            assert_eq!(res.headers()[header::LOCATION], to, "{asked}");
        }
        assert_eq!(private_signup_address(Some("company=a1&renew=b2")), "/signup?plan=private&company=a1&renew=b2");
    }

    /// ★ Lane SU1: **the sign-up page is served at `/signup`** (never the console's fallback),
    /// and its one call, `GET /-/signup`, is holger's to answer (passed through, the person's
    /// bearer as it came) — Gunnar's two choices, each sold by the ONE box.
    #[cfg(feature = "metered")]
    /// ★ LOW-PRIV: **a console that sells nothing says at /signup that this Holger is private**
    /// and to ask its admin, never a sign-up page whose every door answers "Nothing is sold by
    /// this site." (a sold private Holger: the front merges no relay). A selling console keeps
    /// its sign-up page.
    #[cfg(feature = "metered")]
    #[tokio::test]
    async fn a_console_that_sells_nothing_says_this_holger_is_private_at_signup() {
        let (dir, _) = trunk_bundle();
        let get = |p: &str| http::Request::get(p).body(Body::empty()).unwrap();
        let up = || Upstreams::new("127.0.0.1:1", "127.0.0.1:1", None);
        let private = router_selling(Some(dir.path().to_path_buf()), up(), None, false);
        for page in ["/signup", "/signup?plan=private", "/signup?plan=account"] {
            let (status, body) = ask(private.clone(), get(page)).await;
            assert_eq!(status, StatusCode::NOT_FOUND, "{page}: {body}");
            assert!(body.contains(PRIVATE_SIGNUP), "{page}: {body}");
            assert!(!body.contains("/signup.js") && body != SPA, "{page} is no sign-up page: {body}");
        }
        let selling = router_selling(Some(dir.path().to_path_buf()), up(), None, true);
        assert_eq!(ask(selling, get("/signup")).await, (StatusCode::OK, std::fs::read_to_string(dir.path().join("signup.html")).unwrap()));
    }

    /// ★ LOW-PRIV: **an open console's /signup says this Holger is private** (still a 404 and no
    /// document: an open build has no sign-up).
    #[cfg(not(feature = "metered"))]
    #[tokio::test]
    async fn the_open_consoles_signup_says_this_holger_is_private() {
        let (dir, _) = trunk_bundle();
        let app = router(Some(dir.path().to_path_buf()), Upstreams::new("127.0.0.1:1", "127.0.0.1:1", None));
        let (status, body) = ask(app, http::Request::get("/signup").body(Body::empty()).unwrap()).await;
        assert_eq!((status, body.as_str()), (StatusCode::NOT_FOUND, PRIVATE_SIGNUP));
    }

    #[cfg(feature = "metered")]
    #[tokio::test]
    async fn the_signup_page_is_served_and_its_call_is_holgers() {
        let dir = tempfile::tempdir().unwrap();
        std::fs::write(dir.path().join("index.html"), "console").unwrap();
        std::fs::write(dir.path().join("login.html"), "passkey page").unwrap();
        std::fs::write(dir.path().join("signup.html"), "sign-up page").unwrap();
        let holger = echo().await;
        let app = router(Some(dir.path().to_path_buf()), Upstreams::new(&holger, &holger, None));
        let get = |p: &str| http::Request::get(p).body(Body::empty()).unwrap();
        assert_eq!(ask(app.clone(), get("/signup")).await.1, "sign-up page");
        assert_eq!(ask(app.clone(), get("/signup?plan=private")).await.1, "sign-up page");
        let req = http::Request::get("/-/signup").header("authorization", "Bearer person").body(Body::empty()).unwrap();
        let (status, said) = ask(app, req).await;
        assert_eq!(status, StatusCode::OK);
        assert!(said.starts_with("GET /-/signup auth=Bearer person"), "{said}");
    }

    /// ★ A12 (owner rule 2026-10-23): **the sign-up page is Gunnar's, screen for screen**, in
    /// Holger's look: the same choices, plan list, fields, picture, − / +, price, button words
    /// and states, each step a call to this site's own buy doors (the shared relay), never a
    /// shop page. Who is here is holger's `/-/signup`, the session this tab's bearer.
    #[test]
    fn the_signup_page_is_gunnars_screen_for_screen_on_the_sites_own_doors() {
        let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../assets");
        let page = std::fs::read_to_string(root.join("signup.html")).unwrap();
        assert!(page.contains("<script src=\"/signup.js\"></script>") && !page.contains("<script>"), "no inline script: the shared crate's, handed in by the front (A16)");
        for id in ["id=\"heading\"", "id=\"choice\"", "id=\"plans\"", "id=\"state\"", "id=\"account\"", "id=\"email\"", "id=\"company\"", "id=\"domain\"", "id=\"estate\"", "id=\"minus\"", "id=\"plus\"", "id=\"price\"", "id=\"go\"", "id=\"msg\""] {
            assert!(page.contains(id), "Gunnar's screen element {id}");
        }
        assert!(page.contains("Holger instance protected by VPN") && page.contains("url(\"/holger.webp\")"), "Holger's look");
        assert!(!page.contains(">Sign up</button>"), "never Sign up");
        // The script's words and doors are the shared crate's (monetize-site-buy `assets`),
        // checked where Holger's settings fill them in: holger-estate-front `buy` (A16).
        let index = std::fs::read_to_string(root.join("../index-metered.html")).unwrap();
        for f in ["signup.html", "welcome.html", "paid.html"] {
            assert!(index.contains(&format!("href=\"assets/{f}\"")), "trunk copies {f} into the metered bundle");
        }
    }

    /// ★ **The welcome page and the order page are Gunnar's**, over the site's doors: each page
    /// loads its script, the shared crate's (monetize-site-buy `assets`, handed in by the front:
    /// `/welcome.js`, `/paid.js`), with no inline script. The scripts' words and doors are
    /// checked where Holger's settings fill them in: holger-estate-front `buy` (A16, T16).
    #[test]
    fn the_welcome_and_order_pages_load_the_shared_scripts() {
        let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../assets");
        for (page, script) in [("welcome.html", "/welcome.js"), ("paid.html", "/paid.js")] {
            let html = std::fs::read_to_string(root.join(page)).unwrap();
            assert!(html.contains(&format!("<script src=\"{script}\"></script>")) && !html.contains("<script>"), "{page}: no inline script");
        }
        let paid = std::fs::read_to_string(root.join("paid.html")).unwrap();
        // The elements the shared script fills (monetize-site-buy `assets::PAID_IDS`).
        for id in ["cancelled", "steps", "headline", "detail", "change", "room", "progress", "pay", "fault", "foot"] {
            assert!(paid.contains(&format!("id=\"{id}\"")), "paid.html carries #{id}");
        }
    }

    /// ★ **Signed in right after the passkey is registered** (A10 row 10, Gunnar's flow 0.7): a
    /// sign-up's `/login?signup=<session>&user=<name>&next=` opens the register panel with the
    /// name filled and says the account is ready; holger's registration answers a session, the
    /// page keeps it as a login does and goes on to `next`.
    #[test]
    fn the_login_page_signs_the_buyer_in_once_the_passkey_is_registered() {
        let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../assets");
        let login = std::fs::read_to_string(root.join("login.js")).unwrap();
        assert!(login.contains("'Your account ' + named + ' is ready. Touch your security key to finish.'"), "{login}");
        assert!(login.contains("'Touch your security key'"), "the button says what to do: {login}");
        assert!(login.contains("'Your security key is registered. Signing you in …'"), "{login}");
        let after = &login[login.find("'/-/auth/passkey/register/finish'").expect("the register finish")..];
        assert!(after.contains("done.signed_in") && after.contains("location.href = nextDestination(CONSOLE_HOME)"), "the registration signs the person in (holger's cookie) and the page goes on: {after}");
        assert!(!login.contains("sessionStorage.setItem('holger-bearer-token'"), "no script keeps the session (A34-38)");
    }

    // ★ The first touch is the press's own (A29-20): held in the browser by what the person
    // sees, never by login.js's lines (RA2-17): browser-tests `first_touch.rs`.

    /// ★ **LOGIN-1 (owner 2026-10-27, "again"): `/login` IS the login.** It shows the passkey
    /// sign-in at once (one "Log in" press starts the passkey prompt) and NO sign-up chooser: no
    /// "Sign up" link or button, no `/signup` address, no ask of the sign-up door. Sign-up is its
    /// own page, reached from the front page only. It came back with A11 fix 2 (18689f0,
    /// 2026-10-23), which put "Sign up account | Log in" on this page; this test holds it out.
    #[test]
    fn the_login_page_is_the_login_and_offers_no_sign_up() {
        let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../assets");
        let page = std::fs::read_to_string(root.join("login.html")).unwrap();
        let js = std::fs::read_to_string(root.join("login.js")).unwrap();
        assert!(page.contains("<button id=\"login\" class=\"primary\">Log in</button>"), "the passkey sign-in, one press");
        for (name, text) in [("login.html", &page), ("login.js", &js)] {
            let lower = text.to_lowercase();
            assert!(!lower.contains("sign up"), "{name} offers a sign-up on /login");
            assert!(!text.contains("signup-link") && !text.contains("href=\"/signup"), "{name} links the sign-up");
        }
        assert!(!js.contains("monetize.ignalina.dk"), "the shop is holger's word, never the page's");
    }

    /// ★ **HOL-LOGIN (owner 2026-10-27, holger.rs): the newcomer's line follows what the root
    /// sells.** A root that sells accounts (`/-/signup` answers an `account_url`) points a
    /// newcomer to the front page in Gunnar's words; a private instance (nothing sold) keeps the
    /// invite sentence. The line ships hidden and empty, so neither is said before holger answers,
    /// and the no-passkey refusal says the same one of the two. /login still draws no sign-up.
    #[test]
    fn the_login_pages_newcomer_line_follows_what_the_root_sells() {
        let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../assets");
        let page = std::fs::read_to_string(root.join("login.html")).unwrap();
        let js = std::fs::read_to_string(root.join("login.js")).unwrap();
        assert!(page.contains("<p class=\"offer\" id=\"invite-offer\" hidden></p>"), "the line ships hidden and empty: {page}");
        assert!(!page.contains("New here?"), "no newcomer words baked into the page");
        for words in [
            "const NEWCOMER_FRONT = 'New here? Make an account from the ';",
            "const NEWCOMER_INVITE = 'New here? Ask whoever runs this Holger for an invite link.';",
            "fetch('/-/signup', { credentials: 'omit' })",
            "links && links.account_url",
            "front.setAttribute('href', '/')",
        ] {
            assert!(js.contains(words), "missing {words}");
        }
        let refusal = &js[js.find("function noPasskeyHere(").expect("the no-passkey words")..];
        let refusal = &refusal[..refusal.find("\n}").unwrap()];
        assert!(refusal.contains("ACCOUNTS_HERE ? 'New here? Make an account from the front page.' : NEWCOMER_INVITE"), "{refusal}");
        // The open (OSS) bundle carries /login too, so its words stay money-free
        // (tests/open_bundle_speaks_no_money.rs): the line is about making an account.
        for money in ["sell", "sold", "SELLS", "SOLD"] {
            assert!(!js.contains(money) && !page.contains(money), "/login speaks money ({money})");
        }
    }

    /// ★ **The first-login and sign-up panels speak Gunnar's words** (A16, gunnar-ui
    /// `auth/login.html`): an invite's or a sign-up's panel hides "Log in", says "Register your
    /// security key" / "Touch your security key" and "Your account X is ready…" (a site's first
    /// admin is welcomed instead); a device that already holds the passkey is told to sign in
    /// with it; a spent invite closes the panel with the hint; a key registered without a
    /// session says "Sign in with it."; nobody is told to ask "an administrator".
    #[test]
    fn the_login_page_speaks_gunnars_first_login_words() {
        let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../assets");
        // The shared helpers (A34-51) are part of what the page says.
        let js = std::fs::read_to_string(root.join("login.js")).unwrap() + nornir_identity_web::login_js::HELPERS;
        for words in [
            "'Welcome — register your security key for ' + who + ' on ' + location.hostname + '.'",
            "'This device already holds your passkey for this site. Sign in with it.'",
            "'This invite has already been used. If you registered, sign in.'",
            "const ORPHAN_HINT = 'Sign in with your other passkey, or ask for a new invite.';",
            "const REGISTERED_SIGN_IN = 'Your security key is registered. Sign in with it.';",
            "function inviteDone(",
        ] {
            assert!(js.contains(words), "missing {words}");
        }
        assert!(!js.contains("you can log in now") && !js.contains("Ask an administrator for an invite link") && !js.contains("'Register your passkey'"), "{js}");
        let panel = &js[js.find("function registerPanel(").expect("one panel for invite and sign-up")..];
        assert!(panel.contains("document.getElementById('login').hidden = true;"), "Log in steps aside always: {panel}");
        assert!(panel.contains("'Register your security key'") && panel.contains("'Touch your security key'"), "{panel}");
        let page = std::fs::read_to_string(root.join("login.html")).unwrap();
        assert!(!page.contains("An administrator mints you a single-use invite link."), "the invite line is Gunnar's: {page}");
    }

    /// ★ A16: **an instance is managed on the sign-up page, as Gunnar's is** — Renew <co>, Stop
    /// renewing and Terminate <co> (the name typed back, no backup) over the site's shared doors,
    /// Gunnar's script word for word; Holger's own renewal page is gone.
    #[test]
    fn an_instance_is_managed_on_the_sign_up_page_over_the_shared_doors() {
        let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../assets");
        assert!(!root.join("renew.html").exists() && !root.join("renew.js").exists(), "no second renewal page");
        for page in ["../index.html", "../index-metered.html"] {
            let index = std::fs::read_to_string(root.join(page)).unwrap();
            assert!(!index.contains("renew.html") && !index.contains("renew.js"), "trunk copies no renewal page ({page})");
        }
        // Lane P1, Path B: the login page redeems a paid sign-up with the vendor's session, as
        // holger's door takes it (`signup`), never with a bearer beside it.
        let login = std::fs::read_to_string(root.join("login.js")).unwrap();
        assert!(login.contains("params.get('signup') || params.get('session')") && login.contains("ask.signup = SIGNUP") && login.contains("!invite && !SIGNUP && token"), "the sign-up redemption");
    
    }

    /// ★ **A34-51: the login helpers are edda's, once.** `login.js` defines none of
    /// `safeLocalPath`, `nextDestination`, `inviteDone`, `ORPHAN_HINT`, `INVITE_SPENT`;
    /// `login.html` loads `/login-helpers.js` before it, and the console serves edda's script
    /// there as JavaScript.
    #[tokio::test]
    async fn the_login_page_takes_the_shared_helpers() {
        use nornir_identity_web::login_js::{defines_a_helper, HELPERS, PATH};
        let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../assets");
        let js = std::fs::read_to_string(root.join("login.js")).unwrap();
        assert_eq!(defines_a_helper(&js), None, "login.js carries a copy of a shared helper");
        assert!(js.contains("function onInviteDone() { LINK_DONE = true; }"));
        let page = std::fs::read_to_string(root.join("login.html")).unwrap();
        let (helpers, own) = (page.find("<script src=\"/login-helpers.js\"></script>"), page.find("<script src=\"/login.js\"></script>"));
        assert!(helpers.is_some() && helpers < own, "the helpers load first: {page}");
        let dir = tempfile::tempdir().unwrap();
        std::fs::write(dir.path().join("index.html"), "<!doctype html><title>x</title>").unwrap();
        let app = bundle(dir.path(), None, false);
        let res = tower::ServiceExt::oneshot(app, Request::builder().uri(PATH).body(axum::body::Body::empty()).unwrap()).await.unwrap();
        assert_eq!(res.status(), StatusCode::OK);
        assert!(res.headers()[header::CONTENT_TYPE].to_str().unwrap().starts_with("text/javascript"));
        let body = http_body_util::BodyExt::collect(res.into_body()).await.unwrap().to_bytes();
        assert_eq!(&body[..], HELPERS.as_bytes());
    }
}