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)),
}
}
}