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/estate_order/first_link.rs

//! **The operator sends a laid site's first sign-in link again, to a new address** (owner
//! 2026-10-22: holger-rs was ordered with a placeholder `--admin` address). A site's ready
//! mail carries its ONLY first sign-in link and goes ONCE ([`super::tell`], `run.told`) to the
//! order's address; nothing changed the address or sent it again.
//!
//! `monetize site first-link <site> --to <address>` (door `POST /site/first-link`, the
//! operator bearer alone):
//!
//! * the address is set on the run (`run.email`), and the `estate.ready` mail goes again to it
//!   with the link the custody holds ([`super::first_login_link`]); the history says "first
//!   sign-in link re-sent by the operator to <masked address>". The link and the invite are
//!   never logged;
//! * **the site's own first-admin door is asked first** ([`super::Installer::first_door`], at
//!   its public front, as the sale check asks it): the site holds the invite's hash and its
//!   lifetime from its install, so the custody's invite is the ONLY one it opens. A spent door
//!   (the first admin has signed in) is refused by name, `[site-first-link-spent]`. A door the
//!   invite no longer opens (its lifetime is over, or the site no longer takes it) is NOT
//!   refused any more (HOL-FIRST-ADMIN, test2.holger.rs 2026-10-30, where it left the first
//!   admin no way in): a new invite minted here would open nothing on the site, so its MAIN is
//!   asked, as after a hand-off (below);
//! * **the invite is the custody's, and only until the hand-off**: once the site took its
//!   material and the custody forgot it, the ONE box holds no invite, and minting one here would
//!   open nothing on the site: its MAIN mints one;
//! * **FIRST-LINK-1 (owner 2026-10-27): no `--to`** is the first admin's recorded address
//!   (`run.email`). The ONE box asks the site's MAIN to mint a new link, with the same signed
//!   `sign_in_again` order a sold estate's resend is ([`super::send_sign_in_again`], under the
//!   site's LABEL, its rate limit), and the main mails it. **HOL-FIRST-ADMIN: `--to`** is the
//!   address the main mails instead; it is the recorded one from the moment the order is queued
//!   (a refused ask changes nothing). `site status` says the first-admin door ([`door_of`]):
//!   open, spent, expired or handed off, with the main's newest answer, its address masked;
//! * a run that is not an operator site, a site that is not laid and ready, and a malformed
//!   address are refused, and nothing changes.

use super::{lifecycle, phase, run_terms, save, FirstDoor, SaleCheckAt, StartRefusal, Transition};
use crate::state::AppState;

/// The refusal tag of a spent first-admin door.
pub const SPENT: &str = "[site-first-link-spent]";
/// The refusal tag of a malformed address.
pub const ADDRESS: &str = "[site-first-link-address]";
/// The refusal tag of a run that is not a laid, ready operator site.
pub const NOT_READY: &str = "[site-first-link-not-ready]";

/// Pure: **is `to` one plain mail address** — `local@domain.tld`, no spaces, no list, no
/// display name, at most 254 characters.
pub fn address_ok(to: &str) -> bool {
    let to = to.trim();
    if to.is_empty()
        || to.len() > 254
        || to.chars().any(|c| {
            c.is_whitespace()
                || c.is_control()
                || matches!(
                    c,
                    ',' | ';' | '<' | '>' | '"' | '(' | ')' | '[' | ']' | '\\'
                )
        })
    {
        return false;
    }
    let Some((local, domain)) = to.split_once('@') else {
        return false;
    };
    if local.is_empty() || local.len() > 64 || domain.contains('@') {
        return false;
    }
    let labels: Vec<&str> = domain.split('.').collect();
    labels.len() >= 2
        && labels.iter().all(|l| {
            !l.is_empty()
                && !l.starts_with('-')
                && !l.ends_with('-')
                && l.chars().all(|c| c.is_ascii_alphanumeric() || c == '-')
        })
}

/// Pure: **an address as the history and the logs show it** — its first character, `***`,
/// and its domain (`o***@example.org`).
pub fn masked(to: &str) -> String {
    match to.trim().split_once('@') {
        Some((local, domain)) => format!(
            "{}***@{domain}",
            local.chars().next().map(String::from).unwrap_or_default()
        ),
        None => "***".into(),
    }
}

