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

nordisk/monetize

crates/monetize-cloud/src/server/mod.rs

//! **`server` — the iron this box already has.** The free cloud.
//!
//! The trait and this implementation live in monetize itself; the ones that
//! need a paid account live in the private implementation repository. That is
//! what lets the server and the console be run open source by people sharing
//! their servers for free.
//!
//! A self-hoster has one box, and its disk is already paid for. This provider
//! sells THAT: `Change::Ensure` answers [`Ensured::OnHand`] when the box
//! can cover the disk an order needs and [`CloudError::Refused`] when it cannot.
//! **It never answers `Provisioned`, because it never buys anything** — there is
//! no account, no credential, no socket, and no code path that could create a
//! resource. It is the operator's declaration of what the box gives away, made
//! into a ledger that refuses.
//!
//! # Two numbers, and why both
//!
//! * **`pool_gib` — the DECLARED allocation.** What the operator gives tenants
//!   out of this box, in GiB, written in `[clouds.server]`. Sold capacity is
//!   folded from the grants ([`pool::LedgerData`], the same arithmetic `upcloud`
//!   and `sandbox` keep), so `free = pool_gib − Σ granted` is a number the
//!   state file can be rebuilt to from the grants alone (rule 3).
//!   **The pool follows the set** (L1, 2026-10-05): the declaration is kept
//!   with the set size it was made against, and every GiB the set has grown
//!   by since — the grow batcher's purchases — is added to it, re-measured at
//!   every ask. Before, the pool was `pool_gib` for ever and a grown box
//!   refused the room it had bought.
//! * **the PRODUCT's measurement — the wall.** Before a grant, the named
//!   product is asked [`Product::servable`] — gunnar's `Admin.Capacity` — and if
//!   it says the store cannot hold what is asked, the grant is refused whatever
//!   the declared pool says. The product counts; monetize decides (rule 5).
//!
//! Why the measurement is the wall and not the pool: `servable_bytes` is *bytes
//! beyond what tenants already hold*. Folding sold GiB against it would subtract
//! every byte a sold tenant has since written TWICE — once because the product
//! no longer counts it as servable, once because the grant is still sold — and a
//! 300 GiB box would refuse its 151st GiB. An allocation must be a number that
//! does not move when a tenant writes; the operator's declaration is that number,
//! and the product's live answer is what stops it from being a lie about the
//! disk. A product that answers [`Servable::Unmeasured`] is *a measurement that
//! did not happen*, not a full disk: the declared pool decides alone and the
//! grant records that the product could not be asked.
//!
//! # Ceilings on what no tenant owns
//!
//! `[clouds.server] ceilings = { cpu_millicores = 4000, ram_bytes = … }` is the
//! DECLARED limit on the product-scope meters — what the box has of a thing a
//! tenant does not own. An order that would raise such a cap past the number is
//! refused by name at quote time (`RoomLeft::ceilings`). Declared because
//! measured is not here yet: when the product's `Admin.Capacity` reports cores
//! and RAM through [`Servable`], the measurement walls the declaration exactly as
//! it does for disk today.
//!
//! # What it is, on the console
//!
//! `Listing::Account` lists ONE thing: the product's own data set,
//! `kind = dataset`, sized from the product's `total_bytes` (or the declared
//! pool when unmeasured), `provenance = server`, attached to the product — so
//! the panel shows the box itself under `IRON · server` and never an orphan.
//! `Priced::Resource` is zero: your own iron has no per-order COST. What a
//! customer PAYS for it is the deployment's price list, which for the
//! open-source deployment is absent or zero; the transaction then runs at price
//! 0 with no payment vendor at all — see `monetize::transaction`'s free path.
//!
//! # State is persisted after every grant
//!
//! To `MONETIZE_SERVER_CLOUD_STATE`-shaped JSON (the registry defaults it to
//! `<data_dir>/server-pool.json`), rewritten atomically. `None` keeps it in
//! memory, which is right for a test and wrong for a deployment — a restart that
//! forgot its grants would sell the same gigabyte twice.

use crate::factory::{CloudBuild, CloudFactory};
use crate::pool::{self, Grant, LedgerData};
use crate::cloud::{Boot, Booted, Catalog, Change, Changed, Cloud, CloudManifest, Give, Listed, Listing, Priced, RoomLeft};
use crate::grow::Journal;
use crate::lay::{EstatePlan, LaidEstate};
use crate::{
    fingerprint, CloudError, Ensured, Inventory, Owned, OwnedKind, Price, Reclaimed, Resource, ResourceId, ResourceStatus, Zone,
};
use monetize_pay::Currency;
use monetize_product::{bytes, Product, Servable};
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};

