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

nordisk/monetize

crates/monetize-server/src/signup_pull.rs

//! **A pulled product's signup** (5.4, G1 rows #7–#9): the account a paid signup buys is made
//! by the estate's main from a signed `add_principal` order it pulls; monetize never dials it.
//!
//! `paid` queues the account once per signup and answers `Unavailable` (the signup stays
//! PENDING; the return page's poll asks again) until the main acks it. An `ok` ack is the
//! account: only then is the plan's signed entitlement queued, so a fact never lands on a
//! name that turned out to be somebody else's. A `taken…` ack is `NameTaken` (the buyer
//! renames against the money already moved); any other refusal is final. The name check
//! answers from the main's newest report and the queued signups, never by asking the box.

use std::sync::Arc;

use monetize_guardian::pulled::{system_clock, GuardianSlot, Lane, Principal};
use monetize_pay::signup::username_refusal;
use monetize_pay::{NameVerdict, PaidSignup, SignupError, SignupOutcome, SignupSink};
use monetize_product::pull::AddPrincipal;

use crate::config::Config;
use crate::signup::{Plans, Sink};

/// `product`'s signup sink when `[products.<product>] instance` names its estate; `None`
/// otherwise (the product's own sink, linked or guest, serves it). The plan's fact is signed
/// with the PRODUCT's key from `keys` (G8) — the key the estate's boxes verify — never the root.
pub fn pulled_sink(cfg: &Config, product: &str, slot: &GuardianSlot, plans: &Plans, keys: Arc<monetize::Keyring>) -> Option<Sink> {
    let instance = cfg.products.get(product)?.instance.clone()?;
    Some(Arc::new(PulledSink { lane: Lane::new(product, instance, slot.clone(), system_clock()), plans: plans.clone(), policy: cfg.policy, keys }))
}

/// How `paid`'s `Unavailable` begins when the account is QUEUED for the estate's main (not a
/// sink that is down): the signup is pending, and asking again later finishes it.
pub const QUEUED: &str = "queued: ";

/// The signup sink of a product whose estate calls monetize.
pub struct PulledSink {
    lane: Lane,
    plans: Plans,
    policy: monetize::Policy,
    keys: Arc<monetize::Keyring>,
}

impl PulledSink {
    /// **The lane of the estate the account is made on** (ONE monetize, several sites of one
    /// product, owner 2026-10-11): the product's own estate for `None` (or its own name); for
    /// another estate of the product the fleet knows, that estate's lane and its name — the
    /// tenant monetize keys is then `@<estate>/<name>`. An estate the fleet does not know for
    /// this product is refused by name: its account is made nowhere, never on the home.
    fn lane_on(&self, estate: Option<&str>) -> Result<(Lane, Option<String>), SignupError> {
        match estate.map(str::trim).filter(|e| !e.is_empty() && *e != self.lane.instance()) {
            None => Ok((self.lane.clone(), None)),
            Some(e) => match self.lane.knows(e) {
                Ok(true) => Ok((self.lane.on(e), Some(e.to_owned()))),
                Ok(false) => Err(SignupError::Refused(format!("{e} is no estate of this product on the fleet: no main there makes its accounts, and none is made on another estate"))),
                Err(why) => Err(SignupError::Unavailable(why)),
            },
        }
    }
}

impl SignupSink for PulledSink {
    fn paid(&self, s: &PaidSignup) -> Result<SignupOutcome, SignupError> {
        self.paid_on(s, None)
    }

    /// The account is made by the main of the estate the signup came from (A9-A13 on THAT
    /// estate): its `add_principal`, then, once acked, the plan's entitlement — signed for
    /// `@<estate>/<name>`, which the lane hands that estate's main as its own plain name.
    fn paid_on(&self, s: &PaidSignup, estate: Option<&str>) -> Result<SignupOutcome, SignupError> {
        if let Some(why) = username_refusal(&s.username) {
            return Err(SignupError::NameTaken(why));
        }
        let (lane, away) = self.lane_on(estate)?;
        // The tenant monetize keys: the buyer's name, on its estate when it is not the home.
        let tenant = match &away {
            Some(e) => PaidSignup { username: format!("@{e}/{}", s.username), ..s.clone() },
            None => s.clone(),
        };
        // The grant first: a plan this deployment does not sell is refused before any account.
        let fact = crate::signup::signup_fact(&self.plans.snapshot(), &self.policy, self.keys.product(&s.product), &tenant, self.lane.now(), &self.plans.cap_meter_of(&s.product)).map_err(SignupError::Refused)?;
        let ap = AddPrincipal { username: s.username.clone(), kind: "person".into(), signup: format!("{}:{}", s.vendor, s.session) };
        let instance = lane.instance();
        match lane.add_principal(&ap).map_err(SignupError::Unavailable)? {
            Principal::Queued => Err(SignupError::Unavailable(format!("{QUEUED}{instance}'s main creates {:?} when it next calls monetize", s.username))),
            Principal::Created(namespace) => {
                // Routed by the fact's tenant: `@<estate>/…` to that estate's main.
                self.lane.queue_entitlement(&fact).map_err(SignupError::Unavailable)?;
                let namespace = match &away {
                    Some(e) => format!("@{e}/{namespace}"),
                    None => namespace,
                };
                Ok(SignupOutcome::Created { namespace, username: s.username.clone() })
            }
            Principal::Taken(why) => Err(SignupError::NameTaken(format!(
                "username {:?} is taken on {instance}; choose another — the money is already at the vendor and nothing was refunded automatically ({why})",
                s.username
            ))),
            Principal::Refused(why) => Err(SignupError::Refused(format!("{instance}'s main refused the account {:?}: {why}", s.username))),
        }
    }

    fn name_verdict(&self, username: &str) -> Result<NameVerdict, SignupError> {
        self.name_verdict_on("", username, None)
    }

    /// Free or taken on the estate the account would be made on.
    fn name_verdict_on(&self, product: &str, username: &str, estate: Option<&str>) -> Result<NameVerdict, SignupError> {
        let _ = product;
        if let Some(why) = username_refusal(username) {
            return Ok(NameVerdict::Taken(why));
        }
        let (lane, _) = self.lane_on(estate)?;
        match lane.name_taken(username) {
            Ok(true) => Ok(NameVerdict::Taken(format!("username {username:?} is taken; choose another"))),
            Ok(false) => Ok(NameVerdict::Free),
            Err(why) => Err(SignupError::Unavailable(why)),
        }
    }
}