/// Pure: `text` with every mail address in it masked ([`masked`]) — a main's answer names the
/// address it mailed; the operator reads it masked.
pub fn masked_in(text: &str) -> String {
    let mut out = String::with_capacity(text.len());
    let mut rest = text;
    while !rest.is_empty() {
        let ws = rest
            .find(|c: char| !c.is_whitespace())
            .unwrap_or(rest.len());
        out.push_str(&rest[..ws]);
        rest = &rest[ws..];
        let end = rest.find(char::is_whitespace).unwrap_or(rest.len());
        let token = &rest[..end];
        rest = &rest[end..];
        let from = token
            .find(|c: char| c.is_ascii_alphanumeric())
            .unwrap_or(token.len());
        let to = token
            .rfind(|c: char| c.is_ascii_alphanumeric())
            .map_or(from, |i| (i + 1).max(from));
        let core = &token[from..to];
        if core.contains('@') && address_ok(core) {
            out.push_str(&token[..from]);
            out.push_str(&masked(core));
            out.push_str(&token[to..]);
        } else {
            out.push_str(token);
        }
    }
    out
}

/// **The first-admin door's words** as `site status` says them (FIRST-LINK-1).
pub mod door {
    /// The custody's invite still opens it: the ready mail's link signs the first admin in.
    pub const OPEN: &str = "open";
    /// The first admin has signed in (a passkey is held).
    pub const SPENT: &str = "spent";
    /// The invite's lifetime is over: the site no longer takes it.
    pub const EXPIRED: &str = "expired";
    /// The site took its secrets and the ONE box forgot them: its main mints a new link.
    pub const HANDED_OFF: &str = "handed_off";
    /// The door could not be asked.
    pub const UNKNOWN: &str = "unknown";
}

/// What a product's main answers a `sign_in_again` with when its account holds a passkey
/// (holger `PasskeyStore::mint_sign_in_again`, gunnar `repair`): the first link is spent.
pub const PASSKEY_HELD: &str = "already signs in with a passkey";

/// **The site's own first-admin door, asked with the custody's invite** (the run's custody
/// must hold it). The outer `Err`: the run cannot be asked at all; the inner: the door did not
/// answer.
fn ask_door(
    state: &AppState,
    run: &super::EstateRun,
) -> Result<Result<FirstDoor, String>, StartRefusal> {
    let eo = &state.estates;
    let name = &run.estate;
    let terms = run_terms(state, run)
        .ok_or_else(|| StartRefusal::NotSold(format!("{name}'s terms are not readable here")))?;
    let host = terms.host_of(&run.company);
    let custody = eo.custody.as_ref().ok_or_else(|| {
        StartRefusal::NotNow(format!(
            "{NOT_READY} no custody holds {name}'s first sign-in invite"
        ))
    })?;
    let secrets = custody.unseal(&run.company).map_err(|e| {
        StartRefusal::NotNow(format!("{NOT_READY} {name}'s custody does not open: {e}"))
    })?;
    let invite = secrets
        .first_login
        .as_ref()
        .map(|b| super::hex_of(b))
        .ok_or_else(|| {
            StartRefusal::NotNow(format!(
                "{NOT_READY} the custody holds no first sign-in invite for {name}"
            ))
        })?;
    let console_token = super::hex_of(&secrets.break_glass);
    let run_dir = eo
        .run_dir(&run.company)
        .ok_or_else(|| StartRefusal::NotSold("no estate-orders directory".into()))?;
    let life = lifecycle::of(&terms).map_err(StartRefusal::NotSold)?;
    let front_role = life.update_plan().front_role;
    let laid = run.laid.clone();
    let facts = super::pairing_facts(&terms);
    let at = SaleCheckAt {
        facts: &facts,
        company: &run.company,
        host: &host,
        account: &run.account,
        front: laid.as_ref().and_then(|l| l.machine(&front_role)),
        run_dir: &run_dir,
        console_token: &console_token,
        first_login: &invite,
    };
    Ok(eo
        .installer
        .first_door(&at)
        .map_err(|why| format!("{why} (at https://{host})")))
}