pub const PROVIDER: &str = "server";

/// Environment names this provider reads: **none**. There is no credential
/// because there is no account; the state path is the registry's to default.
/// The constant exists so the registry treats every cloud alike.
pub const CONFIG_ENVS: &[&str] = &[];

/// How a server links this box's iron: `[clouds.server] enabled = true,
/// pool_gib = N`, optionally `product = "<id>"`, `zone`, `currency`,
/// `ceilings = { cpu_millicores = …, ram_bytes = … }`. No `cap` — it cannot
/// spend (`spends: false`), and `cap::CannotSpend` holds it to that.
pub const FACTORY: CloudFactory = CloudFactory { name: PROVIDER, config_envs: CONFIG_ENVS, spends: false, build };

/// **How far a product's data set can still GROW, in bytes — answered by whoever
/// drives its growth** (g-dk-market / g-currencies, release-3 mock 2026-09-29).
///
/// This box NEVER buys, and that stays true: the room a reach stands for is
/// bought by the product's grow batcher (monetize-server `grow`, through the
/// capped cloud that holds the set's volumes), not by this provider. Without it
/// `default_cloud = "server"` sold only the box's CURRENT room — 37 GiB declared
/// on a 4×10 GiB set the batcher can enlarge — so after a fill every order was
/// withheld while the set could grow to cover it. A reach is added to BOTH
/// walls ([`Server::covers`]): the declared pool and the product's measurement.
///
/// A shared, late-bound slot: the server's state (which knows the growth, the
/// ceiling and what it still buys) exists only AFTER the clouds are built, so
/// the registry hands this box an empty slot and fills it once it can answer.
/// Empty — or an answer of `None` — is no reach: the old wall, unchanged.
#[derive(Clone, Default)]
pub struct GrowReach(Arc<std::sync::RwLock<Option<ReachFn>>>, Arc<std::sync::RwLock<Option<QueueFn>>>);

/// `product id → bytes the set can still grow by`, `None` when that cannot be
/// said (no growth configured, a sealed box, no ceiling to buy under).
pub type ReachFn = Arc<dyn Fn(&str) -> Option<u64> + Send + Sync>;

/// `product id → whether a grant past both walls is QUEUED` for the set's later
/// growth (round 5.3, owner 2026-10-08: "the customer must not know about a
/// cap! it must be queued and resized later"): `true` where a grow batcher
/// resizes the set once the room or the spend cap allows.
pub type QueueFn = Arc<dyn Fn(&str) -> bool + Send + Sync>;

impl GrowReach {
    /// Who answers from now on (the last setter wins; the slot is shared).
    pub fn answer_with(&self, f: ReachFn) {
        *self.0.write().unwrap_or_else(|e| e.into_inner()) = Some(f);
    }

    /// Who answers [`GrowReach::queues`] from now on.
    pub fn queue_with(&self, f: QueueFn) {
        *self.1.write().unwrap_or_else(|e| e.into_inner()) = Some(f);
    }

    /// The reach of `product`'s set in bytes; 0 when nobody answers.
    pub fn bytes(&self, product: &str) -> u64 {
        let f = self.0.read().unwrap_or_else(|e| e.into_inner()).clone();
        f.and_then(|f| f(product)).unwrap_or(0)
    }

    /// **Is a grant past the walls queued for `product`'s set's growth?**
    /// `false` when nobody answers: the old wall, which refuses.
    pub fn queues(&self, product: &str) -> bool {
        let f = self.1.read().unwrap_or_else(|e| e.into_inner()).clone();
        f.is_some_and(|f| f(product))
    }
}

impl std::fmt::Debug for GrowReach {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(if self.0.read().map(|g| g.is_some()).unwrap_or(false) { "GrowReach(answered)" } else { "GrowReach(none)" })
    }
}

/// The one reader of the block's keys. `pool_gib` is required; the product is
/// the one named or the only one linked; grants live at
/// `<data_dir>/server-pool.json`.
fn build(ctx: &CloudBuild<'_>) -> Result<Box<dyn Cloud>, String> {
    build_with(ctx, GrowReach::default())
}

