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

nordisk/monetize

crates/monetize-embed/src/sold.rs

//! **What a sold private Gunnar is called** — ONE function, called by the shop
//! that sells it (monetize's estate order: the ready mail's first-login link,
//! the DNS records, `laid.json`'s `names`) and by the kit that installs it
//! (its certificate, its passkeys' relying party, its DNS records).
//!
//! A sold estate lives under the SELLING shop's apex (owner, 2026-10-08):
//! gunnar.rs sells `acme.gunnar.rs`; test2's shop sells `acme.test2.gunnar.rs`
//! — the certificate, the passkey RP, the DNS, the link, the mail and
//! `laid.json` all say the same name. Wiring trace A43: monetize named it
//! `<co>.<apex>` while the kit named it `<co>.gunnar.rs` whatever the apex, so
//! a test2 buyer's first-login link led to a name nothing served.

/// The names of one sold estate. `host` first: it is the console, the passkeys'
/// relying party, the name the certificate is for.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SoldNames {
    pub company: String,
    pub apex: String,
    /// `<company>.<apex>`.
    pub host: String,
}

impl SoldNames {
    /// The names of `company` sold by the shop at `apex` (`gunnar.rs`,
    /// `test2.gunnar.rs`). Pure; neither is checked here (the shop's name rule
    /// and the kit's are).
    pub fn of(company: &str, apex: &str) -> SoldNames {
        let (company, apex) = (
            company.trim().to_string(),
            apex.trim().trim_end_matches('.').to_ascii_lowercase(),
        );
        let host = format!("{company}.{apex}");
        SoldNames {
            company,
            apex,
            host,
        }
    }

    /// **Every name the estate serves under the shop's apex**, the kit's
    /// `subzone` shape: `<host>`, `www.<host>`, `git.<host>` — each one an A
    /// record to its front. No `monetize.<host>` (G16, ONE monetize): a sold
    /// estate runs no monetize; its boxes pull from the ONE.
    pub fn names(&self) -> Vec<String> {
        ["", "www.", "git."]
            .iter()
            .map(|p| format!("{p}{}", self.host))
            .collect()
    }

    /// **The word the kit puts between a name's first label and its DNS zone**
    /// (`zone` = `gunnar.rs`): `acme` at gunnar.rs, `acme.test2` at
    /// test2.gunnar.rs — so `git.gunnar.rs` becomes `git.acme.test2.gunnar.rs`.
    /// `None` when the host is not inside `zone` (the kit cannot write it).
    pub fn word_in(&self, zone: &str) -> Option<String> {
        let zone = zone.trim().trim_end_matches('.').to_ascii_lowercase();
        self.host
            .strip_suffix(&format!(".{zone}"))
            .filter(|w| !w.is_empty())
            .map(str::to_string)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    /// ★ A43: the shop and the kit name a sold estate under the SELLING shop's apex.
    #[test]
    fn a_sold_estate_is_named_under_the_selling_shops_apex() {
        let rs = SoldNames::of("acme", "gunnar.rs");
        // G16 (ONE monetize): a sold estate runs no monetize, so it serves no `monetize.<host>`.
        assert_eq!(
            rs.names(),
            vec!["acme.gunnar.rs", "www.acme.gunnar.rs", "git.acme.gunnar.rs"]
        );
        assert_eq!(rs.word_in("gunnar.rs").as_deref(), Some("acme"));
        let t2 = SoldNames::of("acme", "test2.gunnar.rs");
        assert_eq!(t2.host, "acme.test2.gunnar.rs");
        assert_eq!(
            t2.names(),
            vec![
                "acme.test2.gunnar.rs",
                "www.acme.test2.gunnar.rs",
                "git.acme.test2.gunnar.rs"
            ]
        );
        assert_eq!(t2.word_in("gunnar.rs").as_deref(), Some("acme.test2"));
        assert_eq!(
            t2.word_in("vetra.se"),
            None,
            "a host outside the zone has no word in it"
        );
        assert_eq!(
            SoldNames::of("acme", "Test2.Gunnar.RS.").host,
            "acme.test2.gunnar.rs"
        );
    }
}