/// **`site status`'s first-admin door** of a laid operator site (FIRST-LINK-1): `(word,
/// sentence)` — the site's own door asked while the custody holds the invite; after the
/// hand-off, what its main answered the newest `sign_in_again` (the address masked). Empty for
/// anything else.
pub fn door_of(state: &AppState, run: &super::EstateRun) -> (String, String) {
    if !run.site_order || run.phase != phase::CAPTURED || run.account.is_empty() {
        return Default::default();
    }
    let name = &run.estate;
    let Some(custody) = state.estates.custody.as_ref() else {
        return (
            door::UNKNOWN.into(),
            format!("no custody holds {name}'s first sign-in invite here"),
        );
    };
    if !custody.holds(&run.company) {
        return handed_off_door(state, run);
    }
    let (word, words) = match ask_door(state, run) {
        Ok(Ok(FirstDoor::Open)) => (door::OPEN, format!("{name}'s first-admin door is open: the ready mail's first sign-in link signs {} in, once", run.account)),
        Ok(Ok(FirstDoor::Spent)) => (door::SPENT, format!("{name}'s first admin has signed in already: the first-admin door is spent")),
        Ok(Ok(FirstDoor::Closed(why))) => match main_answer(state, run) {
            Some((word, words)) => (if word == door::SPENT { door::SPENT } else { door::EXPIRED }, format!("{name}'s first sign-in invite no longer opens its first-admin door ({why}); {words}")),
            None => (door::EXPIRED, format!("{name}'s first sign-in invite no longer opens its first-admin door ({why}); `site first-link {name}` asks its main to mint a new link")),
        },
        Ok(Err(why)) => (door::UNKNOWN, format!("{name}'s first-admin door could not be asked: {why}")),
        Err(r) => (door::UNKNOWN, r.to_string()),
    };
    (word.into(), masked_in(&words))
}

/// [`door_of`] of a handed-off site: its main's newest answer to a `sign_in_again`.
fn handed_off_door(state: &AppState, run: &super::EstateRun) -> (String, String) {
    let name = &run.estate;
    let (word, words) = match main_answer(state, run) {
        Some((w, words)) if w == door::SPENT => (door::SPENT, words),
        Some((_, words)) => (door::HANDED_OFF, format!("{name} is handed off; {words}")),
        None => (door::HANDED_OFF, format!("{name} is handed off: the ONE box holds no first sign-in invite; `site first-link {name}` asks its main to mint a new link and mail it to {}", masked(&run.email))),
    };
    (word.into(), masked_in(&words))
}

/// **The site's main's newest answer to a `sign_in_again`** (`(word, words)`; `None`: it was
/// never asked): not pulled yet, refused, minted and mailed, or spent (a passkey is held).
fn main_answer(state: &AppState, run: &super::EstateRun) -> Option<(&'static str, String)> {
    use monetize_embed::fleet::kind;
    let (name, fleet) = (&run.estate, run.fleet_name());
    let role = run_terms(state, run)
        .and_then(|t| lifecycle::of(&t).ok())
        .map(|l| l.primary_role())?;
    let g = state.fleet.guardian()?;
    let queued = g
        .store
        .orders_for(fleet, &role, 0)
        .into_iter()
        .filter(|o| o.kind == kind::SIGN_IN_AGAIN)
        .map(|o| o.seq)
        .max();
    let acked = g.store.box_record(fleet, &role).and_then(|b| {
        b.acks
            .into_iter()
            .filter(|a| a.kind == kind::SIGN_IN_AGAIN)
            .max_by_key(|a| a.seq)
    });
    Some(match (queued, acked) {
        (Some(q), a) if a.as_ref().is_none_or(|a| a.seq < q) => (door::HANDED_OFF, format!("a new first sign-in link was asked; its {role} has not pulled the order yet")),
        (_, Some(a)) if !a.ok && a.detail.contains(PASSKEY_HELD) => (door::SPENT, format!("{name}'s first admin signs in with a passkey already; its {role} answered the last ask: {}", a.detail)),
        (_, Some(a)) if a.ok => (door::HANDED_OFF, format!("its {role} answered the last ask: {}", a.detail)),
        (_, Some(a)) => (door::HANDED_OFF, format!("its {role} refused the last ask: {}", a.detail)),
        _ => return None,
    })
}