/// [`FACTORY`]'s build with the registry's [`GrowReach`] slot — what
/// monetize-server calls, so this box counts the set its batcher can grow.
pub fn build_with(ctx: &CloudBuild<'_>, reach: GrowReach) -> Result<Box<dyn Cloud>, String> {
    let s = ctx.settings;
    let pool_gib = s.get::<u64>("pool_gib").map_err(|e| ctx.err(e))?.ok_or_else(|| {
        ctx.err(
            "is enabled but declares no `pool_gib`. Write down how many GiB of this box tenants may be given, e.g. \
             `pool_gib = 100`; 0 is a valid answer (every sale is refused; free plans without resources still work). \
             The product's own measurement is the wall on top of it, never a substitute for it — see monetize_cloud::server",
        )
    })?;
    let named = s.get::<String>("product").map_err(|e| ctx.err(e))?;
    let product = match (&named, ctx.products) {
        (Some(id), all) => all
            .iter()
            .find(|p| p.facts().id == id)
            .cloned()
            .ok_or_else(|| ctx.err(format!("product = {id:?}, but no [products.{id}] is linked into this server")))?,
        (None, [only]) => only.clone(),
        (None, []) => return Err(ctx.err("needs a product to measure through, and none is linked: add a [products.<id>]")),
        (None, many) => {
            return Err(ctx.err(format!(
                "must name which product's disk this box is (`product = \"<product>\"`); {} are linked: {:?}",
                many.len(),
                many.iter().map(|p| p.facts().id).collect::<Vec<_>>()
            )))
        }
    };
    let mut sc = ServerConfig::new(product, pool_gib);
    sc.reach = reach;
    sc.state_path = Some(ctx.data_dir.join("server-pool.json"));
    if let Some(v) = s.get::<String>("zone").map_err(|e| ctx.err(e))? {
        sc.zone = Zone(v);
    }
    if let Some(v) = s.get::<Currency>("currency").map_err(|e| ctx.err(e))? {
        sc.currency = v;
    }
    if let Some(v) = s.get::<std::collections::BTreeMap<String, u64>>("ceilings").map_err(|e| ctx.err(e))? {
        sc.ceilings = v;
    }
    let inner = Server::new(sc).map_err(|e| ctx.err(e))?;
    if let Some(refusal) = pool_past_the_disk(&inner) {
        return Err(ctx.err(refusal));
    }
    eprintln!(
        "monetize-server: [clouds.server] is THIS BOX — {} GiB declared for tenants, walled by {}'s own measurement, never buys (state {})",
        inner.pool_gib(),
        inner.product_id(),
        inner.state_path().map(|p| p.display().to_string()).unwrap_or_default()
    );
    Ok(Box::new(inner))
}

/// **A declared pool bigger than the disk, refused BY NAME at start.**
///
/// The live front declared `pool_gib = 100` on a box whose product measures
/// 37.8 GiB (2026-09-18). Nothing broke, because `covers` overlays the
/// measurement on every grant and the measurement wins — but the config was
/// claiming nearly three times the disk, and every number derived from the
/// declaration alone (the free-GiB arithmetic, `inventory`'s fallback size, an
/// operator reading the file) was that big a lie. A declaration that the
/// product can CONTRADICT is a wall; one that it can contradict *while nobody
/// is told* is a trap, and the only moment anybody would have found out is when
/// the 38th GiB was sold.
///
/// So it is refused here, at start, before the process serves anything: the
/// operator edits one number in a file they were already wrong about. The two
/// silent alternatives were both worse — deriving the pool from the measurement
/// would make an operator's declaration mean nothing and would change the
/// number under them whenever a box was resized, and a warning would join the
/// other lines nobody reads.
///
/// **An unmeasured product is a WARNING and not a refusal.** A product that is
/// down at start, or one that predates `Admin.Capacity`, has said nothing about
/// the disk; refusing on that would take a deployment down because its
/// appliance was rebooting, and "the product did not answer" is not "the
/// operator is wrong". It is said out loud, naming the product's own reason, so
/// the declaration is known to be standing alone.
fn pool_past_the_disk(server: &Server) -> Option<String> {
    let declared = server.pool_gib().saturating_mul(GIB);
    match server.cfg.product.servable() {
        // `total_bytes` is 0 from a product that measures what is LEFT but not
        // what there is in total — a measurement that did not happen, and not a
        // disk of no size.
        Servable::Measured { total_bytes, .. } if total_bytes > 0 && declared > total_bytes => Some(format!(
            "declares `pool_gib = {}` ({}) for tenants, and {} measures a store of {} in total — {} of it does not exist. \
             The product's measurement already walls every grant, so nothing was ever handed out that the disk could not hold; \
             what the declaration does is put a number nobody can honour into the free-GiB arithmetic and in front of whoever \
             reads this file. Write down what the box really has, or give it the disk",
            server.pool_gib(),
            bytes(declared),
            server.product_id(),
            bytes(total_bytes),
            bytes(declared - total_bytes)
        )),
        Servable::Measured { .. } => None,
        Servable::Unmeasured(why) => {
            eprintln!(
                "monetize-server: WARNING [clouds.server] declares {} GiB for tenants and {} did not measure its store, so nothing \
                 has checked that the box has them: {why}",
                server.pool_gib(),
                server.product_id()
            );
            None
        }
    }
}

