crates/monetize-admin/src/lib.rs
//! # monetize-admin — the SOLO monetize box (G6, owner 2026-10-10 08:54)
//!
//! ONE machine runs monetize for EVERY product (gunnar, holger, …): the server
//! and the operator's console, as two separate processes of this one binary,
//! behind the box's own :443. The per-product monetize servers on the estates'
//! fronts go. No estate is dialled: every estate calls monetize (the pull model,
//! owner 2026-10-10 07:39); monetize's only outbound calls are the clouds' APIs
//! and the vendors' (Stripe).
//!
//! The binary is three roles ([`role`]):
//!
//! | role | how it is started | listens on |
//! |---|---|---|
//! | PID 1 | the medium's korp-start, no arguments | `:80`/`:443` (the edge, [`edge`]), the installer's link, the operator's door `:8475` ([`door`]) |
//! | the server | `monetize-admin server --config <data>/monetize/monetize.toml` ([`solo::server_args`]) | `127.0.0.1:50061` ([`solo::SERVER`]) |
//! | the console | `monetize-admin ui --bind 127.0.0.1:8091 --server 127.0.0.1:50061 --root <data>` ([`solo::ui_args`]) | `127.0.0.1:8091` ([`solo::UI`]) |
//!
//! PID 1 starts both children and starts each again when it dies
//! ([`supervise`]); a changed console setting restarts the console.
//!
//! | what | where | who sees it |
//! |---|---|---|
//! | the products, vendors, prices, secrets | fed by the installer (the kit's `monetize-admin` declaration), or PUT whole by the operator on the door after the install (`<data>/solo/door-feed.json`, 0600) | monetize-server, through `monetize_front::intake::apply_pull` |
//! | monetize's config | `<data>/monetize/monetize.toml` (written by the intake only) | the server |
//! | the server's bearer | `<data>/monetize/token`, minted here, 0600 | the console (loopback) |
//! | monetize's signing key | minted here; its public half offered as `monetize_pubkey` | every estate verifies monetize's signed orders with it |
//! | the console's settings | `<data>/monetize-ui/settings.toml`, 0600 (the relying party, the break-glass token) | the console |
//! | the passkeys and sessions | `<data>/monetize-ui/state.redb` | the console |
//! | the web certificates | Let's Encrypt for the fed `web_hosts` (edda `estate-front::web`) | the edge |
//!
//! To the installer (Skidbladnir `nordisk-pairing`) the box is the FRONT of an
//! estate of one ([`product::AdminFront`]).
use std::path::PathBuf;
pub mod alerts;
/// **Config in the application** (2026-10-14): the box's own site book.
pub mod app_config;
pub mod console;
pub mod door;
pub mod edge;
pub mod money_log;
pub mod offsite;
pub mod product;
pub mod restore;
/// The ONE box updates itself from the next signed release (owner 2026-10-14, point 5).
pub mod self_update;
pub mod solo;
pub mod supervise;
/// The build this binary is (the short commit, `unknown` outside a checkout).
pub const GIT_HASH: &str = env!("MONETIZE_ADMIN_GIT_HASH");
/// Where the medium stages the seed, under the data root.
pub const SEED_REL: &str = "pairing/seed.toml";
/// The data root's variable and default.
pub const DATA_ENV: &str = "MONETIZE_ADMIN_DATA";
/// The network the box brings up (`MONETIZE_ADMIN_NET`, else this): the public
/// NIC and the utility NIC.
pub const DEFAULT_NET: &str = "eth0:dhcp,eth1:dhcp";
/// Everything the console keeps, under the data root.
pub const CONSOLE_REL: &str = "monetize-ui";
/// The feed key of the break-glass bearer for the first passkey.
pub const FEED_ADMIN_TOKEN: &str = "console_admin_token";
/// The offer key of the build.
pub const OFFER_VERSION: &str = "version";
/// **What the server child links** (G11, 2026-10-10): the open set, and the shop's own
/// installer (`monetize-shop-pairing`, `[estate_orders] pairing = "shop-pairing"`) — the solo
/// box sells and pairs estates with no gunnar front in it.
pub fn linked() -> monetize_server::registry::Linked {
let mut linked = monetize_server::registry::Linked::open();
linked.installer(monetize_shop_pairing::factory());
// The sites the operator adds and edits in the application (2026-10-14), kept on this
// box's data disk beside the fed declaration.
linked.site_book = Some(monetize_server::site_book::SiteBookLink(std::sync::Arc::new(app_config::AppBook::on(data_root()))));
linked
}
/// **Which process this is** — by the first word after the program.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Role {
/// No role word: the box's PID 1 (the edge, the installer, the door, the children).
Pid1,
/// `server …`: monetize-server on the rest of the words.
Server(Vec<String>),
/// `ui …`: the console on the rest of the words.
Ui(Vec<String>),
/// `offsite …`: the off-box copy's operator words ([`offsite::cli`]), run
/// on the operator's machine (`offsite open`), never on the box.
Offsite(Vec<String>),
}
/// The role `args` (the program first, as `std::env::args`) names.
pub fn role(args: impl IntoIterator<Item = String>) -> Role {
let mut words = args.into_iter().skip(1);
match words.next().as_deref() {
Some("server") => Role::Server(words.collect()),
Some("ui") => Role::Ui(words.collect()),
Some("offsite") => Role::Offsite(words.collect()),
_ => Role::Pid1,
}
}
/// **One line of the installer's listener, at its tone** (stinkies
/// madmin-pairing-warn-level): the ordinary steps of a pairing — hello, the
/// listener open, the installer reached, the successor offer, the listener
/// closed, the log door open — are INFO; only what the listener itself calls a
/// warning (a refused connection, a link that cannot be raised) is WARN.
pub fn say_installer(tone: nordisk_pairing::bootstrap::Tone, line: &str) {
match tone {
nordisk_pairing::bootstrap::Tone::Info => tracing::info!("{line}"),
nordisk_pairing::bootstrap::Tone::Warn => tracing::warn!("{line}"),
}
}
/// **The first boot of an installation** (lane G7, the G5 follow-up; Gunnar `vm::first_boot`):
/// the pairing state of a previous installation is cleared once, decided by the ESP marker
/// (`esp` is `/boot`). A KEEP boot (the shop keep-boots the box from its release's keep
/// medium: a new ESP onto the kept data volume) of the SAME estate keeps the feed, the box's key
/// and its certificate (`Some(vec![])`): nordisk-pairing `reset_if_fresh_install_of`. The ESP
/// marker is written whole (`boxside::mark_esp`: staged, synced, renamed), so a power cut never
/// leaves an empty marker that reads "already cleared".
pub fn first_boot(esp: &std::path::Path, root: &std::path::Path, seed: &nordisk_pairing::seed::Seed) -> Option<Vec<String>> {
nordisk_pairing::boxside::reset_if_fresh_install_of(esp, root, &nordisk_pairing::boxside::PAIRING_STATE, seed, &nordisk_pairing::boxside::mark_esp)
}
/// The data root: `MONETIZE_ADMIN_DATA`, else `/data`.
pub fn data_root() -> PathBuf {
std::env::var(DATA_ENV).ok().filter(|v| !v.trim().is_empty()).map(PathBuf::from).unwrap_or_else(|| PathBuf::from("/data"))
}
/// `<data>/monetize-ui`.
pub fn console_dir(root: &std::path::Path) -> PathBuf {
root.join(CONSOLE_REL)
}
/// Pure: `VmRSS` and `VmHWM` (kB) out of `/proc/self/status`.
pub fn rss_of(status: &str) -> Option<(u64, u64)> {
let kb = |k: &str| status.lines().find(|l| l.starts_with(k)).and_then(|l| l.split_whitespace().nth(1)?.parse().ok());
Some((kb("VmRSS:")?, kb("VmHWM:")?))
}
#[cfg(test)]
mod role_tests {
#[test]
fn the_offsite_words_are_their_own_role() {
let r = super::role(["monetize-admin", "offsite", "open", "--into", "d"].map(String::from));
assert_eq!(r, super::Role::Offsite(["open", "--into", "d"].map(String::from).to_vec()));
assert_eq!(super::role(["monetize-admin"].map(String::from)), super::Role::Pid1);
}
}
#[cfg(test)]
mod rss_tests {
#[test]
fn the_resident_set_and_its_peak_are_read_off_proc_status() {
let t = "Name:\tmonetize-admin\nVmHWM:\t 18644 kB\nVmRSS:\t 10212 kB\n";
assert_eq!(super::rss_of(t), Some((10212, 18644)));
assert_eq!(super::rss_of("Name: x\n"), None);
}
}
#[cfg(test)]
mod tests {
#[derive(Clone, Default)]
struct Heard(std::sync::Arc<std::sync::Mutex<Vec<u8>>>);
impl std::io::Write for Heard {
fn write(&mut self, b: &[u8]) -> std::io::Result<usize> {
self.0.lock().unwrap().extend_from_slice(b);
Ok(b.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
/// ★ madmin-pairing-warn-level (MEASURED monetize-admin mock 2026-10-01: 6
/// WARN per install for the ordinary pairing steps): an Info line of the
/// installer's listener is said at INFO, a Warn line at WARN.
#[test]
fn the_installers_ordinary_steps_are_info_and_its_warnings_warn() {
use nordisk_pairing::bootstrap::Tone;
let heard = Heard::default();
let h = heard.clone();
let sub = tracing_subscriber::fmt().with_writer(move || h.clone()).with_ansi(false).finish();
tracing::subscriber::with_default(sub, || {
super::say_installer(Tone::Info, "hello — this VM presented itself to the installer");
super::say_installer(Tone::Warn, "a connection to the log door was refused: x");
});
let said = String::from_utf8(heard.0.lock().unwrap().clone()).unwrap();
assert!(said.lines().any(|l| l.contains(" INFO ") && l.contains("hello — this VM presented itself")), "{said}");
assert!(said.lines().any(|l| l.contains(" WARN ") && l.contains("refused: x")), "{said}");
}
fn front_seed(estate: &str) -> nordisk_pairing::seed::Seed {
nordisk_pairing::seed::Seed {
estate: estate.into(),
tunnel: Default::default(),
front: Default::default(),
kind: nordisk_pairing::seed::Kind::Front,
installer_ca_pem: String::new(),
bootstrap_cert_pem: String::new(),
bootstrap_key_pem: String::new(),
peers_expected: 1,
mullvad: None,
tailscale: None,
installer_link: None,
}
}
fn front_feed(estate: &str) -> String {
nordisk_pairing::join::JoinReply {
estate: estate.into(),
me: nordisk_pairing::join::Assigned { name: "vm1".into(), overlay: std::net::Ipv4Addr::new(10, 44, 0, 1) },
cert_pem: None,
role: Some(nordisk_pairing::Role::Front),
primary_overlay: None,
primary_utility: None,
peers: Vec::new(),
feed: Default::default(),
}
.render()
.unwrap()
}
/// ★ G7 (the G5 follow-up, A7 row 6): a KEEP boot of the ONE box — a new ESP, the kept data
/// volume, the same estate — keeps the box's feed; the ESP is marked, so it is decided once.
#[test]
fn a_keep_boot_of_the_same_estate_keeps_the_admin_boxs_feed() {
use nordisk_pairing::boxside::{ESP_CLEARED, JOINED_REL};
let (esp, data) = (tempfile::tempdir().unwrap(), tempfile::tempdir().unwrap());
std::fs::create_dir_all(esp.path().join("korp")).unwrap();
nordisk_pairing::fed_at_rest::write(data.path(), JOINED_REL, &front_feed("monetize.test")).unwrap();
let cleared = super::first_boot(esp.path(), data.path(), &front_seed("monetize.test"));
assert_eq!(cleared, Some(Vec::new()), "a keep boot of the same estate clears nothing");
assert!(data.path().join(JOINED_REL).exists(), "the feed is kept");
assert!(esp.path().join(ESP_CLEARED).exists(), "the boot is marked");
assert_eq!(super::first_boot(esp.path(), data.path(), &front_seed("monetize.test")), None, "decided once");
}
/// ★ G7: installed for ANOTHER estate onto the kept volume, the box starts unpaired, and
/// the ESP marker is written whole (`boxside::mark_esp`: staged, synced, renamed).
#[test]
fn another_estate_on_the_kept_volume_starts_unpaired_and_the_esp_marker_is_whole() {
use nordisk_pairing::boxside::{ESP_CLEARED, JOINED_REL};
let (esp, data) = (tempfile::tempdir().unwrap(), tempfile::tempdir().unwrap());
std::fs::create_dir_all(esp.path().join("korp")).unwrap();
nordisk_pairing::fed_at_rest::write(data.path(), JOINED_REL, &front_feed("other.test")).unwrap();
let cleared = super::first_boot(esp.path(), data.path(), &front_seed("monetize.test")).expect("a first boot");
assert!(cleared.contains(&JOINED_REL.to_string()), "{cleared:?}");
assert!(!data.path().join(JOINED_REL).exists());
let marker = std::fs::read_to_string(esp.path().join(ESP_CLEARED)).unwrap();
assert!(marker.contains("the pairing state of the previous installation was cleared"), "the marker is boxside::mark_esp's: {marker:?}");
}
}
/// ★ **PID 1's background work runs as named jera jobs** (A32-14): the off-box copy and the trial
/// confirm watch were raw threads (a panic ended the copy for the box's life, without a word).
/// Read off the source.
#[cfg(test)]
mod a32_jobs {
fn code(src: &str) -> &str {
&src[..src.find("#[cfg(test)]\nmod").unwrap_or(src.len())]
}
#[test]
fn the_offsite_copy_is_a_jera_duty() {
let c = code(include_str!("offsite.rs"));
assert!(c.contains("monetize_server::jobs::every(OFFSITE_JOB"), "the copy starts as a jera duty");
assert!(!c.contains(".name(\"monetize-offsite\".into())"), "the raw thread is still started");
}
#[test]
fn the_trial_confirm_watch_is_a_jera_duty() {
let c = code(include_str!("self_update.rs"));
assert!(c.contains("monetize_server::jobs::every(super::CONFIRM_JOB"), "the watch starts as a jera duty");
assert!(!c.contains("named(\"self-update-confirm\""), "the raw thread is still started");
}
}