/// **A handed-off site's first link, minted by its main** (FIRST-LINK-1): the signed
/// `sign_in_again` order ([`super::send_sign_in_again`], its rate limit, under the site's
/// label) for the first admin's RECORDED address. The words mask it.
///
/// HOL-MAIL (holger-rs 2026-10-27): the words promise nothing — the main may refuse (a main fed
/// no relay mails nothing). They name the order queued (`asked`); what the main answered is the
/// site's status ([`answer_of`], [`door_of`]), which `site first-link` follows.
fn by_main(
    state: &AppState,
    name: &str,
    run: &super::EstateRun,
    to: &str,
    why: &str,
) -> Result<FirstLinkAsked, StartRefusal> {
    let to = if to.is_empty() { run.email.trim() } else { to };
    if !address_ok(to) {
        return Err(StartRefusal::BadName(format!("{ADDRESS} {name}'s recorded first-admin address {:?} is not one mail address: give --to; nothing was asked", masked(to))));
    }
    let a = super::ask_sign_in_again(state, &run.company, Some(to))?;
    eprintln!("monetize-server: site {name}: the operator asked its main for a new first sign-in link for {} (to {}), order {}", run.account, masked(to), a.seq);
    Ok(FirstLinkAsked {
        words: format!(
            "{name} {why}: its {} was asked to mint a new sign-in link for {} and mail it to {} (signed order {}, the link works once, for {}); nothing is mailed until the {} answers — `site status {name}` says its answer",
            a.role,
            run.account,
            masked(to),
            a.seq,
            a.ttl,
            a.role
        ),
        asked: a.seq,
    })
}

/// **What `site first-link` asked** (HOL-MAIL): the words, and the seq of the signed
/// `sign_in_again` order queued for a handed-off site's main (0: no main was asked).
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct FirstLinkAsked {
    pub words: String,
    pub asked: u64,
}

/// **A handed-off site's main's newest answer to a `sign_in_again`** (HOL-MAIL): `(seq, mailed)`
/// — the seq of the newest order it answered (0: none) and whether it minted and mailed the
/// link (false: it refused, its reason in [`door_of`]'s words). `(0, false)` for a site whose
/// main was never asked, and for anything that is not a laid operator site.
pub fn answer_of(state: &AppState, run: &super::EstateRun) -> (u64, bool) {
    use monetize_embed::fleet::kind;
    if !run.site_order || run.phase != phase::CAPTURED {
        return (0, false);
    }
    let role = run_terms(state, run)
        .and_then(|t| lifecycle::of(&t).ok())
        .map(|l| l.primary_role());
    let (Some(g), Some(role)) = (state.fleet.guardian(), role) else {
        return (0, false);
    };
    g.store
        .box_record(run.fleet_name(), &role)
        .and_then(|b| {
            b.acks
                .into_iter()
                .filter(|a| a.kind == kind::SIGN_IN_AGAIN)
                .max_by_key(|a| a.seq)
        })
        .map_or((0, false), |a| {
            (a.seq, a.ok && !a.detail.contains(PASSKEY_HELD))
        })
}

/// **`EstateOrder.FirstLink`**: send the site `name`'s first sign-in link again, to `to`.
/// `Ok` is what the operator reads.
pub fn first_link(state: &AppState, name: &str, to: &str) -> Result<String, StartRefusal> {
    first_link_asking(state, name, to).map(|a| a.words)
}