const GIB: u64 = 1 << 30;

#[derive(Clone)]
pub struct ServerConfig {
    /// The product whose store this box's disk IS. Asked before every grant and
    /// listed by `inventory`. There is no `server` without one: a pool with
    /// nobody to measure it is a number nothing can contradict.
    pub product: Arc<dyn Product>,
    /// The product's id, read once from its facts when the box is configured.
    pub product_id: &'static str,
    /// GiB this box gives to tenants, declared by the operator. `0` is a valid
    /// answer — it refuses every sale, the shape a box that only serves free
    /// plans without resources wants.
    pub pool_gib: u64,
    /// Where the grants are remembered. `None` is in-memory: right for a test,
    /// wrong for a deployment (see the module doc).
    pub state_path: Option<PathBuf>,
    /// What the zero price and the inventory are a number OF. Your own iron has
    /// no price, but a `Price` without a currency is ambiguous, so it is named.
    pub currency: Currency,
    /// The one zone: the box. `this-box` unless the operator names it.
    pub zone: Zone,
    /// **Declared ceilings on product-scope meters**, by the product's own
    /// meter name (`cpu_millicores`, `ram_bytes`): what this box HAS of a thing
    /// no tenant owns. An order past one is refused by name at quote time.
    /// Empty declares nothing, and then nothing is refused on their account —
    /// the product's own `can_absorb` is still asked.
    pub ceilings: std::collections::BTreeMap<String, u64>,
    /// **How far the set can still grow** ([`GrowReach`]): added to both walls.
    /// Empty by default — the box's current room alone.
    pub reach: GrowReach,
}

impl ServerConfig {
    pub fn new(product: Arc<dyn Product>, pool_gib: u64) -> ServerConfig {
        let product_id = product.facts().id;
        ServerConfig { product, product_id, pool_gib, state_path: None, currency: Currency::Eur, zone: Zone("this-box".to_string()), ceilings: Default::default(), reach: GrowReach::default() }
    }
}

impl std::fmt::Debug for ServerConfig {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("ServerConfig")
            .field("product", &self.product_id)
            .field("pool_gib", &self.pool_gib)
            .field("state_path", &self.state_path)
            .field("currency", &self.currency)
            .field("zone", &self.zone)
            .field("ceilings", &self.ceilings)
            .field("reach", &self.reach)
            .finish()
    }
}

#[derive(Default, serde::Serialize, serde::Deserialize)]
struct Stored {
    version: u32,
    /// The grants and the sold count, shared with `upcloud` and `sandbox` by
    /// construction. Every grant here is `OnHand`; the type has no other arm
    /// this provider can produce.
    pool: LedgerData,
    /// **What the declaration was made against**: the `pool_gib` in force and
    /// the set's measured size when it was first seen. The pool follows the
    /// set from there — see [`Server::grown_gib`]. Absent in a state file
    /// written before it (2026-10-05): taken at the next measurement.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    declared: Option<Declared>,
}

/// The set a `pool_gib` was declared against.
#[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
struct Declared {
    pool_gib: u64,
    set_bytes: u64,
}

/// The box's own disk, sold from a declared pool and walled by the product's
/// own measurement. Never buys.
pub struct Server {
    cfg: ServerConfig,
    zones: Vec<Zone>,
    /// Held for the whole of `ensure`: the pool arithmetic must not interleave.
    state: Mutex<Stored>,
}

impl std::fmt::Debug for Server {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Server")
            .field("product", &self.cfg.product_id)
            .field("pool_gib", &self.cfg.pool_gib)
            .field("sold_gib", &self.sold_gib())
            .finish_non_exhaustive()
    }
}

impl Server {
    /// Refuses an empty zone, and a state file that exists but does not parse:
    /// a provider that guessed over a corrupt grant list could sell the same
    /// gigabyte twice, which is the one thing a pool exists to prevent.
    pub fn new(cfg: ServerConfig) -> Result<Server, CloudError> {
        if cfg.zone.0.trim().is_empty() {
            return Err(CloudError::Refused("server cloud: zone is empty".into()));
        }
        let stored = match cfg.state_path.as_deref() {
            Some(path) => Self::load(path)?,
            None => Stored { version: 1, ..Default::default() },
        };
        let server = Server { zones: vec![cfg.zone.clone()], state: Mutex::new(stored), cfg };
        // The set this declaration is made against, measured NOW — at start,
        // so a growth before the first order is still counted as growth.
        {
            let mut st = server.state.lock().unwrap_or_else(|e| e.into_inner());
            server.grown_gib(&mut st, &server.cfg.product.servable())?;
        }
        Ok(server)
    }

    /// **What the set has grown by since `pool_gib` was declared**, whole GiB
    /// (L1, owner 2026-10-05: the pool was `pool_gib + reach − sold` for ever,
    /// read once at install, so every GiB the grow batcher had already bought
    /// was lost to it and test2's box refused room it had). The declaration
    /// is kept with the set size it was made against; a new `pool_gib` is a
    /// new declaration, made against the set as it is then. A product that
    /// measures no total adds nothing and records nothing.
    fn grown_gib(&self, st: &mut Stored, servable: &Servable) -> Result<u64, CloudError> {
        let total = match servable {
            Servable::Measured { total_bytes, .. } if *total_bytes > 0 => *total_bytes,
            _ => return Ok(0),
        };
        match st.declared {
            Some(d) if d.pool_gib == self.cfg.pool_gib => Ok(total.saturating_sub(d.set_bytes) / GIB),
            _ => {
                st.declared = Some(Declared { pool_gib: self.cfg.pool_gib, set_bytes: total });
                self.persist(st)?;
                Ok(0)
            }
        }
    }

    fn load(path: &Path) -> Result<Stored, CloudError> {
        match std::fs::read(path) {
            Ok(bytes) => serde_json::from_slice(&bytes).map_err(|e| {
                CloudError::Unavailable(format!(
                    "server cloud: state {} does not parse ({e}); refusing to start rather than forget what this box has granted",
                    path.display()
                ))
            }),
            Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(Stored { version: 1, ..Default::default() }),
            Err(e) => Err(CloudError::Unavailable(format!("server cloud: state {}: {e}", path.display()))),
        }
    }

    fn persist(&self, st: &Stored) -> Result<(), CloudError> {
        let Some(path) = self.cfg.state_path.as_deref() else { return Ok(()) };
        let bytes = serde_json::to_vec_pretty(st).map_err(|e| CloudError::Unavailable(format!("server cloud: state encode: {e}")))?;
        pool::write_atomically(path, &bytes, "server cloud state")
    }