/// **`EstateOrder.FirstLink`** with the order it queued for a handed-off site's main
/// ([`FirstLinkAsked::asked`]; 0: no main was asked, the words are the whole answer).
pub fn first_link_asking(
    state: &AppState,
    name: &str,
    to: &str,
) -> Result<FirstLinkAsked, StartRefusal> {
    let said = |words: String| FirstLinkAsked { words, asked: 0 };
    let name = name.trim().to_ascii_lowercase();
    let to = to.trim().to_string();
    // FIRST-LINK-1: no address is the first admin's recorded one.
    if !to.is_empty() && !address_ok(&to) {
        return Err(StartRefusal::BadName(format!(
            "{ADDRESS} {:?} is not one mail address (local@domain): nothing was changed or sent",
            masked(&to)
        )));
    }
    let eo = &state.estates;
    let base = eo.cfg.as_ref().ok_or_else(|| {
        StartRefusal::NotSold("this monetize lays no sites ([estate_orders] is absent)".into())
    })?;
    let Some(run) = eo.get(&name).filter(|r| !phase::is_gone(&r.phase)) else {
        return Err(StartRefusal::NotFound(format!(
            "{name} is not a site this box laid: no first sign-in link to send"
        )));
    };
    if !run.site_order {
        return Err(StartRefusal::NotNow(format!("{NOT_READY} {name} is not one of the operator's sites: a sold estate's link is its buyer's (sign in again)")));
    }
    if run.phase != phase::CAPTURED {
        return Err(StartRefusal::NotNow(format!("{NOT_READY} {name} is not laid and ready (it is {}): its ready mail carries the first sign-in link once it is", run.phase)));
    }
    if run.account.is_empty() {
        return Err(StartRefusal::NotNow(format!(
            "{NOT_READY} {name} names no first admin: there is no first sign-in link to send"
        )));
    }
    let custody = eo.custody.as_ref().ok_or_else(|| {
        StartRefusal::NotNow(format!(
            "{NOT_READY} no custody holds {name}'s first sign-in invite"
        ))
    })?;
    if !custody.holds(&run.company) {
        return by_main(state, &name, &run, &to, "is handed off");
    }
    let to_given = to.clone();
    let to = if to.is_empty() {
        run.email.trim().to_string()
    } else {
        to
    };
    if !address_ok(&to) {
        return Err(StartRefusal::BadName(format!("{ADDRESS} {name}'s recorded first-admin address {:?} is not one mail address: give --to; nothing was changed or sent", masked(&to))));
    }
    let _pen = eo.pen_for_a_call(base, &run.company)?;
    // Read again under the pen: the run is written by its own pen only.
    let Some(mut run) = eo.get(&run.company) else {
        return Err(StartRefusal::NotFound(format!("{name} is gone")));
    };
    let host = run_terms(state, &run)
        .ok_or_else(|| StartRefusal::NotSold(format!("{name}'s terms are not readable here")))?
        .host_of(&run.company);
    // The site's own door, at its public front, with the invite the custody holds.
    match ask_door(state, &run)?.map_err(|why| StartRefusal::Busy(format!("{name}'s first-admin door could not be asked at https://{host}: {why}; nothing was changed or sent"))) {
        Ok(FirstDoor::Open) => {}
        Ok(FirstDoor::Spent) => return Err(StartRefusal::NotNow(format!("{SPENT} {name}'s first admin has signed in already; use passkeys/recovery on the site"))),
        // HOL-FIRST-ADMIN: a new invite minted here would open nothing on the site; its main mints one.
        Ok(FirstDoor::Closed(why)) => {
            drop(_pen);
            return by_main(state, &name, &run, &to_given, &format!("no longer takes the custody's first sign-in invite ({})", masked_in(&why)));
        }
        Err(busy) => return Err(busy),
    }
    let now = state.now();
    let p = run.phase.clone();
    run.email = to.clone();
    run.told = false;
    run.history.push(Transition {
        phase: p,
        at_ms: now,
        note: format!(
            "first sign-in link re-sent by the operator to {}",
            masked(&to)
        ),
    });
    save(state, &run).map_err(StartRefusal::NotSold)?;
    // The ready mail again, to the new address, with the custody's link (it saves the run).
    super::tell(state, &mut run);
    eprintln!("monetize-server: site {name}: the operator re-sent its first sign-in link to {} (sent: {})", masked(&to), run.told);
    if run.told {
        Ok(said(format!("{name}'s first sign-in link is mailed to {} again; it works once, until the site's first admin signs in.", masked(&to))))
    } else {
        Ok(said(format!("{name}'s first sign-in address is now {}; the mail did not go yet and is sent again within the hour.", masked(&to))))
    }
}