    pub fn product_id(&self) -> &'static str {
        self.cfg.product_id
    }

    pub fn pool_gib(&self) -> u64 {
        self.cfg.pool_gib
    }

    pub fn state_path(&self) -> Option<&Path> {
        self.cfg.state_path.as_deref()
    }

    /// GiB granted out of the declared pool, folded from the grants: the sum
    /// over every server name a plan ever used, because the box has ONE disk
    /// however many names plans give it.
    pub fn sold_gib(&self) -> u64 {
        self.state.lock().unwrap_or_else(|e| e.into_inner()).pool.sold.values().sum()
    }

    /// `pool_gib − sold`, the declared side of the answer.
    pub fn free_gib(&self) -> u64 {
        self.cfg.pool_gib.saturating_sub(self.sold_gib())
    }

    /// Every grant, in key order. All `OnHand`, by construction.
    pub fn grants(&self) -> Vec<(String, Grant)> {
        self.state.lock().unwrap_or_else(|e| e.into_inner()).pool.grants.iter().map(|(k, g)| (k.clone(), g.clone())).collect()
    }

    /// The one id this provider knows: the product's data set.
    fn dataset_id(&self) -> ResourceId {
        ResourceId { provider: PROVIDER.into(), id: format!("dataset:{}", self.cfg.product_id) }
    }

    /// **Can this box cover `gib` more?** The declared side (pool minus
    /// granted), then the measured side (the product's own word on the disk,
    /// which the declaration cannot talk over; an unmeasured answer is not a
    /// wall). Answers the free GiB before the grant. ONE writer for `ensure`
    /// and `can_serve`, so a quote and the purchase it precedes cannot disagree.
    fn covers(&self, st: &mut Stored, reference: &str, gib: u64) -> Result<u64, CloudError> {
        let sold: u64 = st.pool.sold.values().sum();
        // The product's measurement, read ONCE per ask: the set as it is now
        // (what it has grown by since the declaration) and the wall below.
        let servable = self.cfg.product.servable();
        let grown_gib = self.grown_gib(st, &servable)?;
        let grown = if grown_gib > 0 { format!(" + {grown_gib} GiB the set has grown by since") } else { String::new() };
        // The set's growable reach (g-dk-market): what the grow batcher can
        // still add under its ceiling, on BOTH walls. Whole GiB only on the
        // declared side, so the pool never counts a fraction it cannot grant.
        let reach = self.cfg.reach.bytes(self.cfg.product_id);
        let reach_gib = reach / GIB;
        let grows = if reach_gib > 0 { format!(" + {reach_gib} GiB the set can still grow by under its ceiling") } else { String::new() };
        let free = self.cfg.pool_gib.saturating_add(grown_gib).saturating_add(reach_gib).saturating_sub(sold);
        // ★ **QUEUED, not refused, where the set grows later** (round 5.3,
        // owner 2026-10-08): the grant is recorded past both walls and the
        // shortfall waits for the grow batcher's resize — the batcher grows
        // the set for the queued purchases once the room or the spend cap
        // allows, and tells the operator what waits. Said here for the log.
        let short_of_disk = match &servable {
            Servable::Measured { servable_bytes, .. } => servable_bytes.saturating_add(reach) < gib.saturating_mul(GIB),
            Servable::Unmeasured(_) => false,
        };
        if (free < gib || short_of_disk) && self.cfg.reach.queues(self.cfg.product_id) {
            eprintln!(
                "server cloud: {gib} GiB for {reference} is past the room on hand ({free} GiB of the pool free{grown}{grows}): QUEUED for the set's resize — \
                 the grow batcher enlarges the set once the room or the spend cap allows"
            );
            return Ok(free);
        }
        if free < gib {
            return Err(CloudError::Refused(format!(
                "server cloud: this box cannot cover {gib} GiB for {reference}: {} GiB declared{grown}{grows}, {sold} GiB already granted, \
                 {free} GiB free — short {} GiB. `server` never buys; raise pool_gib only if the disk really has it, \
                 or serve this order from a cloud that does buy",
                self.cfg.pool_gib,
                gib - free
            )));
        }
        match servable {
            Servable::Measured { servable_bytes, .. } if servable_bytes.saturating_add(reach) < gib.saturating_mul(GIB) => Err(CloudError::Refused(format!(
                "server cloud: {} measured that it can serve only {} more{grows}, and {gib} GiB is asked for {reference} — short {}. \
                 The declared pool ({} GiB, {free} GiB unsold) is not what the disk has; the product counts, and it says no",
                self.cfg.product_id,
                bytes(servable_bytes),
                bytes(gib.saturating_mul(GIB) - servable_bytes.saturating_add(reach)),
                self.cfg.pool_gib
            ))),
            Servable::Measured { .. } | Servable::Unmeasured(_) => Ok(free),
        }
    }

    fn only_disks(resource: &Resource) -> Result<(&str, u64), CloudError> {
        match resource {
            Resource::Disk { server, gib } => Ok((server.0.as_str(), *gib)),
            other => Err(CloudError::Refused(format!(
                "server cloud: this box is the only box — `server` sells no servers and no plan changes, and {} is one. \
                 An order needing it needs a cloud that sells iron",
                fingerprint(other)
            ))),
        }
    }
}

/// The box's own iron, as its own: the wall it serves within, its one dataset row.
impl Server {


    /// Zero. Your own iron has no per-order price; what it costs to run the box
    /// is not a thing monetize measures.
    fn own_price(&self, resource: &Resource) -> Result<Price, CloudError> {
        Self::only_disks(resource)?;
        Ok(Price { minor_per_month: 0, currency: self.cfg.currency })
    }

    /// `OnHand`, or `Refused` naming the shortfall. Idempotent on
    /// `reference + fingerprint` like every provider: a retried attempt gets
    /// the grant it already has, never a second one.
    fn own_ensure(&self, reference: &str, resource: &Resource) -> Result<Ensured, CloudError> {
        if reference.is_empty() || reference.len() > 255 {
            return Err(CloudError::Refused("server cloud: reference must be 1–255 chars".into()));
        }
        let (server, gib) = Self::only_disks(resource)?;
        if gib == 0 {
            return Err(CloudError::Refused("server cloud: a disk of 0 GiB".into()));
        }
        let key = pool::Ledger::key(reference, &fingerprint(resource));
        let mut st = self.state.lock().unwrap_or_else(|e| e.into_inner());
        if let Some(Grant::OnHand { free_after, .. }) = st.pool.grant(&key) {
            return Ok(Ensured::OnHand { pool_free_after: *free_after });
        }
        let free = self.covers(&mut st, reference, gib)?;
        // A queued grant (past the walls) leaves nothing free.
        let free_after = free.saturating_sub(gib);
        st.pool.record(&key, Grant::OnHand { server: server.to_string(), gib, free_after });
        self.persist(&st)?;
        Ok(Ensured::OnHand { pool_free_after: free_after })
    }

    /// **The quote's question**: the disks in `delta` summed, against the same
    /// two walls `ensure` checks — and no grant. A retried reference is not
    /// special-cased here: a quote is asked before an order, never after.
    fn own_can_serve(&self, delta: &[Resource]) -> Result<(), String> {
        let mut gib = 0u64;
        for r in delta {
            gib = gib.saturating_add(Self::only_disks(r).map_err(|e| e.to_string())?.1);
        }
        if gib == 0 {
            return Ok(());
        }
        let mut st = self.state.lock().unwrap_or_else(|e| e.into_inner());
        self.covers(&mut st, "this order", gib).map(|_| ()).map_err(|e| e.to_string())
    }

    /// The pool's free GiB (declared + grown + growable − sold), never more
    /// than the product measured it can serve (+ the reach) — the two walls
    /// `covers` checks, as a size.
    fn own_room_left_bytes(&self) -> Option<u64> {
        let mut st = self.state.lock().unwrap_or_else(|e| e.into_inner());
        let sold: u64 = st.pool.sold.values().sum();
        let servable = self.cfg.product.servable();
        let grown_gib = self.grown_gib(&mut st, &servable).ok()?;
        let reach = self.cfg.reach.bytes(self.cfg.product_id);
        let free = self.cfg.pool_gib.saturating_add(grown_gib).saturating_add(reach / GIB).saturating_sub(sold).saturating_mul(GIB);
        Some(match servable {
            Servable::Measured { servable_bytes, .. } => free.min(servable_bytes.saturating_add(reach)),
            Servable::Unmeasured(_) => free,
        })
    }

    /// The declared limits on what no tenant owns. See [`ServerConfig::ceilings`].
    fn own_ceilings(&self) -> std::collections::BTreeMap<String, u64> {
        self.cfg.ceilings.clone()
    }

    /// A no-op, and `Ok` whatever the id: on-hand iron is not returned to
    /// anyone, because nobody was ever paid for it. The transaction never calls
    /// this for an `OnHand` grant anyway; a caller that does gets the truth,
    /// which is that there was nothing to give back.
    fn own_destroy(&self, _id: &ResourceId) -> Result<(), CloudError> {
        Ok(())
    }

    /// `Active` for the data set — the one thing this provider ever lists —
    /// and `UnknownResource` for anything else, never a silent `Gone`.
    fn own_status(&self, id: &ResourceId) -> Result<ResourceStatus, CloudError> {
        if *id == self.dataset_id() {
            Ok(ResourceStatus::Active)
        } else {
            Err(CloudError::UnknownResource(id.clone()))
        }
    }

    /// The box itself: one `dataset` row, sized by the product's own
    /// `total_bytes` (floored to GiB) when it measured, by the declared pool
    /// when it could not. `provenance = server`, attached to the product,
    /// `reference = None` because no order bought it. Nothing is foreign — there
    /// is no account for anything else to be in.
    fn own_inventory(&self) -> Result<Inventory, CloudError> {
        let gib = match self.cfg.product.servable() {
            Servable::Measured { total_bytes, .. } if total_bytes > 0 => total_bytes / GIB,
            _ => self.cfg.pool_gib,
        };
        let owned = vec![Owned {
            id: self.dataset_id(),
            kind: OwnedKind::Dataset,
            gib,
            zone: self.cfg.zone.0.clone(),
            attached_to: Some(self.cfg.product_id.to_string()),
            reference: None,
            monthly_minor: Some(0),
            currency: self.cfg.currency,
            provenance: PROVIDER.into(),
            resource: None,
            backup: None,
        }];
        Ok(Inventory { owned, foreign: 0, currency: self.cfg.currency })
    }
}

/// **The box's own iron, in the nine ops.** It never buys: an ensure is `OnHand` from the
/// declared pool, walled by the product's own measurement; its room is that wall. It lays,
/// changes, boots and grows nothing, and says so by name.
impl Cloud for Server {
    fn manifest(&self) -> CloudManifest {
        CloudManifest { provider: PROVIDER.into(), zones: self.zones.clone(), settings: CONFIG_ENVS.iter().map(|s| s.to_string()).collect(), grow_refused: Some(crate::grow::unsupported(PROVIDER).to_string()) }
    }
    fn catalog(&self, _zone: &Zone) -> Result<Catalog, CloudError> {
        Err(crate::lay::unsupported(PROVIDER))
    }
    fn price(&self, what: &Priced<'_>) -> Result<Price, CloudError> {
        match what {
            Priced::Resource(r) => self.own_price(r),
            Priced::Plan(_) => Err(crate::lay::unsupported(PROVIDER)),
            Priced::Grow(_) => Err(crate::grow::unsupported(PROVIDER)),
        }
    }
    fn lay(&self, _plan: &EstatePlan, _journal: &Journal) -> Result<LaidEstate, CloudError> {
        Err(crate::lay::unsupported(PROVIDER))
    }
    fn change(&self, change: &Change, _journal: &Journal) -> Result<Changed, CloudError> {
        match change {
            Change::Ensure { reference, resource, .. } => self.own_ensure(reference, resource).map(Changed::Ensured),
            Change::Grow { .. } | Change::Settle { .. } => Err(crate::grow::unsupported(PROVIDER)),
            // Nothing was laid here, so no medium was attached.
            Change::ForgetMedia { .. } => Ok(Changed::Forgotten(Vec::new())),
            Change::Retire { .. } => Err(crate::lay::unsupported_verb(PROVIDER, "retire_machine")),
            Change::Plan { .. } => Err(crate::lay::unsupported_verb(PROVIDER, "change_plan")),
            Change::LabelAdopted { .. } => Err(crate::lay::unsupported_verb(PROVIDER, "label_adopted")),
        }
    }
    fn boot(&self, boot: &Boot, _journal: &Journal) -> Result<Booted, CloudError> {
        let verb = match boot {
            Boot::Keep(_) => "keep_boot",
            Boot::Power { .. } => "set_power",
            Boot::Restore(_) => "restore_from_backup",
        };
        Err(crate::lay::unsupported_verb(PROVIDER, verb))
    }
    fn firewall(&self, _laid: &LaidEstate) -> Result<(), CloudError> {
        Err(crate::lay::unsupported_verb(PROVIDER, "set_estate_firewall"))
    }
    /// Nothing here was bought, so nothing is given back: the box's own data set is
    /// spared by kind.
    fn destroy(&self, what: &Give<'_>) -> Result<Reclaimed, CloudError> {
        match what {
            Give::Resource(id) => {
                self.own_destroy(id)?;
                Ok(Reclaimed { destroyed: vec![(*id).clone()], ..Reclaimed::default() })
            }
            Give::References(refs) => Ok(crate::release_for_by_rows(self.own_inventory()?, refs, |id| self.own_destroy(id))),
        }
    }
    fn inventory(&self, what: &Listing<'_>) -> Result<Listed, CloudError> {
        Ok(match what {
            Listing::Account => Listed::Account(self.own_inventory()?),
            Listing::Status(id) => Listed::Status(self.own_status(id)?),
            Listing::Under(refs) => Listed::Under(crate::cloud::under(self.own_inventory()?, refs)),
            Listing::Set(_) => return Err(crate::grow::unsupported(PROVIDER)),
            Listing::Admits(_) => Listed::Admitted,
            Listing::Room { delta, .. } => Listed::Room(RoomLeft { serve: self.own_can_serve(delta), left_bytes: self.own_room_left_bytes(), ceilings: self.own_ceilings() }),
        })
    }
}

#[cfg(test)]
mod tests;