nordisk/holger
server/cli/src/paired.rs
//! **holger as a box of an installer-built estate** (feature `paired`,
//! 2026-09-27) — the owner's plan 2a: vm1 the front (ui + monetize + plugin
//! door), vm2 holger's primary, vm3 its twin, over WireGuard + mutual TLS, set
//! up by the same installer as gunnar's (Skidbladnir `nordisk-pairing`).
//!
//! Nothing product-neutral is written here: the installer's wire, the box's
//! kept state (own key and certificate, `joined.toml`, the closed marker, the
//! fresh-install reset) and the listener thread are `nordisk_pairing::boxside`,
//! shared with gunnar. This module is holger's [`boxside::Product`] half —
//! its kind, tunnel key, capacity, what it offers, and what a feed means to it:
//!
//! * the ROLE the installer's election gave it becomes holger's role record
//! (`twin::elect`, tried on every feed and loop pass until it is recorded —
//! then never again: a role is a bootstrap fact); the pairing loop then
//! applies it LIVE ([`HolgerSteps::become_twin`]) — the loopback bind of the
//! public doors alone waits for the next start;
//! * the front's plugin door and this box's own certificate are what
//! the pull channel (metered) reads where the ONE monetize is from — no
//! `plugin_door:` block.
//!
//! It runs iff the medium staged a seed at `<root>/pairing/seed.toml`
//! (`<root>` = `HOLGER_APPLIANCE_DATA` or `/data`, the appliance's durable
//! root) — the same place gunnar's medium stages its own.
//!
//! **A holger box FINISHES an install** (2026-09-27, stinky 14 correction 4):
//! when holger IS PID 1 it brings its own network up first ([`pid1_duties`],
//! edda `appliance-pid1`: netlink + DHCP on eth0 and the utility eth1, the
//! lease kept alive, SIGTERM, the reaper — korp-start brings none), and after
//! the feed it runs the shared pairing loop (`nordisk_pairing::boxloop`): wg0
//! raised on the fed overlay address with the front's relay owning the /24,
//! the signal written, PAIRED reported — the installer closes on that. The
//! twin's own steps ([`HolgerSteps`]): become the twin LIVE on the running
//! server, be certified by the FRONT against the primary's own issuer
//! (`server_lib::twin_tls`), and follow over the primary's replication door —
//! PAIRED once a follow cycle over mutual TLS was whole.
use std::collections::BTreeMap;
use std::net::Ipv4Addr;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use nordisk_pairing::boxloop::{self, BoxDoors, Steps};
use nordisk_pairing::boxside::{self, Installed};
use nordisk_pairing::join::JoinReply;
use nordisk_pairing::seed::{Kind, Seed, TunnelKind};
use nordisk_pairing::Role;
/// Where the medium stages the seed, under the appliance root.
pub const SEED_REL: &str = "pairing/seed.toml";
/// The offer key of the build this box runs (gunnar's `version`, the same word).
pub const OFFER_VERSION: &str = "version";
/// **The console's login on this box** (A7 row 14, Gunnar d1b12b15's pattern): the FRONT
/// mints the console's key and offers its public half ([`OFFER_CONSOLE_PUBKEY`]); this box
/// enrols it and derives the console's bearer from it and its own key ([`console_bearer`],
/// the key under [`CONSOLE_KEY_DIR`], whose public half it offers as [`OFFER_BOX_PUBKEY`]);
/// the bearer is the static-token login `StaticToken(subject: "console", token_file:
/// Some("/data/holger-console-token"))` its RON declares. The front derives the same bearer
/// for this box (holger-estate-front `console_key`). No secret comes from the shop: until
/// 2026-10-22 the shop fed every VM one `holger_console_token`.
pub const OFFER_CONSOLE_PUBKEY: &str = "console_pubkey";
/// What this box offers the front: its console key's public half (base64).
pub const OFFER_BOX_PUBKEY: &str = "console_box_pubkey";
/// This box's console key, under [`appliance_root`] (the ceremony's 0600 `private.key`).
pub const CONSOLE_KEY_DIR: &str = "console";
/// The derivation's words (blake3 `derive_key`); the front derives under the same words.
pub const CONSOLE_BEARER_CONTEXT: &str =
"holger 2026-10-22 console bearer from the front's and the box's x25519 keys";
/// Where the derived bearer is written, under [`appliance_root`] (0600).
pub const CONSOLE_TOKEN_FILE: &str = "holger-console-token";
/// The floor a console bearer must clear (gunnar-ui's: 32 characters, 8 distinct).
pub const CONSOLE_TOKEN_MIN_LEN: usize = 32;
pub const CONSOLE_TOKEN_MIN_DISTINCT: usize = 8;
/// Pure: a fed bearer strong enough to guard the console, or a refusal by name.
pub fn console_token_check(t: &str) -> Result<(), String> {
let distinct = t.chars().collect::<std::collections::BTreeSet<_>>().len();
if t.len() < CONSOLE_TOKEN_MIN_LEN
|| distinct < CONSOLE_TOKEN_MIN_DISTINCT
|| t.chars().any(|c| c.is_whitespace() || c.is_control())
{
return Err(format!(
"REFUSED [holger-console-token-weak] the derived console bearer is {} characters with {distinct} distinct (needs >= {CONSOLE_TOKEN_MIN_LEN} and >= {CONSOLE_TOKEN_MIN_DISTINCT}, no whitespace); it is NOT installed",
t.len()
));
}
Ok(())
}
/// Pure: **the console's bearer between `my_private` and the other end's `their_public`
/// (base64)**, 64 hex characters (x25519, then blake3 `derive_key` under
/// [`CONSOLE_BEARER_CONTEXT`]); the front derives the same one from its half of the pair.
pub fn console_bearer(my_private: &[u8; 32], their_public_b64: &str) -> Result<String, String> {
let theirs = wg_appliance_core::keys::decode_key(their_public_b64.trim()).map_err(|e| format!("REFUSED [holger-console-key-bad] the front's offered console key is not a 32-byte base64 key: {e:?}"))?;
let shared = boringtun::x25519::StaticSecret::from(*my_private)
.diffie_hellman(&boringtun::x25519::PublicKey::from(theirs));
if !shared.was_contributory() {
return Err("REFUSED [holger-console-key-bad] the front's offered console key is a low-order point: no login is derived from it".to_string());
}
Ok(
blake3::derive_key(CONSOLE_BEARER_CONTEXT, shared.as_bytes())
.iter()
.map(|b| format!("{b:02x}"))
.collect(),
)
}
/// This box's console key under `root`, minted once and kept.
fn console_key(root: &Path) -> Result<wg_appliance_core::Identity, String> {
wg_appliance_core::ceremony::load_or_mint_identity(&root.join(CONSOLE_KEY_DIR))
.map_err(|e| format!("this box's console key under {}: {e}", root.display()))
}
/// **Enrol the front's console key**: the bearer derived from the offered key and this box's
/// own is written to `<root>/holger-console-token`, 0600, atomically and only when it
/// changed. The bearer provider reads the file at request time, so the login is live the
/// moment the offer arrives. `Ok(false)`: the feed carries no offered console key yet.
pub fn install_console_token(root: &Path, feed: &BTreeMap<String, String>) -> Result<bool, String> {
let Some(front) = nordisk_pairing::feedbus::offered(feed, OFFER_CONSOLE_PUBKEY) else {
return Ok(false);
};
let own = console_key(root)?;
let derived = console_bearer(&own.private, &front)?;
let t = derived.as_str();
console_token_check(t)?;
let path = root.join(CONSOLE_TOKEN_FILE);
if std::fs::read_to_string(&path).ok().as_deref() == Some(t) {
return Ok(true);
}
// Staged, fsynced, renamed, the directory fsynced (edda appliance_pid1::durable).
appliance_pid1::durable::write_atomic(&path, t.as_bytes(), Some(0o600))
.map_err(|e| format!("{}: {e}", path.display()))?;
Ok(true)
}
/// ★ **The key this box opens its front's reports under** (lane HOL-FLOW-FRONT): x25519 of
/// this box's console key and the front's offered one (the pair the console's bearer comes
/// from), under the report's OWN context (`holger_downstream_rules::front::key_of`), so the key
/// is no bearer. `Ok(None)`: the feed carries no offered console key yet.
pub fn front_report_key(
root: &Path,
feed: &BTreeMap<String, String>,
) -> Result<Option<[u8; 32]>, String> {
let Some(front) = nordisk_pairing::feedbus::offered(feed, OFFER_CONSOLE_PUBKEY) else {
return Ok(None);
};
let own = console_key(root)?;
let theirs = wg_appliance_core::keys::decode_key(front.trim()).map_err(|e| format!("REFUSED [holger-console-key-bad] the front's offered console key is not a 32-byte base64 key: {e:?}"))?;
let shared = boringtun::x25519::StaticSecret::from(own.private)
.diffie_hellman(&boringtun::x25519::PublicKey::from(theirs));
if !shared.was_contributory() {
return Err("REFUSED [holger-console-key-bad] the front's offered console key is a low-order point: no report key is derived from it".to_string());
}
Ok(Some(server_lib::front_report::front::key_of(
shared.as_bytes(),
)))
}
/// Pure: does this auth config declare a static token read from `file` that is not there yet?
pub fn declares_missing_token_file(
auth: Option<&server_lib::auth::AuthConfig>,
file: &Path,
) -> bool {
auth.map(|a| {
a.methods.iter().any(|m| matches!(m, server_lib::auth::AuthMethodConfig::StaticToken { token_file: Some(f), .. } if Path::new(f) == file))
})
.unwrap_or(false)
&& !file.is_file()
}
/// **What the boot waits for before its doors start** (lane D7): set while
/// [`await_console_token`] or [`await_estate_tunnel`] waits,
/// cleared when it returns. The pairing loop's steps that need the running server name it,
/// so a box that never starts says what it is missing (test3-holger-rs r6: the twin stood at
/// "become-twin: the server has not started yet" while the boot waited for
/// `holger_console_token`, which the shop fed to the front alone).
static BOOT_WAIT: std::sync::Mutex<Option<String>> = std::sync::Mutex::new(None);
fn boot_waits(what: Option<String>) {
*BOOT_WAIT.lock().unwrap_or_else(|p| p.into_inner()) = what;
}
/// What the boot waits for now, if anything.
pub fn boot_wait() -> Option<String> {
BOOT_WAIT.lock().unwrap_or_else(|p| p.into_inner()).clone()
}
/// Pure: **why a step that needs the running server cannot run yet**, naming the wait.
pub fn not_started(wait: Option<&str>) -> String {
match wait {
Some(w) => format!("the server has not started yet: the boot waits for {w}"),
None => "the server has not started yet: the twin's role is switched on the running doors"
.to_string(),
}
}
// ── the server's start, as the pair status says it (lane D10) ───────────────
/// **Where holger-server's start is**, for the pair status the installer reads (lane D10).
/// test3-holger-rs: the main said PAIRED while its server was not serving, because the
/// loop's `primary_ready` needs only the role record, and a start that returned Err as
/// PID 1 hung the box with its last status frozen. Now the pair status says PAIRED only
/// once the server serves, and a refused start is said word for word while the start is
/// tried again.
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct ServerNow {
/// The start reached `STAGE serving`.
pub serving: bool,
/// What the start does now (a step of `main.rs`'s boot), when it waits for no feed.
pub stage: String,
/// The last refusal of a start, word for word, kept until the server serves.
pub refused: Option<String>,
/// How many starts were refused.
pub refusals: u32,
}
static SERVER: std::sync::Mutex<Option<ServerNow>> = std::sync::Mutex::new(None);
fn server_with(f: impl FnOnce(&mut ServerNow)) {
let mut g = SERVER.lock().unwrap_or_else(|p| p.into_inner());
f(g.get_or_insert_with(ServerNow::default));
}
/// The start is at `stage` (said in the pair status while it does not serve).
pub fn server_stage(stage: &str) {
server_with(|s| s.stage = stage.to_string());
}
/// The start was refused: `why`, word for word. The start is tried again.
pub fn server_refused(why: &str) {
server_with(|s| {
s.serving = false;
s.refusals += 1;
s.refused = Some(why.replace(['\r', '\n'], " ").trim().to_string());
});
}
/// The server reached `STAGE serving`.
pub fn server_serving() {
// From here a PID-1 fault restarts the box (unless it loops): edda appliance_pid1::fault.
appliance_pid1::fault::serving_now();
watch_memory();
server_with(|s| {
s.serving = true;
s.refused = None;
s.stage = "serving".into();
});
}
/// Pure: **one memory line** from `/proc/self/status` and `/proc/meminfo` (owner 2026-10-23, the
/// lean estate: main and the twin run in 2 GB and 1 GB boxes; the line is the proof of their
/// headroom): this process's resident set and its peak, the box's memory and what is available.
pub fn memory_line(status: &str, meminfo: &str) -> Option<String> {
let kib = |text: &str, key: &str| {
text.lines()
.find_map(|l| l.strip_prefix(key))
.and_then(|v| v.split_whitespace().next())
.and_then(|n| n.parse::<u64>().ok())
};
let (rss, peak) = (kib(status, "VmRSS:")?, kib(status, "VmHWM:")?);
let (total, avail) = (kib(meminfo, "MemTotal:")?, kib(meminfo, "MemAvailable:")?);
Some(format!(
"memory: holger-server rss {} MiB, peak {} MiB; box {} MiB, available {} MiB",
rss / 1024,
peak / 1024,
total / 1024,
avail / 1024
))
}
/// **The box's own duties** (A28 M18): jera jobs (holger-live `duty`), each listed while it
/// runs, its failures said as ALERT lines, all stopped at the clean stop ([`stop_duties`]).
/// The memory line, every five minutes.
pub const MEMORY_WATCH: &str = "holger.memory-watch";
/// The pair status, looked at every second (said when it changes).
pub const PAIR_STATUS: &str = "holger.pair-status";
/// The pairing tried again after a seed that could not be read ([`start_or_retry`]).
pub const PAIRING_RETRY: &str = "holger.pairing-retry";
/// Where a duty keeps its looks: `<appliance root>/duties/<kind>`, on a box that has the root.
fn duty_ledger(kind: &str) -> Option<PathBuf> {
let root = appliance_root();
root.is_dir().then(|| root.join("duties").join(kind))
}
/// Where a duty says its failures: this box's log, at ERROR.
fn duty_say() -> holger_live::duty::Say {
Arc::new(|l: &str| log::error!("{l}"))
}
/// **The clean stop's step for the duties**: every duty of this box stopped. Answers how many ran.
pub fn stop_duties() -> usize {
holger_live::duty::stop_all()
}
/// A pairing start (the seam the retry is tested through).
type Starter = fn(&Path) -> Result<Option<Arc<Installed<HolgerBox>>>, String>;
/// **The pairing, tried again in the background** with `backoff` before each try: done when a
/// try reads a seed (its pairing is kept for the process's life) or finds none staged.
fn retry_pairing(
root: PathBuf,
start: Starter,
backoff: fn(u32) -> std::time::Duration,
) -> Result<Arc<holger_live::duty::Duty>, String> {
holger_live::duty::start(
PAIRING_RETRY,
duty_ledger(PAIRING_RETRY).as_deref(),
move |n| backoff(u32::try_from(n).unwrap_or(u32::MAX)),
duty_say(),
move |n| {
let n = u32::try_from(n).unwrap_or(u32::MAX).saturating_add(1);
log::warn!("pairing: try {n}");
Ok(match start(&root) {
Ok(Some(p)) => {
log::warn!(
"pairing: the seed read on try {n} — the installer's listener serves"
);
let _ = LATE_PAIRING.set(p);
holger_live::duty::Look::Done
}
Ok(None) => holger_live::duty::Look::Done,
Err(e) => {
log::error!(
"pairing: {e} — still up; trying again in {} s",
backoff(n).as_secs()
);
holger_live::duty::Look::Again
}
})
},
)
}
/// Says [`memory_line`] at once and every five minutes, once per process.
fn watch_memory() {
static ONCE: std::sync::Once = std::sync::Once::new();
ONCE.call_once(|| {
let every = |n: u64| std::time::Duration::from_secs(if n == 1 { 0 } else { 300 });
let started = holger_live::duty::start(
MEMORY_WATCH,
duty_ledger(MEMORY_WATCH).as_deref(),
every,
duty_say(),
|_| {
let read = |p: &str| std::fs::read_to_string(p).unwrap_or_default();
if let Some(line) = memory_line(&read("/proc/self/status"), &read("/proc/meminfo"))
{
// Said at WARN, the box's log floor (RUST_LOG is unset on an appliance): the
// line is the proof of the lean boxes' headroom, read off the log door.
log::warn!("{line}");
}
Ok(holger_live::duty::Look::Again)
},
);
if let Err(e) = started {
log::error!("ALERT {MEMORY_WATCH} the memory line is NOT said: {e}");
}
});
}
/// Where the start is now.
pub fn server_now() -> ServerNow {
SERVER
.lock()
.unwrap_or_else(|p| p.into_inner())
.clone()
.unwrap_or_default()
}
/// Pure: **what the start waits for**, in words: the boot's named wait, else its stage.
pub fn server_waits(now: &ServerNow, wait: Option<&str>) -> String {
match wait {
Some(w) => format!("the boot waits for {w}"),
None if now.stage.is_empty() => "it is starting".to_string(),
None => now.stage.clone(),
}
}
/// The pair status's step while the server does not serve.
pub const SERVE_STEP: &str = "serve";
/// The loop's own step for a primary (nordisk-pairing `Step::PrimaryCheckAdmin`).
const PRIMARY_STEP: &str = "check-admin";
/// Pure: **the pair status with the server's start in it** (lane D10). A box whose
/// server does not serve is never PAIRED: its step is `serve`, saying what the start
/// waits for. A refused start is said word for word ("server REFUSED [holger-…] …")
/// on every step until a later start serves. A serving box: the loop's status as is.
pub fn with_server(
mut s: nordisk_pairing::signal::Signal,
now: &ServerNow,
wait: Option<&str>,
) -> nordisk_pairing::signal::Signal {
if now.serving || s.refused.is_some() {
return s;
}
if let Some(why) = &now.refused {
s.paired = false;
s.step = SERVE_STEP.into();
s.tries = now.refusals;
s.last_transient = Some(format!(
"server {why}; the start is tried again, now {}",
server_waits(now, wait)
));
} else if s.paired || s.step == PRIMARY_STEP {
s.paired = false;
s.step = SERVE_STEP.into();
s.tries = s.tries.max(1);
s.last_transient = Some(format!(
"waiting for the server: {}",
server_waits(now, wait)
));
}
s
}
/// [`with_server`] over this process's start.
pub fn status_now(s: nordisk_pairing::signal::Signal) -> nordisk_pairing::signal::Signal {
with_server(s, &server_now(), boot_wait().as_deref())
}
/// Pure: **why the primary is not ready yet**, or `None` once its server serves: the
/// main's last step is its server's start (lane D10).
pub fn primary_waits(now: &ServerNow, wait: Option<&str>) -> Option<String> {
if now.serving {
return None;
}
Some(match &now.refused {
Some(why) => format!(
"server {why}; the start is tried again, now {}",
server_waits(now, wait)
),
None => format!("waiting for the server: {}", server_waits(now, wait)),
})
}
fn primary_waits_now() -> Option<String> {
primary_waits(&server_now(), boot_wait().as_deref())
}
/// The seconds a start waits again after its `n`th refusal: 5, 10, 20, 40, then 60.
pub fn start_backoff(n: u32) -> std::time::Duration {
std::time::Duration::from_secs((5u64 << n.saturating_sub(1).min(4)).min(60))
}
/// The test seam for the boot's waits (`HOLGER_BOOT_WAIT_SECS`): the budget of the
/// console token's and wg0's waits, `default` when unset or unparsable.
pub const BOOT_WAIT_ENV: &str = "HOLGER_BOOT_WAIT_SECS";
pub fn boot_wait_budget(default: std::time::Duration) -> std::time::Duration {
std::env::var(BOOT_WAIT_ENV)
.ok()
.and_then(|v| v.trim().parse::<u64>().ok())
.map(std::time::Duration::from_secs)
.unwrap_or(default)
}
/// **The pair status, said in the boot log when it changes** (lane D10): the line the
/// installer reads, with the server's start in it, also on the box's own log.
fn watch_status(installed: Arc<Installed<HolgerBox>>) {
// No ledger: a look a second, written to redb, would be two commits a second for the
// box's life; the duty is listed and its failures are said all the same.
let last = std::sync::Mutex::new(String::new());
let started = holger_live::duty::start(
PAIR_STATUS,
None,
|_| std::time::Duration::from_secs(1),
duty_say(),
move |_| {
let line = status_now(nordisk_pairing::bootstrap::Vm::status(&*installed)).line();
let mut last = last.lock().unwrap_or_else(|p| p.into_inner());
if line != *last {
log::warn!(
"pair status:{}",
line.trim_start().trim_start_matches("pair")
);
*last = line;
}
Ok(holger_live::duty::Look::Again)
},
);
if let Err(e) = started {
log::error!("ALERT {PAIR_STATUS} the pair status is NOT said in the boot log: {e}");
}
}
/// **First boot of an installer-built box**: the RON declares the console's token
/// file, and only the installer's feed writes it — so the boot WAITS for the feed
/// (the listener is already serving) rather than start with a login that
/// authenticates nobody (`refuse_disabled_static_token` would refuse it). Bounded;
/// a box never fed is refused by name.
pub fn await_console_token(
auth: Option<&server_lib::auth::AuthConfig>,
budget: std::time::Duration,
) -> Result<(), String> {
let file = appliance_root().join(CONSOLE_TOKEN_FILE);
if !declares_missing_token_file(auth, &file) {
return Ok(());
}
// Said BY NAME with its start, once a minute, in words no log filter withholds (test3-holger-rs
// 2026-10-22: the box's redaction dropped every line naming the console's file whole).
let mut wait = holger_live::waits::Wait::new(&who_waits(), CONSOLE_KEY_WAIT);
boot_waits(Some(wait.words()));
let t = std::time::Instant::now();
while !file.is_file() {
if let Some(l) = wait.due() {
log::warn!("pairing: {l}");
}
if t.elapsed() > budget {
boot_waits(Some(format!("nothing: it REFUSED to start, the front never offered its console key (`{OFFER_CONSOLE_PUBKEY}`) in {} s", budget.as_secs())));
return Err(format!("REFUSED [holger-console-token-unfed] {} is the console's login and the front never offered its console key (`{OFFER_CONSOLE_PUBKEY}`) in {} s", file.display(), budget.as_secs()));
}
std::thread::sleep(std::time::Duration::from_millis(500));
}
log::warn!("pairing: {}", wait.done());
boot_waits(None);
Ok(())
}
/// **What the boot waits for before its doors start, by name** (no secret, no word a log
/// filter withholds): the front's console key, offered through the installer.
pub const CONSOLE_KEY_WAIT: &str = "the front's offered console key, to derive the console login from (the installer relays it from the front)";
/// The tunnel wait, by name.
pub const TUNNEL_WAIT: &str =
"wg0, this estate box's tunnel (its doors open on the tunnel address only)";
/// Who waits, for a wait line: `this box (primary)` once the door elected it, else `this box`.
fn who_waits() -> String {
match kept_reply(&appliance_root()).and_then(|r| r.role) {
Some(role) => format!("this box ({})", role.as_str()),
None => "this box".to_string(),
}
}
/// **The front's monetize key, as the installer's feed carries it**, pure:
/// `offer.<front>.monetize_pubkey` in the join reply's feed (vm1 mints it at its
/// first start, so no medium can carry it).
pub fn fed_monetize_key(reply: &JoinReply) -> Option<String> {
reply
.feed
.iter()
.find(|(k, v)| {
k.starts_with("offer.") && k.ends_with(".monetize_pubkey") && !v.trim().is_empty()
})
.map(|(_, v)| v.trim().to_string())
}
/// **The feed key of the estate's pay page** (monetize-shop-pairing `contract::FEED_BILLING_URL`,
/// the name gunnar-server's feed reads): the shop's estate feed, given to every box.
pub const PAY_PAGE_FEED: &str = "refusal_link";
/// **The estate's pay page, as the shop feeds it**, pure: [`PAY_PAGE_FEED`] in the join reply's
/// feed (the site row's renewal page, lane M4). A paid refusal names it.
pub fn fed_pay_page(reply: &JoinReply) -> Option<String> {
reply
.feed
.get(PAY_PAGE_FEED)
.map(|v| v.trim().to_string())
.filter(|v| !v.is_empty())
}
/// [`fed_pay_page`] of the feed this box keeps (`pairing/joined.toml`), once it is in.
pub fn fed_pay_page_kept() -> Option<String> {
kept_reply(&appliance_root())
.as_ref()
.and_then(fed_pay_page)
}
/// **The shop's first-login invite, as the installer feeds it** (lane P1; monetize-shop-pairing
/// `FEED_OPERATOR_INVITE`, `FEED_OPERATOR_INVITE_TTL`, `FEED_OPERATOR`): the SHA-256 of the
/// secret the buyer's mail carries, how long it lives, and the buyer's account it is pinned to.
pub const FIRST_LOGIN_FEED: &str = "operator_invite_sha256";
pub const FIRST_LOGIN_TTL_FEED: &str = "operator_invite_ttl_secs";
pub const BUYER_FEED: &str = "operator_name";
/// The first-login link's lifetime when the feed names none (the shop's default, seven days).
pub const FIRST_LOGIN_TTL_DEFAULT: i64 = 7 * 24 * 3600;
/// Pure: **the first-login invite a feed carries** — `(sha256 hex, ttl secs, buyer)`; `None`
/// when it names no invite or no buyer. A TTL that is no number is the default.
pub fn fed_first_login(feed: &BTreeMap<String, String>) -> Option<(String, i64, String)> {
let get = |k: &str| {
feed.get(k)
.map(|v| v.trim().to_string())
.filter(|v| !v.is_empty())
};
let hash = get(FIRST_LOGIN_FEED)?.to_ascii_lowercase();
let buyer = get(BUYER_FEED)?;
let ttl = get(FIRST_LOGIN_TTL_FEED)
.and_then(|t| t.parse::<i64>().ok())
.filter(|t| *t > 0)
.unwrap_or(FIRST_LOGIN_TTL_DEFAULT);
Some((hash, ttl, buyer))
}
/// [`fed_first_login`] of the feed this box keeps (`pairing/joined.toml`), once it is in.
pub fn fed_first_login_kept() -> Option<(String, i64, String)> {
kept_reply(&appliance_root()).and_then(|r| fed_first_login(&r.feed))
}
/// **The site the ONE box fed this box** (RA5-1), from the feed it KEEPS (`pairing/joined.toml`)
/// with any signed refeed over it: the estate's declared variant (a sold estate is invite only,
/// an operator site sells sign-up), `server_lib::identity_host::site_from_feed`. `None`: the
/// feed declares nothing yet (the box is invite only until it does).
#[cfg(feature = "auth-passkey")]
pub fn fed_site_at(data_root: &Path) -> Option<server_lib::identity_host::Site> {
let feed = kept_reply(&appliance_root())
.map(|r| r.feed)
.unwrap_or_default();
#[cfg(feature = "metered")]
let feed = server_lib::refeed::over(feed, data_root);
#[cfg(not(feature = "metered"))]
let _ = data_root;
server_lib::identity_host::site_from_feed(&feed, cfg!(feature = "metered"))
}
/// **A metered box of an estate takes the front's monetize key from the feed it KEEPS**
/// (`pairing/joined.toml`) when its config names none: a restarted box enforces at once. A
/// box whose kept feed names none yet does NOT wait for it any more (it waited up to 600 s,
/// then enforced no plan until a restart): its paid-state store is made keyless and the
/// front's later offer is applied live (A7 row 8, `server_lib::late::follow_monetize`).
pub fn fed_monetize_key_kept() -> Option<String> {
kept_reply(&appliance_root())
.as_ref()
.and_then(fed_monetize_key)
}
/// **The box's feed bus, as server-lib sees it** (A7 rows 1 and 8): every feed and re-feed
/// the box keeps (Skidbladnir `nordisk-pairing::feedbus`, [`Installed::feed_bus`]) and the
/// role the installer's door elected.
pub struct BoxFeeds(pub Arc<Installed<HolgerBox>>);
impl server_lib::late::Feeds for BoxFeeds {
fn subscribe(&self, name: &'static str, take: server_lib::late::Take) {
self.0
.feed_bus()
.on_published(name, move |r: &JoinReply| take(&r.feed));
}
fn now(&self) -> BTreeMap<String, String> {
self.0.latest().map(|r| r.feed).unwrap_or_default()
}
fn door_role(&self) -> Option<Option<String>> {
door_role_of(self.0.latest().as_ref())
}
}
/// Pure: the role the door elected, in nordisk-pairing `elect::pull_role`'s words: `None`
/// when no door fed this box, `Some(None)` while it has not elected one.
pub fn door_role_of(reply: Option<&JoinReply>) -> Option<Option<String>> {
reply.map(|r| r.role.map(|role| role.as_str().to_string()))
}
/// **Where a RE-ADMITTED primary is filled from**, pure: the installer's
/// `recharge_from` (nordisk-pairing `door::RECHARGE_FROM_FEED`, the twin's
/// control plane) in the primary's own feed. A dead main laid again from the
/// one medium is re-admitted into its closed estate (`installer::readmit`)
/// and told this; a first install feeds none, a twin is never told.
pub fn recharge_from_of(reply: &JoinReply) -> Option<String> {
if reply.role != Some(Role::Primary) {
return None;
}
reply
.feed
.get(nordisk_pairing::door::RECHARGE_FROM_FEED)
.map(|v| v.trim().to_string())
.filter(|v| !v.is_empty())
}
/// [`recharge_from_of`] the feed kept under `root` (`pairing/joined.toml`).
pub fn fed_recharge_from_at(root: &Path) -> Option<String> {
let reply = kept_reply(root)?;
let from = recharge_from_of(&reply)?;
// An estate box is filled from its twin over wg0 (lane E1).
Some(if server_lib::tunnel_addr::wg_only() {
over_the_tunnel_at(&from, &reply)
} else {
from
})
}
/// **The first boot after an installation** ([`start`]): the previous installation's pairing
/// state is cleared — and a KEEP boot of the SAME estate clears nothing (lane G5, A7 row 6;
/// Gunnar ff58dad1): every keep-media fallback unpaired the box before. The ESP marker is
/// written durably (`boxside::mark_esp`). `Some(vec![])`: a keep boot, kept.
pub fn first_boot(esp: &Path, root: &Path, seed: &Seed) -> Option<Vec<String>> {
boxside::reset_if_fresh_install_of(esp, root, &boxside::PAIRING_STATE, seed, &boxside::mark_esp)
}
/// The fed reply kept under `root` (`pairing/joined.toml`, SEALED at rest: lane G5, A7 row 21,
/// Skidbladnir `nordisk_pairing::fed_at_rest`; a plain one from before still reads).
pub(crate) fn kept_reply(root: &Path) -> Option<JoinReply> {
JoinReply::parse(&nordisk_pairing::fed_at_rest::read(
root,
nordisk_pairing::boxside::JOINED_REL,
)?)
.ok()
}
/// **The boot's `recharge_from` from the feed** — read once the feed is in
/// (after [`await_console_token`]), used only when the RON names none: the
/// box then serves nothing until the recharge lane has filled it from its
/// twin (`Holger::start`, `holger-twin-15`).
pub fn fed_recharge_from() -> Option<String> {
let from = fed_recharge_from_at(&appliance_root())?;
log::warn!("pairing: this box was RE-ADMITTED as the primary and is to be filled from its twin at {from} (the installer's `{}`)", nordisk_pairing::door::RECHARGE_FROM_FEED);
Some(from)
}
/// **The feed key naming this estate's console host** (private-holger-ops
/// `install::PASSKEY_RP_FEED`): the name a person's browser reaches the console
/// at, which is the relying party holger's passkey plane is bound to. Fed to
/// every box, because the RON on the medium is shared by every estate of the
/// kit (`holger.rs`, `test2.holger.rs`) and a passkey is bound to ONE name.
pub const PASSKEY_RP_FEED: &str = "passkey_rp_id";
/// Pure: **holger's passkey plane, for the fed relying party** — added to
/// `auth` when the feed names a host and the config declares no `Passkey`
/// method of its own (a declared one always wins: the RON is the operator's
/// word). An open config (`methods: []`) is left open — a passkey method there
/// would turn authentication ON as a side effect. `Some(rp_id)` when added.
///
/// The people are holger's from then on (holger-ui `people`, the thin-layer
/// move 2026-10-04): the console carries every login, invite and session to
/// this box's `/-/auth/passkey/*`.
pub fn passkey_from_feed(
auth: &mut Option<server_lib::auth::AuthConfig>,
feed: &BTreeMap<String, String>,
) -> Option<String> {
passkey_for(auth, feed, None)
}
/// [`passkey_from_feed`], with **the estate's own name as the relying party when the feed
/// names none** (lane F12, test3-holger-rs 2026-10-21): the ONE box's shop feeds no
/// `passkey_rp_id` — the kit does — and names the estate in the join reply instead
/// ([`estate_of`], the order's first name, the very host the buyer's browser reaches). Without
/// this a shop-installed main never opened its passkey plane: `/-/auth/passkey/config` answered
/// 404, the shop's sale check (monetize-shop-pairing `judge_holger`, its first-login door) never
/// passed, and the site stayed `installing (paired)`. A fed `passkey_rp_id` always wins.
pub fn passkey_for(
auth: &mut Option<server_lib::auth::AuthConfig>,
feed: &BTreeMap<String, String>,
estate: Option<&str>,
) -> Option<String> {
let fed = feed
.get(PASSKEY_RP_FEED)
.map(|v| v.trim().to_ascii_lowercase())
.filter(|v| !v.is_empty());
let rp = fed.or_else(|| {
estate
.map(|e| e.trim().trim_end_matches('.').to_ascii_lowercase())
.filter(|e| !e.is_empty())
})?;
if !is_dns_name(&rp) {
log::error!("pairing: the fed `{PASSKEY_RP_FEED}` {rp:?} is not a DNS name; holger's passkey plane is NOT enabled from it");
return None;
}
let a = auth.as_mut()?;
if a.methods.is_empty()
|| a.methods
.iter()
.any(|m| matches!(m, server_lib::auth::AuthMethodConfig::Passkey { .. }))
{
return None;
}
a.methods.push(server_lib::auth::AuthMethodConfig::Passkey {
rp_id: rp.clone(),
rp_origin: format!("https://{rp}"),
rp_name: "holger".into(),
session_key_file: None,
bootstrap_until_first_admin: true,
});
Some(rp)
}
/// Pure: a DNS name with a dot, not an address.
pub fn is_dns_name(n: &str) -> bool {
n.len() <= 253
&& n.contains('.')
&& n.parse::<std::net::IpAddr>().is_err()
&& n.split('.').all(|l| {
!l.is_empty()
&& l.len() <= 63
&& l.chars().all(|c| c.is_ascii_alphanumeric() || c == '-')
})
}
/// Pure: **the estate's own name**, as the installer named it in the join reply
/// (`estate`, the order's first name on the ONE box: `test3.holger.rs`). The
/// medium's declaration is shared by every estate, so this is where its
/// `{estate}` comes from (lane W2, `Holger::resolve_estate`). `None` for a name
/// that is not a DNS name.
pub fn estate_of(reply: &JoinReply) -> Option<String> {
let e = reply
.estate
.trim()
.trim_end_matches('.')
.to_ascii_lowercase();
if is_dns_name(&e) {
return Some(e);
}
log::error!("pairing: the fed estate name {:?} is not a DNS name; the declaration's {{estate}} is not filled in", reply.estate);
None
}
/// [`estate_of`] over the reply the pairing loop kept (`pairing/joined.toml`).
pub fn fed_estate() -> Option<String> {
estate_of(&kept_reply(&appliance_root())?)
}
/// Pure: **the estate's public repositories as the installer fed them** (NIGHT-PUBREPO,
/// `public_repositories = "crates,npm"`, `server_lib::posture::PUBLIC_REPOSITORIES_FEED`): the
/// names; unfed, none (the medium makes no repository public).
pub fn public_repositories_of(feed: &BTreeMap<String, String>) -> Vec<String> {
feed.get(server_lib::posture::PUBLIC_REPOSITORIES_FEED)
.map(|v| server_lib::posture::public_list_of(v))
.unwrap_or_default()
}
/// [`public_repositories_of`] over the reply the pairing loop kept (`pairing/joined.toml`).
pub fn fed_public_repositories() -> Vec<String> {
kept_reply(&appliance_root())
.map(|r| public_repositories_of(&r.feed))
.unwrap_or_default()
}
/// [`fed_public_repositories`] with what the ONE box fed this box AGAIN over it (MAIL-RELAY +
/// NIGHT-PUBREPO: a signed `refeed`, kept under `data_root` by `server_lib::refeed`): a list fed
/// after the lay is in force from the next start.
pub fn fed_public_repositories_at(data_root: &Path) -> Vec<String> {
#[cfg(feature = "metered")]
{
let feed = kept_reply(&appliance_root())
.map(|r| r.feed)
.unwrap_or_default();
public_repositories_of(&server_lib::refeed::over(feed, data_root))
}
#[cfg(not(feature = "metered"))]
{
let _ = data_root;
fed_public_repositories()
}
}
/// [`passkey_from_feed`] over the feed the pairing loop kept
/// (`pairing/joined.toml`) — read once the feed is in (after
/// [`await_console_token`]).
pub fn fed_passkey(auth: &mut Option<server_lib::auth::AuthConfig>) {
let Some(reply) = kept_reply(&appliance_root()) else {
return;
};
let named = !reply
.feed
.get(PASSKEY_RP_FEED)
.is_none_or(|v| v.trim().is_empty());
if let Some(rp) = passkey_for(auth, &reply.feed, estate_of(&reply).as_deref()) {
let from = if named {
format!("the installer's `{PASSKEY_RP_FEED}`")
} else {
format!("the estate's name; no `{PASSKEY_RP_FEED}` was fed")
};
log::warn!("pairing: holger's passkey plane is on for https://{rp} ({from}) — the console's people are this box's");
}
}
/// **The feed key naming the site a sold estate was bought at** (monetize-shop-pairing
/// `console_provider`, `https://holger.rs`; Gunnar's front sets `GUNNAR_UI_PROVIDER` from it).
pub const PROVIDER_FEED: &str = "console_provider";
/// Pure: **the provider the feed names**, as holger's sign-up links take one
/// (`server_lib::room::provider_named`); `None` when it names none or not an `https://` site.
pub fn provider_of_feed(feed: &BTreeMap<String, String>) -> Option<String> {
server_lib::room::provider_named(feed.get(PROVIDER_FEED).map(String::as_str))
}
/// [`provider_of_feed`] over the kept reply, set as `HOLGER_PROVIDER` for this process unless the
/// operator set it (A16: a sold Holger's "New instance" and "Grow" lead to its provider, also on
/// an own domain). Called at boot, before the doors open.
pub fn fed_provider() {
if std::env::var_os(server_lib::room::PROVIDER_ENV).is_some() {
return;
}
let Some(reply) = kept_reply(&appliance_root()) else {
return;
};
if let Some(p) = provider_of_feed(&reply.feed) {
log::warn!("pairing: this estate's provider is {p} (the fed `{PROVIDER_FEED}`): its sign-up page leads there");
std::env::set_var(server_lib::room::PROVIDER_ENV, p);
}
}
/// **The estate's separation of duties** (lane W7, `server_lib::auth::estate_duties`): a box
/// whose RON grants no duty gets the table where the console's admins (its bearer and the first
/// admin's passkey) hold RepositoryAdmin and SecurityApprover and never Importer, and the
/// admin invites the Importers who upload and the further approvers. Without it the one console
/// login could never approve anything: an importer never approves its own upload.
pub fn estate_duties(auth: &mut Option<server_lib::auth::AuthConfig>) {
let Some(a) = auth.as_mut() else { return };
let said = server_lib::auth::estate_duties(a);
if !said.is_empty() {
log::warn!(
"pairing: the estate's duties (the RON grants none): {}",
said.join("; ")
);
}
}
/// holger's boot log, kept for the installer from the first line.
/// With a pinned head: the install, the pairing and the first boot are kept for life
/// (nordisk-pairing seals the head at the installer's close, and at once on a restart that
/// finds it closed); the runtime after it is the evictable tail (Gunnar f21d8d09).
pub static BOOT_LOG: nordisk_pairing::bootlog::BootLog =
nordisk_pairing::bootlog::BootLog::for_install();
/// The appliance's durable root.
pub fn appliance_root() -> PathBuf {
std::env::var_os("HOLGER_APPLIANCE_DATA")
.map(PathBuf::from)
.unwrap_or_else(|| PathBuf::from("/data"))
}
/// holger's half of the box.
pub struct HolgerBox {
/// The appliance root: the seed, the pairing state, the WireGuard key.
root: PathBuf,
/// holger's own data root (where its role record lives).
data_root: PathBuf,
tunnel: TunnelKind,
/// **This box's own live slot** (L20, holger-live): `None` off a korp appliance — the live
/// door then refuses by name and the front keeps the box from its keep medium.
live: Option<Arc<holger_live::LiveSlot>>,
}
impl HolgerBox {
/// This box's own live slot, if it has one.
pub fn live_slot(&self) -> Option<&Arc<holger_live::LiveSlot>> {
self.live.as_ref()
}
}
/// The refusal of a box with no live slot (the shared door's word).
pub const NO_LIVE_SLOT: &str =
"REFUSED [live-unsupported] this holger box runs off a korp appliance: no live slot";
/// The offer key of the key this box signs its buyers' actor tickets with — the ONE shop's
/// installer waits for the elected primary's (`monetize-shop-pairing` `TICKET_OFFER`) and keeps
/// it as the estate's appliance key.
pub const OFFER_TICKET_KEY: &str = "ticket_pubkey";
/// The offer key of this box's own fleet key, the one the ONE box pins for its role.
pub const OFFER_FLEET_KEY: &str = "fleet_pubkey";
/// **This box's keys, as the installer carries them** (metered): its fleet key (`fleet/box.ed25519`,
/// minted once) is both what the ONE box pins for its pull channel and what its actor tickets are
/// signed with (`server_lib::room`), so the shop takes the appliance key from the box itself.
#[cfg(feature = "metered")]
pub fn key_offers(data_root: &Path) -> BTreeMap<String, String> {
match server_lib::holger_fleet::BoxKey::load_or_mint(data_root) {
Ok(k) => {
let hex = server_lib::holger_fleet::hex(&k.public());
BTreeMap::from([
(OFFER_TICKET_KEY.to_string(), hex.clone()),
(OFFER_FLEET_KEY.to_string(), hex),
])
}
Err(e) => {
log::error!("pairing: this box's fleet key cannot be read or minted ({e}): it offers no ticket key, and the shop's installer waits");
BTreeMap::new()
}
}
}
/// Pure: the role record a feed asks for. `None` = the feed names no role yet.
/// `wg_only` (an estate box, lane E1): a twin follows the primary's OVERLAY address
/// and nothing else; otherwise its utility address, as before.
pub fn role_for(reply: &JoinReply, wg_only: bool) -> Option<server_lib::twin::NodeRole> {
match reply.role? {
Role::Primary => Some(server_lib::twin::NodeRole::Primary),
Role::Twin => {
let at = if wg_only {
primary_overlay_of(reply)?
} else {
reply.primary_utility.or(reply.primary_overlay)?
};
Some(server_lib::twin::NodeRole::Twin {
primary: format!("{at}:{}", nordisk_pairing::CONTROL_PORT),
poll_seconds: server_lib::twin::DEFAULT_POLL_SECONDS,
})
}
_ => None,
}
}
/// Pure: **the primary's overlay address** in a feed: the reply's word, else the
/// primary peer's.
pub fn primary_overlay_of(reply: &JoinReply) -> Option<Ipv4Addr> {
reply.primary_overlay.or_else(|| {
reply
.peers
.iter()
.find(|p| p.role == Role::Primary)
.map(|p| p.overlay)
})
}
/// Pure: **a fed box address, over the tunnel** (lane E1, Gunnar's `over_the_tunnel`
/// pattern): the installer feeds a box's UTILITY address (`recharge_from`, the primary
/// the pairing loop names); an estate box dials the same box's OVERLAY address. An
/// address no fed peer holds is left as it is (and the estate dial refuses it).
pub fn over_the_tunnel(ip: Ipv4Addr, reply: &JoinReply) -> Ipv4Addr {
if reply.primary_utility == Some(ip) {
if let Some(o) = primary_overlay_of(reply) {
return o;
}
}
reply
.peers
.iter()
.find(|p| p.utility == Some(ip))
.map(|p| p.overlay)
.unwrap_or(ip)
}
/// [`over_the_tunnel`] for a fed `host:port`.
pub fn over_the_tunnel_at(at: &str, reply: &JoinReply) -> String {
match at.trim().parse::<std::net::SocketAddrV4>() {
Ok(sa) => {
std::net::SocketAddrV4::new(over_the_tunnel(*sa.ip(), reply), sa.port()).to_string()
}
Err(_) => at.to_string(),
}
}
/// **This box's estate posture** (lane E1): WireGuard-only on the overlay network, at
/// `overlay` once fed. Set by the seed's tunnel kind (a WireGuard seed is an estate box).
pub fn set_estate_posture(overlay: Option<Ipv4Addr>, reply: Option<&JoinReply>) {
if let Some(r) = reply {
server_lib::topology::set_pairing(Some(board_pairing(r)));
}
let was = server_lib::tunnel_addr::estate();
let overlay = overlay.or(was.as_ref().and_then(|e| e.overlay));
let pairs = match reply {
Some(r) => tunnel_pairs(r),
None => was.as_ref().map(|e| e.pairs.clone()).unwrap_or_default(),
};
let now = server_lib::tunnel_addr::Estate {
net: nordisk_pairing::OVERLAY_NET,
overlay,
pairs,
};
if was.as_ref() != Some(&now) {
server_lib::tunnel_addr::set(Some(now));
log::warn!(
"pairing: an ESTATE box — WireGuard-only, every estate link over wg0 ({})",
overlay
.map(|o| o.to_string())
.unwrap_or_else(|| "no overlay address yet".into())
);
}
}
/// Pure: **the estate board's pairing view** (lane E3): every box the feed names —
/// this one with the role the election gave it, then its peers — with each one's
/// WireGuard overlay address and, where fed, its utility address.
pub fn board_pairing(reply: &JoinReply) -> server_lib::topology::Pairing {
use server_lib::topology::PairedBox;
let mut boxes = vec![PairedBox {
name: reply.me.name.clone(),
role: reply
.role
.map(|r| r.as_str().to_string())
.unwrap_or_default(),
overlay: Some(reply.me.overlay),
utility: if reply.role == Some(Role::Primary) {
reply.primary_utility
} else {
None
},
}];
for p in reply.peers.iter().filter(|p| p.name != reply.me.name) {
let utility = p.utility.or(if p.role == Role::Primary {
reply.primary_utility
} else {
None
});
boxes.push(PairedBox {
name: p.name.clone(),
role: p.role.as_str().to_string(),
overlay: Some(p.overlay),
utility,
});
}
server_lib::topology::Pairing {
estate: reply.estate.clone(),
me: reply.me.name.clone(),
boxes,
not_after_unix: 0,
}
}
/// Pure: **`(utility, overlay)` of every fed box** — the primary the reply names and
/// every peer with a utility address.
pub fn tunnel_pairs(reply: &JoinReply) -> Vec<(Ipv4Addr, Ipv4Addr)> {
let mut v: Vec<(Ipv4Addr, Ipv4Addr)> = reply
.peers
.iter()
.filter_map(|p| Some((p.utility?, p.overlay)))
.collect();
if let (Some(u), Some(o)) = (reply.primary_utility, primary_overlay_of(reply)) {
if !v.iter().any(|(x, _)| *x == u) {
v.push((u, o));
}
}
v
}
/// **Before the doors open, an estate box waits for its tunnel** (lane E1): its posture
/// from the seed and the kept feed, then wg0's overlay address bindable — the estate
/// doors bind there and nowhere else. Not an estate box (another tunnel kind): nothing.
pub fn await_estate_tunnel(budget: std::time::Duration) -> Result<(), String> {
let root = appliance_root();
// [`start`] set the posture from the seed's tunnel kind.
if !server_lib::tunnel_addr::wg_only() {
return Ok(());
}
let deadline = std::time::Instant::now() + budget;
let mut wait: Option<holger_live::waits::Wait> = None;
loop {
let reply = kept_reply(&root);
let overlay = reply.as_ref().map(|r| r.me.overlay);
set_estate_posture(overlay, reply.as_ref());
if let Some(o) = overlay {
if std::net::TcpListener::bind((o, 0)).is_ok() {
log::warn!("pairing: wg0 answers on {o} — the estate doors open there");
if let Some(w) = &wait {
log::warn!("pairing: {}", w.done());
}
boot_waits(None);
return Ok(());
}
}
if std::time::Instant::now() >= deadline {
return Err(format!("REFUSED [{}] this estate box's tunnel never came up ({}) within {} s: its estate doors open on wg0 only. REFUSING TO START.", server_lib::tunnel_addr::NOT_WG, overlay.map(|o| format!("wg0 {o} is not bindable")).unwrap_or_else(|| "nothing fed yet".into()), budget.as_secs()));
}
let w =
wait.get_or_insert_with(|| holger_live::waits::Wait::new(&who_waits(), TUNNEL_WAIT));
if let Some(l) = w.due() {
log::warn!("pairing: {l}");
boot_waits(Some(w.words()));
}
std::thread::sleep(std::time::Duration::from_millis(500));
}
}
/// Pure: **the same role, only its primary moved** — a twin fed a twin role naming
/// another primary. Not an election against the recorded one (that is primary ↔
/// twin): a main recharged from this twin lives at new addresses, and the re-feed
/// names them before the lane re-points the twin. It was logged at ERROR as "a role
/// is a bootstrap fact" on every recharge (MEASURED by the private-holger-ops
/// estate-box gate, 2026-10-04, R1).
pub fn same_role_moved_primary(
recorded: &server_lib::twin::NodeRole,
fed: &server_lib::twin::NodeRole,
) -> bool {
recorded.is_twin() && fed.is_twin() && recorded.primary() != fed.primary()
}
/// Pure: **the primary a twin's record must be re-pointed at**, or `None` — a TWIN
/// record naming another host than the installer's `primary` (the pairing loop's
/// word, which the installer re-admission carries). MEASURED by the private-holger-ops
/// estate-box gate (2026-10-04, U1 after R1): a twin whose record named `0.0.0.0:50051`
/// (the recharged main's BIND address, from the restore lane) was refused "a role is
/// a bootstrap fact" at every try, and its upgrade's re-admission waited for ever. A
/// primary's record is never re-pointed: twin ↔ primary stays the refusal.
pub fn repoint_to(recorded: &server_lib::twin::NodeRole, primary: Ipv4Addr) -> Option<String> {
let named = recorded.primary()?;
(!named.starts_with(&format!("{primary}:")))
.then(|| format!("{primary}:{}", nordisk_pairing::CONTROL_PORT))
}
/// `MemTotal` of `/proc/meminfo`, bytes — the one reader `AdminService.Capacity` uses too
/// (stinky SB #37: the box's capacity RPC and its join measure the election's axes alike).
fn ram_bytes() -> Option<u64> {
server_lib::capacity::ram_bytes()
}
impl boxside::Product for HolgerBox {
fn kind(&self) -> Kind {
Kind::Holger
}
/// Live update (L20): what this box runs, off its own slot.
fn live_running(&self) -> Result<nordisk_pairing::live::Running, String> {
self.live
.as_ref()
.ok_or_else(|| NO_LIVE_SLOT.to_string())?
.running()
}
/// Live update: a new app for the slot not running, then the graceful re-exec.
fn live_stage(&self, stage: &nordisk_pairing::live::Stage) -> Result<String, String> {
self.live
.as_ref()
.ok_or_else(|| NO_LIVE_SLOT.to_string())?
.stage(stage)
}
/// Live update with what the release declares (lane B5): the declaration verified for the
/// new app and laid with it, the release's env held against this box's cmdline.
fn live_stage_declared(
&self,
stage: &nordisk_pairing::live::Stage,
declared: &nordisk_pairing::live::Declared,
) -> Result<String, String> {
self.live
.as_ref()
.ok_or_else(|| NO_LIVE_SLOT.to_string())?
.stage_declared(stage, declared)
}
fn tunnel_pubkey(&self) -> String {
if self.tunnel != TunnelKind::Wireguard {
return String::new();
}
match wg_appliance_core::ceremony::load_or_mint_identity(&self.root.join("wg")) {
Ok(id) => id.public_b64,
Err(e) => {
log::error!(
"pairing: this box cannot mint its tunnel key; the join carries none: {e}"
);
String::new()
}
}
}
/// What the election compares: cores, RAM, data room and the start-up disk test's MiB/s
/// (lane B3, shared nordisk-pairing `elect::disktest`). `hello` answers "ask again" while
/// the test runs, at most `HOLD_HELLO`; a box that runs none reads 0.
fn capacity(&self) -> Result<Option<nordisk_pairing::elect::Capacity>, String> {
// The test of THIS box's data root (one per process).
let disk_mib_s = match DISKTEST.get().filter(|(at, _)| *at == self.data_root) {
Some((_, t)) => {
t.hold_hello(std::time::Instant::now())?;
t.disk_mib_s()
}
None => 0,
};
let cores = std::thread::available_parallelism()
.map(|n| n.get() as u32)
.map_err(|e| format!("cores: {e}"))?;
let ram_bytes = ram_bytes().ok_or("MemTotal cannot be read from /proc/meminfo")?;
let data_bytes = server_lib::metering::statvfs(&self.data_root)
.map(|s| s.total_bytes)
.unwrap_or(0);
Ok(Some(nordisk_pairing::elect::Capacity {
cores,
ram_bytes,
data_bytes,
disk_mib_s,
}))
}
fn offer(&self, _fed: Option<&JoinReply>) -> BTreeMap<String, String> {
let mut o = BTreeMap::from([(OFFER_VERSION.to_string(), server_lib::GIT_HASH.to_string())]);
// The console key's public half (A7 row 14): the front derives this box's login from it.
match console_key(&self.root) {
Ok(id) => {
o.insert(OFFER_BOX_PUBKEY.to_string(), id.public_b64);
}
Err(e) => log::error!(
"pairing: {e}: the box offers no console key, and the console cannot reach it"
),
}
#[cfg(feature = "metered")]
o.extend(key_offers(&self.data_root));
o
}
/// The fed console bearer is installed (every feed; unchanged is a no-op), and
/// the elected role becomes holger's role record, once.
fn fed(&self, reply: &JoinReply) {
clock_fed(&reply.feed);
// Lane HOL-WGDS: the front's downstream interface (its WireGuard key and endpoint),
// kept for the invites this box signs for its WireGuard downstreams.
#[cfg(feature = "estate")]
{
use server_lib::sync::downstreams::{
keep_front_hub, OFFER_HUB_ENDPOINT, OFFER_HUB_KEY,
};
if let (Some(k), Some(e)) = (
nordisk_pairing::feedbus::offered(&reply.feed, OFFER_HUB_KEY),
nordisk_pairing::feedbus::offered(&reply.feed, OFFER_HUB_ENDPOINT),
) {
match keep_front_hub(&self.data_root, &k, &e) {
Ok(true) => log::info!("pairing: the front's downstream interface is kept ({e}) for the WireGuard downstreams' invites"),
Ok(false) => {}
Err(why) => log::error!("pairing: the front's downstream interface is not taken: {why}"),
}
}
}
match install_console_token(&self.root, &reply.feed) {
Ok(true) => log::info!("pairing: the front's console key is enrolled and the console's login installed ({})", self.root.join(CONSOLE_TOKEN_FILE).display()),
Ok(false) => {}
Err(e) => log::error!("pairing: {e}"),
}
// The key the front's report of its doors and routes is opened under (HOL-FLOW-FRONT).
match front_report_key(&self.root, &reply.feed)
.and_then(|k| k.map(|k| server_lib::front_report::keep_key(&self.data_root, &k)).transpose())
{
Ok(Some(true)) => log::info!("pairing: the key for the front's report is kept: its doors and routes can be drawn"),
Ok(_) => {}
Err(e) => log::error!("pairing: the front's report key: {e}"),
}
if self.tunnel == TunnelKind::Wireguard {
set_estate_posture(Some(reply.me.overlay), Some(reply));
}
let Some(role) = role_for(reply, self.tunnel == TunnelKind::Wireguard) else {
return;
};
match server_lib::twin::recorded_role(&self.data_root) {
Ok(Some(r)) if r.role == role => {}
// The same role, its primary moved: a main laid again at new addresses
// (a recharge) is fed to its twin before the lane re-points it.
// The same role, its primary moved: the installer says where the
// primary lives NOW (a main recharged at new addresses) — re-pointed.
Ok(Some(r)) if same_role_moved_primary(&r.role, &role) => {
let (to, was) = (role.primary().unwrap_or_default().to_string(), r.role.primary().unwrap_or_default().to_string());
match server_lib::twin::repoint(&self.data_root, &to, server_lib::clock::now_unix_secs()) {
Ok(_) => log::warn!("pairing: the installer names the primary `{to}`; this twin's record named `{was}` — re-pointed"),
Err(e) => log::error!("pairing: the installer names the primary `{to}` and this twin's record (`{was}`) could not be re-pointed: {e}"),
}
}
Ok(Some(r)) => log::error!(
"pairing: the installer's election says `{}`, and this box was elected `{}` at {} — a role is a bootstrap fact; rebuild the box to change it",
if role.is_twin() { "twin" } else { "primary" },
if r.role.is_twin() { "twin" } else { "primary" },
r.elected_at
),
// Not recorded yet: elected now. A refusal is said here and tried again
// on every later feed and every loop pass ([`HolgerSteps`], lane D5).
Ok(None) => match record_election(&self.data_root, &role) {
Ok(_) => log::warn!(
"pairing: elected `{}` by the installer — recorded in {}; the pairing loop applies it live",
if role.is_twin() { "twin" } else { "primary" },
self.data_root.display()
),
Err(e) => log::error!("pairing: {e}"),
},
Err(e) => log::error!("pairing: the role record cannot be read: {e}"),
}
}
}
/// **Whether this process may move the machine's clock**: PID 1 of its own
/// medium (`korp.entry=` on the command line). A container entrypoint is PID 1
/// of its namespace too, and a privileged container shares the host's clock:
/// stepping it there would move somebody's real machine — so there the verified
/// offset is owed in-process (`server_lib::clock`) and the system clock is left
/// alone. Naming a source in the medium's `ESTATE_CLOCK` turns it on explicitly.
pub fn owns_the_clock() -> bool {
std::env::var_os(server_lib::clock::CLOCK_ENV).is_some()
|| (appliance_pid1::net::is_init() && appliance_pid1::net::booted_by_korp_start())
}
/// **The installer fed this box** (every feed, and once at start from the kept
/// feed): the clock reads the feed on every sync (`server_lib::clock::set_feed`).
/// A fed run ([`FED_CLOCK`]: the triggers armed first, then syncs backing off
/// while the front comes up, on its own thread) starts at the first call in this
/// process (the boot sync), on a NEW clock, and on a REPEATED clock while this
/// process has not set its clock right ([`clock_feed_syncs`], Gunnar f2e602dd: a
/// re-laid twin whose boot sync could not reach the front through a tunnel not
/// yet up was re-fed the same clock and asked nobody). Never blocks the caller.
pub fn clock_fed(feed: &BTreeMap<String, String>) {
static STARTED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
let changed = server_lib::clock::set_feed(feed);
match server_lib::clock::config_now() {
Ok(server_lib::clock::Config::Sources { .. }) => {}
Err(e) => {
log::error!("clock: {e}");
return;
}
Ok(_) => return,
}
let first = !STARTED.swap(true, std::sync::atomic::Ordering::SeqCst);
let fed_clock = server_lib::clock::fed_clock(feed).0.is_some();
if !clock_feed_syncs(first, changed, fed_clock, server_lib::clock::last_outcome()) {
return;
}
if !first && !changed {
log::info!("clock: the installer fed the same clock again and this process has not set its clock right yet — a sync now");
}
if !FED_CLOCK.start() {
log::error!(
"clock: the fed sync could not get a thread of its own; the triggers take over"
);
}
}
/// Pure: **whether a feed starts a fed run** — the first in this process (the
/// boot sync), a new clock, or a repeated one while the clock is not set right
/// (`server_lib::clock::feed_resyncs`).
pub fn clock_feed_syncs(
first: bool,
changed: bool,
fed_clock: bool,
last: Option<server_lib::clock::Outcome>,
) -> bool {
first || server_lib::clock::feed_resyncs(changed, fed_clock, last)
}
/// **This box's fed clock runs** (edda `estate_clock::offer::FedRun`, Gunnar's
/// `clock::fed_arrived` + `tunnel_up`): one per process.
static FED_CLOCK: server_lib::clock::FedRun =
server_lib::clock::FedRun::new(fed_clock_sync, fed_clock_arm);
/// One fed sync: logged, its outcome returned.
fn fed_clock_sync() -> server_lib::clock::Outcome {
let owns = owns_the_clock();
let r = server_lib::clock::sync(
&server_lib::clock::config_now(),
server_lib::clock::Trigger::Timer,
owns,
);
clock_log(&r);
log::info!(
"clock: following the front ({}; this process {} the machine's clock)",
if server_lib::clock::is_trusted() {
"verified"
} else {
"NOT verified"
},
if owns { "moves" } else { "does not move" }
);
r.outcome
}
/// The running triggers (a skew refusal, the 6-hour timer), armed once.
fn fed_clock_arm() {
server_lib::clock::arm_triggers(owns_the_clock(), clock_log);
}
/// **This box's tunnel just came up** ([`ClockOnTunnelUp`]): the fed clock
/// source is the front's address, reached through this tunnel on a box whose
/// fed syncs ran before it was up (Gunnar 1115a4e6, i54a run23: the twin's
/// syncs timed out, it paused while its tunnel came up, and the front's calls
/// were refused for skew meanwhile). A clock not yet right is asked NOW — once
/// per process, nothing when it is right or no clock is configured.
fn clock_tunnel_up() {
if matches!(
server_lib::clock::config_now(),
Ok(server_lib::clock::Config::Sources { .. })
) && FED_CLOCK.tunnel_up(server_lib::clock::last_outcome())
{
log::info!("clock: the tunnel is up — the fed clock is asked now");
}
}
/// **A tunnel driver that says when it is up** — the pairing loop's tunnel
/// (nordisk-pairing `BoxDoors::tunnel_up` reads `status()` right after `up`),
/// with `on_up` called on every `status()` that reports up. Holger's hook for
/// the loop's `TunnelUp` (Gunnar calls `clock::tunnel_up` from its own loop).
pub struct ClockOnTunnelUp {
inner: Box<dyn nordisk_pairing::tunnel::Tunnel>,
on_up: fn(),
}
impl ClockOnTunnelUp {
pub fn new(inner: Box<dyn nordisk_pairing::tunnel::Tunnel>, on_up: fn()) -> Self {
ClockOnTunnelUp { inner, on_up }
}
}
impl nordisk_pairing::tunnel::Tunnel for ClockOnTunnelUp {
fn kind(&self) -> TunnelKind {
self.inner.kind()
}
fn public_key(&self) -> String {
self.inner.public_key()
}
fn up(
&mut self,
me: &nordisk_pairing::tunnel::Identity,
peers: &[nordisk_pairing::join::Peer],
) -> Result<(), nordisk_pairing::tunnel::TunnelError> {
self.inner.up(me, peers)
}
fn add_peer(
&mut self,
peer: &nordisk_pairing::join::Peer,
) -> Result<(), nordisk_pairing::tunnel::TunnelError> {
self.inner.add_peer(peer)
}
fn status(&self) -> nordisk_pairing::tunnel::TunnelStatus {
let s = self.inner.status();
if s.up {
(self.on_up)();
}
s
}
fn repoint(
&mut self,
me: &nordisk_pairing::tunnel::Identity,
peers: &[nordisk_pairing::join::Peer],
) -> Result<bool, nordisk_pairing::tunnel::TunnelError> {
self.inner.repoint(me, peers)
}
fn set_preshared_key(
&mut self,
psk: Option<&str>,
) -> Result<bool, nordisk_pairing::tunnel::TunnelError> {
self.inner.set_preshared_key(psk)
}
fn installer_reach(
&self,
) -> Result<nordisk_pairing::installer_link::Reach, nordisk_pairing::tunnel::TunnelError> {
self.inner.installer_reach()
}
}
/// A sync's report, at the level it deserves.
fn clock_log(r: &server_lib::clock::Report) {
use server_lib::clock::Outcome;
match r.outcome {
// The success too reaches the boot log (`server_lib::CLOCK_LOG_TARGET` is named at
// info in the default floor): a box's last clock line must be its last sync's.
Outcome::Stepped
| Outcome::Slewed
| Outcome::AlreadyRight
| Outcome::NotConfigured
| Outcome::Off => {
log::info!(target: server_lib::CLOCK_LOG_TARGET, "clock: {}", r.summary())
}
_ => {
log::warn!(target: server_lib::CLOCK_LOG_TARGET, "clock: {}. If this box's clock is wrong, every expiry it checks is checked against it.", r.summary())
}
}
for row in &r.sources {
if let Some(why) = &row.error {
log::warn!(target: server_lib::CLOCK_LOG_TARGET, "clock: source {} failed: {why}", row.source);
}
}
}
/// **The installer's election, recorded as holger's role record** (`twin::elect`),
/// or the refusal by its code: "the installer's election could not be recorded:
/// [holger-twin-N] …". Called by every feed and every loop pass that carries the
/// election until it is recorded (lane D5); the callers read the record first, so
/// a recorded role is never elected again.
fn record_election(
data_root: &Path,
role: &server_lib::twin::NodeRole,
) -> Result<server_lib::twin::RoleRecord, String> {
let now = server_lib::clock::now_unix_secs();
server_lib::twin::elect(
data_root,
role.clone(),
"installer",
"the installer's election (nordisk-pairing: the weakest box is the twin)",
now,
)
.map_err(|e| format!("the installer's election could not be recorded: {e}"))
}
/// **holger's steps after the tunnel** — the product half of the shared loop.
pub struct HolgerSteps {
/// The appliance root: the installer's kept feed (`pairing/joined.toml`).
root: PathBuf,
/// holger's own data root (where its role record lives).
data_root: PathBuf,
/// A WireGuard seed: the role names the primary's overlay address.
wireguard: bool,
/// Why the server does not serve yet, `None` once it does (lane D10:
/// [`primary_waits`] over this process's start; a test passes its own).
server: fn() -> Option<String>,
}
impl HolgerSteps {
/// **The role record, elected on this pass when it is missing** (lane D5, as
/// gunnar's `become_twin` records its own role on every pass): a refused
/// election in [`boxside::Product::fed`] is tried again here from the kept
/// feed on every loop pass, and the step names the refusal instead of "not
/// recorded yet". `None` while the kept feed carries no election.
fn role_record(&self) -> Result<Option<server_lib::twin::RoleRecord>, String> {
if let Some(r) = server_lib::twin::recorded_role(&self.data_root)? {
return Ok(Some(r));
}
let Some(role) = kept_reply(&self.root).and_then(|r| role_for(&r, self.wireguard)) else {
return Ok(None);
};
let r = record_election(&self.data_root, &role)?;
log::warn!(
"pairing: elected `{}` by the installer — recorded in {} on the loop's pass",
if role.is_twin() { "twin" } else { "primary" },
self.data_root.display()
);
Ok(Some(r))
}
}
impl Steps for HolgerSteps {
/// The primary is ready once the installer's election is holger's role
/// record ([`boxside::Product::fed`] above writes it); holger has no admin
/// key to install from the feed.
fn primary_ready(&self, _feed: &BTreeMap<String, String>) -> Result<bool, String> {
match self.role_record()? {
// The main's last step is its server's start (lane D10): never PAIRED before it serves.
Some(r) if !r.role.is_twin() => match (self.server)() {
None => Ok(true),
Some(why) => Err(why),
},
Some(r) => Err(format!("the installer elected this box primary, and its role record (elected {} by {}) says twin — a role is a bootstrap fact", r.elected_at, r.elected_by)),
None => Ok(false),
}
}
/// **Become the twin, LIVE** (holger-twin-mtls): the installer's election is
/// already this box's role record ([`boxside::Product::fed`]); this switches
/// the RUNNING server to it — every door refuses writes by name from here —
/// and starts the follow, without a restart. Idempotent. Transient until the
/// server has started and the record is written.
fn become_twin(&self, primary: Ipv4Addr) -> Result<(), String> {
// The record first: a refused election is said by its code on every pass.
let mut record = self
.role_record()?
.ok_or("the installer's election is not recorded yet")?;
// An estate box follows its primary over wg0 (lane E1): the loop names the
// primary's utility address, the twin dials the same box's overlay address.
let primary = match server_lib::tunnel_addr::wg_only()
.then(|| kept_reply(&appliance_root()))
.flatten()
{
Some(reply) => over_the_tunnel(primary, &reply),
None => primary,
};
let Some(live) = server_lib::twin_follow::live() else {
return Err(not_started(boot_wait().as_deref()));
};
let mut named = record.role.primary().unwrap_or_default().to_string();
// A twin whose record names another primary than the installer does is
// RE-POINTED, never refused: the installer knows where the primary lives now
// (a main recharged at new addresses; a lane that named its bind address).
if let Some(to) = repoint_to(&record.role, primary) {
record = server_lib::twin::repoint(
&self.data_root,
&to,
server_lib::clock::now_unix_secs(),
)?;
log::warn!("pairing: the installer's primary is `{to}` and this twin's record named `{named}` — re-pointed");
named = to;
}
if !record.role.is_twin() || !named.starts_with(&format!("{primary}:")) {
return Err(format!("the pairing loop says twin of {primary}, and this box's role record says `{}` of `{named}` — a role is a bootstrap fact", if record.role.is_twin() { "twin" } else { "primary" }));
}
if !live.posture().is_twin() {
live.posture().become_twin(record.role.clone());
log::warn!("pairing: this box is now a TWIN of `{named}`, live — every write refused by name, the follow started");
}
live.start(&record.role).map(|_| ())
}
/// **Certified?** — the FRONT certifies (BecomeTwin → TwinCsr →
/// IssueTwinCertificate → InstallTwinCertificate against the primary's own
/// issuer); the twin sees it took when its credential is installed.
fn certified(&self, _primary: Ipv4Addr) -> Result<bool, String> {
Ok(
server_lib::twin_tls::load(&self.data_root, server_lib::twin_tls::Purpose::Twin)?
.is_some(),
)
}
/// `(applied, head)` in holger's unit — a whole follow cycle over the
/// primary's replication door: equal (PAIRED) once the last cycle that
/// reached the primary was whole, and it went over mutual TLS.
fn follow_state(&self) -> Result<(u64, u64), String> {
let live =
server_lib::twin_follow::live().ok_or_else(|| not_started(boot_wait().as_deref()))?;
let (complete, why, last_whole) = live.posture().completeness();
let how = live.posture().follow();
if !complete {
return Err(format!("the twin is not caught up: {why}"));
}
if !how.starts_with("mtls ") {
return Err(format!(
"the twin follows `{how}`, not over the primary's replication door"
));
}
Ok((last_whole as u64, last_whole as u64))
}
}
/// **The seed's tunnel for this box**: WireGuard on the front's relay with the
/// key the ceremony keeps under `<root>/wg` — the key this box PRESENTED
/// ([`boxside::Product::tunnel_pubkey`]), so the relay knows it by it.
fn tunnel_factory(root: PathBuf) -> boxloop::TunnelFactory {
Box::new(move |seed, feed| {
let driver = match seed.tunnel {
TunnelKind::Wireguard => {
let id = wg_appliance_core::ceremony::load_or_mint_identity(&root.join("wg"))
.map_err(|e| {
nordisk_pairing::tunnel::TunnelError::Failed(format!(
"wg0: this box's key: {e}"
))
})?;
// The link to the front carries the PSK the installer fed (lane G5, A7 row 13;
// Gunnar 356e8dd2); a value that is not a key is refused by name. A re-fed PSK is
// set by the loop's doors (`BoxDoors`), and a front recharge re-points wg0 at once.
let psk = feed
.get(nordisk_pairing::installer::FEED_WG_PSK)
.map(String::as_str);
Box::new(
nordisk_pairing::wireguard::WireGuard::box_via_front_with_psk(id.private, psk)?,
) as Box<dyn nordisk_pairing::tunnel::Tunnel>
}
_ => nordisk_pairing::tunnel::driver_for(seed, feed)?,
};
// The tunnel coming up asks the fed clock at once (Gunnar 1115a4e6).
Ok(Box::new(ClockOnTunnelUp::new(driver, clock_tunnel_up))
as Box<dyn nordisk_pairing::tunnel::Tunnel>)
})
}
// ── PID 1 ────────────────────────────────────────────────────────────────────
/// The net spec holger reads when it is PID 1 (the kernel command line hands
/// `HOLGER_NET=…` to init as an environment variable).
pub const NET_ENV: &str = "HOLGER_NET";
/// A box with no `HOLGER_NET`: the cloud shape — the public NIC owns the
/// default route, the utility NIC (where the installer's link and the twin's
/// primary are) is address-only.
pub const DEFAULT_NET: &str = "eth0:dhcp,eth1:dhcp";
/// The DHCP T1 test seam (honoured only on a korp-started kernel).
pub const DHCP_T1_ENV: &str = "HOLGER_DHCP_T1_OVERRIDE_SECS";
/// Pure: the plans for `HOLGER_NET` (`None`/blank = [`DEFAULT_NET`]).
pub fn net_plans(spec: Option<&str>) -> Result<Vec<appliance_pid1::net::NetConfig>, String> {
let spec = spec
.map(str::trim)
.filter(|s| !s.is_empty())
.unwrap_or(DEFAULT_NET);
appliance_pid1::net::NetConfig::parse_list(spec)
.map_err(|e| format!("REFUSED [holger-net] {NET_ENV}: {e:#}"))
}
/// **The admin view's Logs pane**, pure over the log: the door (`install` until
/// the installer closed the box, then `log-door` — the same listener answering
/// only `GET /install/log` to the installer's certificate), the counts, and the
/// newest `n` lines, oldest first.
pub fn admin_logs(
log: &nordisk_pairing::bootlog::BootLog,
closed: bool,
n: usize,
) -> server_lib::grpc::holger_proto::AdminLogs {
let total = log.total();
let chunk = log.since(total.saturating_sub(n as u64), n);
server_lib::grpc::holger_proto::AdminLogs {
present: true,
door: if closed { "log-door" } else { "install" }.into(),
closed,
lines_total: total,
lines_dropped: log.dropped(),
tail: chunk.lines,
}
}
/// Is a seed staged — is this a box of an installer-built estate?
pub fn seed_staged() -> bool {
appliance_root().join(SEED_REL).exists()
}
/// **The boot log from the first line** (called before the logger): the ESP's
/// story, then — through [`nordisk_pairing::bootlog::Tap`] — every line holger
/// logs, onto the log the installer collects. Only on a seeded box.
pub fn log_target() -> Option<env_logger::Target> {
if !seed_staged() {
return None;
}
BOOT_LOG.capture_esp(Path::new(nordisk_pairing::bootlog::ESP_BOOTLOG), "holger");
Some(env_logger::Target::Pipe(Box::new(
nordisk_pairing::bootlog::Tap::new(&BOOT_LOG, std::io::stderr()),
)))
}
/// **What holger owes the system when it IS PID 1**, before anything binds:
/// the reaper and — on a seeded box — its network (loopback, then every entry
/// of [`NET_ENV`] or [`DEFAULT_NET`], DHCP leases kept alive). Returns whether
/// this process is PID 1 (the caller then stops on SIGTERM, which a PID 1
/// without a handler never receives). Not PID 1: nothing, `false`. Must run
/// inside the tokio runtime (the reaper and the renewals are tasks).
/// The name the boot proves the published resolver on (A7 row 29).
pub const RESOLVER_PROBE: &str = "crates.io";
pub fn pid1_duties() -> Result<bool, String> {
if !appliance_pid1::net::is_init() {
return Ok(false);
}
// A32-14, stays a task: PID 1's zombie reaper, for the box's life.
tokio::spawn(appliance_pid1::runtime::reaper());
// Whether this /data survived the last install (korp-installer's record on the ESP), once.
appliance_pid1::install::announce_install_mode(None, &|l| log::info!("{l}"));
// ★ THE NETWORK COMES UP WITH OR WITHOUT A SEED (lane B1), as Gunnar's PID 1 does
// (gunnar-server serve.rs: `boot::bring_up_network()` before anything binds, no seed
// condition; a DHCP timeout is not fatal). It used to return here on a seedless box —
// "no network is brought up here" — so a seedless main served on no interface at all:
// `cargo xtask installtest` (holger 62fc981d) saw `STAGE serving` and a door that never
// answered for 1232 s.
if !seed_staged() {
log::warn!("pid1: holger is PID 1 with no pairing seed — the declared interfaces come up all the same");
}
let plans = net_plans(std::env::var(NET_ENV).ok().as_deref())?;
// The resolver proof (A7 row 29): crates.io is what a holger mirror reaches first; a
// miss is asked again for about 30 s in the background.
let how = appliance_pid1::net::Bringup {
hostname_prefix: "holger",
resolver_probe: Some(RESOLVER_PROBE),
narrate: &narrate_boot,
};
// A boot DHCP that failed is asked again in the background (edda); its late lease is said
// and renewed as the boot's own (G6 follow-up, lane B4).
let sessions = appliance_pid1::net::bring_up_network_with(plans, &how, pid1_late_lease())
.map_err(|e| format!("REFUSED [holger-net] {e:#}"))?;
let t1 = appliance_pid1::runtime::dhcp_t1_override(DHCP_T1_ENV);
for s in sessions {
log::info!(
"pid1: {} leased {}/{} — kept alive from here",
s.interface,
s.lease.address,
s.lease.prefix
);
// A32-14, stays a task: PID 1's lease renewal, one per lease, for the box's life.
tokio::spawn(appliance_pid1::runtime::dhcp_renewal(s, t1));
}
Ok(true)
}
/// The kernel's story and a network line into the boot log (edda `narration::ship_boot_narration`).
pub fn narrate_boot(note: &str) {
appliance_pid1::narration::ship_boot_narration(note, &|l| BOOT_LOG.push(l));
}
/// **The late lease of the box's PID 1** (edda `net::LateLease`): its line narrated into the
/// boot log as the boot's own, its renewal on Holger's T1 seam ([`DHCP_T1_ENV`]).
pub fn pid1_late_lease() -> appliance_pid1::net::LateLease {
appliance_pid1::net::LateLease {
narrate: narrate_boot,
t1_override_var: Some(DHCP_T1_ENV),
}
}
/// **Holger's PID-1 fault codes** (A7 row 17, Gunnar's `GN-01..05`): a rung refuses BY
/// the code, so codes are added, never renumbered.
pub const FAULT_CODES: [(&str, &str); 4] = [
(
"HL-01",
"PID 1's duties failed: the declared network could not be brought up",
),
(
"HL-02",
"holger-server could not start: its configuration or its start was refused",
),
("HL-05", "holger-server stopped serving with an error"),
(PANIC_CODE, PANIC_MEANING),
];
/// **Holger's panic code** (lane G6, Gunnar's `GN-99`): the server's and the front's.
pub const PANIC_CODE: &str = "HL-99";
/// What [`PANIC_CODE`] means.
pub const PANIC_MEANING: &str = "the app panicked";
/// The panic as edda's fault: Holger's code, the crash record under `data`.
pub fn panic_fault(data: Option<PathBuf>) -> appliance_pid1::fault::PanicFault {
appliance_pid1::fault::PanicFault {
code: PANIC_CODE,
meaning: PANIC_MEANING,
data,
}
}
/// The narration a panic's crash record holds: the boot log's tail, as [`fault_end`]'s.
pub fn narration() -> Vec<u8> {
BOOT_LOG.since(0, 4000).lines.join("\n").into_bytes()
}
/// **A panic on the appliance ends as a fault** (lane G6, Gunnar's
/// `boot::install_panic_fault`): edda's hook, `HL-99`, the crash record on the data disk
/// with the boot log's tail, then a serving box restarts (unless it faulted within 30 min)
/// and a booting one powers off. Armed only on the appliance (PID 1 booted by korp), never
/// in a test, on a laptop or in a container. Every line is said on the console and logged.
pub fn install_panic_fault() {
appliance_pid1::fault::narration_from(narration);
appliance_pid1::fault::install_panic_fault(
panic_fault(Some(appliance_root())),
appliance_pid1::stop::on_appliance,
|l: &str| {
eprintln!("holger: {l}");
log::error!("{l}");
log::logger().flush();
},
);
}
/// The product name of Holger's stop record on the ESP (`/boot/holger-last-stop.txt`).
pub const STOP_RECORD_PRODUCT: &str = "holger";
/// Pure: **the code an `Err` at `stage` carries** (the last stage reached), and its meaning.
pub fn fault_code(stage: &str) -> (&'static str, &'static str) {
let i = match stage {
"exec" => 0,
"serving" | "follow" => 2,
_ => 1,
};
FAULT_CODES[i]
}
/// **An `Err` that would end PID 1 ends through edda** (`appliance_pid1::fault::end`):
/// the code by the last stage reached, the crash record on the data disk, then a serving
/// box restarts (unless it faulted within 30 min) and a booting box powers off. Never
/// returns. Holger has no fault ear, so no ack is waited on.
pub fn fault_end(why: &str) -> ! {
let (stage, serving) = appliance_pid1::fault::progress();
let (code, meaning) = fault_code(stage);
let data = appliance_root();
// The narration the panic hook was given ([`install_panic_fault`]: [`narration`]).
let narration = appliance_pid1::fault::narration_now();
let f = appliance_pid1::fault::Fault {
code,
meaning: Some(meaning),
stage,
serving,
reason: why,
data: data.is_dir().then_some(data.as_path()),
narration: &narration,
};
log::logger().flush();
appliance_pid1::fault::end(
&f,
&|l| {
log::error!("{l}");
log::logger().flush();
},
None,
)
}
/// **A clean stop's end on the appliance** (A7 row 18): the data disk quiesced (each step
/// bounded at 10 s and said), the stop kept on the ESP, then the power with no sync after
/// it — or the re-exec into a staged live update before any of that.
pub fn power_off_cleanly(said: &str) -> ! {
log::info!("{said} — the data disk is synced and remounted read-only, then the power goes");
log::logger().flush();
let data = appliance_root();
#[cfg(target_os = "linux")]
holger_live::appliance::end_of_stop(|| -> () {
appliance_pid1::power::power_off_cleanly(STOP_RECORD_PRODUCT, &data, said, &|l| {
eprintln!("holger: {l}")
})
});
#[cfg(not(target_os = "linux"))]
appliance_pid1::power::power_off_cleanly(STOP_RECORD_PRODUCT, &data, said, &|l| {
eprintln!("holger: {l}")
});
#[allow(unreachable_code)]
loop {
std::thread::sleep(std::time::Duration::from_secs(3600));
}
}
/// How long one look at a door may take before the door counts as not serving.
pub const DOOR_LOOK: std::time::Duration = std::time::Duration::from_secs(2);
/// Pure: **the addresses this box's own doors answer at** — every exposed endpoint's gRPC and
/// HTTP listener as the start bound it (an estate box's on its overlay address), a wildcard bind
/// looked at on loopback. An address that does not parse is left out.
pub fn own_doors<'a>(
listeners: impl IntoIterator<Item = (&'a str, Option<&'a str>)>,
) -> Vec<std::net::SocketAddr> {
let mut out = Vec::new();
for (grpc, http) in listeners {
for at in std::iter::once(grpc).chain(http) {
let Ok(mut a) = at.trim().parse::<std::net::SocketAddr>() else {
continue;
};
if a.ip().is_unspecified() {
a.set_ip(if a.is_ipv4() {
std::net::Ipv4Addr::LOCALHOST.into()
} else {
std::net::Ipv6Addr::LOCALHOST.into()
});
}
if !out.contains(&a) {
out.push(a);
}
}
}
out
}
/// **Whether every door of this box answers now** (a blocking look): each takes a connection
/// within `within`. No door at all is not serving.
pub fn doors_answer(doors: &[std::net::SocketAddr], within: std::time::Duration) -> bool {
!doors.is_empty()
&& doors
.iter()
.all(|a| std::net::TcpStream::connect_timeout(a, within).is_ok())
}
/// **The box's health on trial** (lane W9; the front's own trial looks at its web face the same
/// way): the app serves when every door of it takes a connection. A new app whose doors never
/// answer is never confirmed, and the trial's look at `live::CONFIRM_DEADLINE` rolls it back.
pub fn box_serving(doors: Vec<std::net::SocketAddr>) -> holger_live::trial::Serving {
Box::new(move || doors_answer(&doors, DOOR_LOOK))
}
/// The doors the trial looks at, known once the declaration is read ([`trial_serves_on`]).
static TRIAL_DOORS: std::sync::OnceLock<Vec<std::net::SocketAddr>> = std::sync::OnceLock::new();
/// **Arm the live trial at the app's first line** (lane B3, A9 row 6; korp `live::arm_trial`):
/// on a korp box whose slot runs on trial, the `live.trial` jobs start NOW, on the appliance's
/// own ledger (`/data/live/jobs`, no declaration needed), with the clock started here. Until
/// [`trial_serves_on`] names the doors the app does not serve, so a start that never gets to
/// serving (a console token that never comes, a declaration refused for ever) is rolled back
/// at `CONFIRM_DEADLINE`, never only at the next reboot. Before pairing and before `boot_server`.
pub fn arm_trial() {
let armed = std::time::Instant::now();
#[cfg(target_os = "linux")]
{
if !holger_live::appliance::present() {
return;
}
let slot = Arc::new(holger_live::appliance::slot());
if !slot.on_trial() {
return;
}
log::warn!("live update: this app runs on TRIAL, armed at its first line — it confirms after serving {} s, or is rolled back {} s from now (jera job {})", holger_live::CONFIRM_AFTER.as_secs(), holger_live::CONFIRM_DEADLINE.as_secs(), holger_live::trial::KIND_TRIAL);
let jobs = appliance_root().join(holger_live::trial::JOBS_REL);
let serving: holger_live::trial::Serving = Box::new(|| {
TRIAL_DOORS
.get()
.is_some_and(|d| doors_answer(d, DOOR_LOOK))
});
let roll_back: holger_live::trial::RollBack = Box::new(|| {
log::error!(
"live update: the rollback re-exec failed: {}",
holger_live::appliance::reexec_now()
)
});
if holger_live::trial::start_armed(slot, serving, &jobs, roll_back, armed).is_none() {
log::error!("live update: the trial's jobs did not start ({}): the slot is not confirmed — the next start rolls it back", jobs.display());
}
}
let _ = armed;
}
/// **The app serves on these doors** (every listener bound): the armed trial looks at them
/// from now on ([`arm_trial`]).
pub fn trial_serves_on(doors: Vec<std::net::SocketAddr>) {
let _ = TRIAL_DOORS.set(doors);
}
// ── the boot narration (lane B3, A9 row 4) ──────────────────────────────────────────────
static NARRATOR: std::sync::OnceLock<Arc<nordisk_pairing::narrator::Narrator>> =
std::sync::OnceLock::new();
/// The box's narrator: every line into the boot log (the log door and DEV-NARRATE read it)
/// and onto the console, never through the logger's WARN filter. On a seeded box it ticks
/// once a minute with the current stage, so silence means dead.
pub fn narrator() -> &'static Arc<nordisk_pairing::narrator::Narrator> {
NARRATOR.get_or_init(|| {
let n = Arc::new(nordisk_pairing::narrator::Narrator::new(|l: &str| {
BOOT_LOG.push(l);
eprintln!("holger: {l}");
}));
if seed_staged() {
nordisk_pairing::narrator::start_ticks(Arc::clone(&n));
}
n
})
}
/// **Cross a boot boundary** (`exec`, `network`, `pairing`, `serving`, `follow`, …): the STAGE
/// line on every reader, and edda's PID 1 told the stage a fault is said at.
pub fn stage(token: &'static str, detail: &str) {
narrator().at(token, detail);
if token == "serving" {
appliance_pid1::fault::serving_now();
} else {
appliance_pid1::fault::reached(token);
}
}
// ── the start-up disk test (lane B3, A9 row 5) ──────────────────────────────────────────
static DISKTEST: std::sync::OnceLock<(PathBuf, Arc<nordisk_pairing::elect::disktest::DiskTest>)> =
std::sync::OnceLock::new();
/// **Start the disk test on `data_root`** (once): the election reads its MiB/s, and `hello`
/// waits for it (at most `HOLD_HELLO`), as Gunnar's does.
pub fn start_disktest(data_root: &Path) {
DISKTEST.get_or_init(|| {
(
data_root.to_path_buf(),
nordisk_pairing::elect::disktest::DiskTest::start(
data_root.to_path_buf(),
|l: &str| log::warn!("{l}"),
),
)
});
}
/// **The box's live slot on trial** (L20; a jera job since L21): called once this app serves —
/// every listener bound. The `live.trial` jobs were started at the app's first line
/// ([`arm_trial`]); this names the doors their looks probe, and the slot is confirmed after
/// serving [`holger_live::CONFIRM_AFTER`].
pub fn watch_trial(installed: &Installed<HolgerBox>, doors: Vec<std::net::SocketAddr>) {
// The trial was armed at the app's first line ([`arm_trial`], lane B3); from here its
// looks see these doors.
let _ = installed;
trial_serves_on(doors);
}
/// **Start** — iff a seed is staged. Reads it, clears the pairing state on the
/// first boot after an installation, opens the box and serves the installer's
/// listener (unless the installer closed it on this installation). Returns what
/// was started, for the caller to keep; errors are refusals by name.
/// The pairing a later try opened (lane B1), kept for the process's life.
static LATE_PAIRING: std::sync::OnceLock<Arc<Installed<HolgerBox>>> = std::sync::OnceLock::new();
/// **[`start`], and a refusal that does not end the box** (lane B1). A seed that cannot be
/// read — a release medium's metadata that gave none (`[pairing-seed-metadata]`), a staged
/// seed no reader parses (`[pairing-seed]`) — used to end PID 1 through `?`, before anything
/// could fetch the log. Now it is said, the box goes on without pairing, and the pairing is
/// tried again in the background with [`start_backoff`]; the installer's listener opens on the
/// try that reads a seed.
pub fn start_or_retry(data_root: &Path) -> Option<Arc<Installed<HolgerBox>>> {
match start(data_root) {
Ok(p) => p,
Err(e) => {
log::error!("pairing: {e} — this box stays up, serves what it can and tries the pairing again in {} s", start_backoff(1).as_secs());
if let Err(e) = retry_pairing(data_root.to_path_buf(), start, start_backoff) {
log::error!("ALERT {PAIRING_RETRY} the pairing is NOT tried again ({e}): restart the box to try it");
}
None
}
}
}
pub fn start(data_root: &Path) -> Result<Option<Arc<Installed<HolgerBox>>>, String> {
let root = appliance_root();
// A release medium stages only the metadata marker: its seed is the shop's, in user_data.
// The ONE seed reader (Skidbladnir nordisk-pairing `seed::metadata`, P4a; A28 L12).
let Some(seed) = nordisk_pairing::seed::metadata::read(&root.join(SEED_REL))? else {
return Ok(None);
};
if seed.kind != Kind::Holger {
return Err(format!(
"REFUSED [pairing-seed-kind] the staged seed is for a `{:?}` box; this is holger",
seed.kind
));
}
match first_boot(Path::new("/boot"), &root, &seed) {
Some(c) if c.is_empty() => log::info!("pairing: a keep boot of the same estate onto its kept data — the installer's feed, this box's key and certificate are KEPT"),
Some(cleared) => log::warn!("pairing: first boot after an installation — cleared {cleared:?}"),
None => {}
}
// The box's own live slot (L20): on a korp appliance, its A/B app slots.
#[cfg(target_os = "linux")]
let live = holger_live::appliance::present().then(|| Arc::new(holger_live::appliance::slot()));
#[cfg(not(target_os = "linux"))]
let live = None;
// The start-up disk test (lane B3): measured on the idle box, read by the election.
start_disktest(data_root);
let product = HolgerBox {
root: root.clone(),
data_root: data_root.to_path_buf(),
tunnel: seed.tunnel,
live,
};
let installed = Arc::new(Installed::open(&root, seed.clone(), product)?);
// A WireGuard seed is an estate box: WireGuard-only from here on (lane E1).
if server_lib::tunnel_addr::estate_is_wg_only(Some(seed.tunnel == TunnelKind::Wireguard)) {
let kept = installed.latest();
set_estate_posture(kept.as_ref().map(|r| r.me.overlay), kept.as_ref());
}
// A restart of a box fed before: its clock follows the front from the kept
// feed at once (the installer feeds a running box, never a restarted one).
if let Some(reply) = installed.latest() {
clock_fed(&reply.feed);
}
// The admin view's Logs pane reads this box's boot log and its door
// (`server_lib::admin_view`): the library does not own the log, this does.
let door_state = installed.clone();
server_lib::admin_view::set_log_door(move |n| admin_logs(&BOOT_LOG, door_state.closed(), n));
// The installer's listener on every start: the install until the installer
// closes it, and the log door for the box's whole life — also after a
// restart of a closed box (nordisk-pairing serve_installer_after_close), so
// `estate logs` reads this box after a reboot, a recharge or a grow.
// Each listener line at its level (nordisk-pairing's Tone, Gunnar 3eb19cd3): a refusal is
// a WARN, the listener doing its job (the close, the log door, a reader going away) an
// INFO — the untoned serve said every line at WARN.
let say: boxside::Say = Arc::new(|t: nordisk_pairing::bootstrap::Tone, l: &str| match t {
nordisk_pairing::bootstrap::Tone::Info => log::info!("installer: {l}"),
nordisk_pairing::bootstrap::Tone::Warn => log::warn!("installer: {l}"),
});
// The installer sees the box with its renewal steps (lane E2): the certificate the front
// renews at two thirds of its life is kept, and so is the fresh bootstrap handed with it.
// ... and its pair status carries the server's start (lane D10): never PAIRED before it serves.
let served: Arc<dyn nordisk_pairing::bootstrap::Vm> = Arc::new(
crate::renewing::Renewing::new(installed.clone(), &root, SEED_REL, &seed)
.with_status(Arc::new(status_now)),
);
watch_status(installed.clone());
if installed.closed() {
log::info!(
"pairing: the installer closed this box on an earlier start — the log door only"
);
boxside::serve_installer_after_close_toned(served, seed.clone(), &BOOT_LOG, say)
.map_err(|e| format!("the installer's log door thread: {e}"))?;
} else {
boxside::serve_installer_toned(served, seed.clone(), &BOOT_LOG, say)
.map_err(|e| format!("the installer's listener thread: {e}"))?;
}
// The box's own certificate, read live by the health words (lane E2).
let own = root.clone();
server_lib::cert_health::set_pairing_cert(move || {
std::fs::read_to_string(own.join(boxside::OWN_CERT_REL))
.ok()
.filter(|c| c.contains("BEGIN CERTIFICATE"))
});
// The loop runs on every start, closed or not: a box that reboots after its
// install raises wg0 again from what it was fed and says PAIRED again.
let (i, steps) = (
installed.clone(),
HolgerSteps {
root: root.clone(),
data_root: data_root.to_path_buf(),
wireguard: seed.tunnel == TunnelKind::Wireguard,
server: primary_waits_now,
},
);
let say: Arc<dyn Fn(&str) + Send + Sync> = Arc::new(|l: &str| log::warn!("{l}"));
boxloop::start(
root.clone(),
seed,
move || BoxDoors::new(i, steps, tunnel_factory(root)),
say,
)
.map_err(|e| format!("the pairing loop's thread: {e}"))?;
Ok(Some(installed))
}
#[cfg(test)]
mod d7_boot_wait {
/// ★ A16 (C19): **a sold estate's provider is the fed `console_provider`**, an `https://` site;
/// anything else names none.
#[test]
fn a_sold_estates_provider_is_the_fed_console_provider() {
let fed = |v: &str| {
std::collections::BTreeMap::from([(super::PROVIDER_FEED.to_string(), v.to_string())])
};
assert_eq!(
super::provider_of_feed(&fed("https://holger.rs")),
Some("https://holger.rs".to_owned())
);
assert_eq!(super::provider_of_feed(&fed("http://holger.rs")), None);
assert_eq!(
super::provider_of_feed(&std::collections::BTreeMap::new()),
None
);
}
use super::*;
/// test3-holger-rs 2026-10-22: the pair status names the boot's wait WITH its start, in
/// words no log filter withholds (the box's redaction dropped the old words, which named the
/// console's token file, whole: the installer read nothing for 40 minutes).
#[test]
fn the_pair_status_names_the_boots_wait_with_its_start_and_no_filter_withholds_it() {
let w = holger_live::waits::Wait::at(
"this box (primary)",
CONSOLE_KEY_WAIT,
1_792_692_725,
std::time::Instant::now(),
);
let s = nordisk_pairing::signal::Signal {
name: "vm2".into(),
tunnel: nordisk_pairing::seed::TunnelKind::Wireguard,
tunnel_up: true,
joined: true,
role: Some(Role::Primary),
step: PRIMARY_STEP.into(),
tries: 0,
last_transient: None,
paired: false,
refused: None,
};
let line = with_server(s, &ServerNow::default(), Some(&w.words())).line();
assert!(
line.contains(
"waiting for the server: the boot waits for the front's offered console key"
) && line.contains("since 2026-10-22T18:12:05Z"),
"{line}"
);
assert_eq!(
nordisk_pairing::redact::secret_class(&format!("pair status:{line}")),
None,
"{line}"
);
for what in [CONSOLE_KEY_WAIT, TUNNEL_WAIT] {
assert_eq!(nordisk_pairing::redact::secret_class(what), None, "{what}");
}
}
/// test3-holger-rs on r6: the step said only "the server has not started yet" for
/// an hour while the boot waited for the console's bearer.
#[test]
fn a_step_that_needs_the_server_names_what_the_boot_waits_for() {
let said = not_started(Some("the installer to feed `holger_console_token` (/data/holger-console-token is declared and not written yet)"));
assert!(said.starts_with("the server has not started yet"), "{said}");
assert!(
said.contains("the boot waits for the installer to feed `holger_console_token`"),
"{said}"
);
assert_eq!(
not_started(None),
"the server has not started yet: the twin's role is switched on the running doors"
);
}
}
#[cfg(test)]
mod tests {
/// ★ A seedless PID 1 brings its network up (lane B1; Gunnar serve.rs brings it up with
/// no seed condition). Read off the source: nothing in `pid1_duties` may return between
/// the seed check and `bring_up_network_with`.
#[test]
fn a_seedless_pid1_still_brings_its_network_up() {
let src = include_str!("paired.rs");
let body = &src[src.find("pub fn pid1_duties()").unwrap()..];
let body = &body[..body
.find("bring_up_network_with(")
.expect("pid1_duties brings the network up")];
let after_seed = &body[body
.find("seed_staged()")
.expect("the seed is still looked at")..];
assert!(
!after_seed.contains("return Ok(true)"),
"a seedless PID 1 returns before its network:\n{after_seed}"
);
}
/// ★ Lane P1: **the shop's first-login invite in the feed** — its hash (lower-cased), its
/// lifetime and the buyer it is pinned to; a feed without a hash or a buyer carries none,
/// and a lifetime that is no number is the shop's default.
#[test]
fn the_feed_carries_the_shops_first_login_invite_for_its_buyer() {
let h = "AB".repeat(32);
let feed = BTreeMap::from([
(FIRST_LOGIN_FEED.to_string(), h.clone()),
(FIRST_LOGIN_TTL_FEED.to_string(), "3600".to_string()),
(BUYER_FEED.to_string(), " acme ".to_string()),
]);
assert_eq!(
fed_first_login(&feed),
Some(("ab".repeat(32), 3600, "acme".to_string()))
);
let mut no_ttl = feed.clone();
no_ttl.insert(FIRST_LOGIN_TTL_FEED.to_string(), "soon".into());
assert_eq!(fed_first_login(&no_ttl).unwrap().1, FIRST_LOGIN_TTL_DEFAULT);
let mut nobody = feed.clone();
nobody.remove(BUYER_FEED);
assert_eq!(fed_first_login(&nobody), None);
assert_eq!(fed_first_login(&BTreeMap::new()), None);
}
use super::*;
/// ★ The lean estate's memory proof: the server's resident set, its peak, and the box's
/// memory, one line; a file that lacks a field says nothing.
#[test]
fn the_memory_line_says_the_servers_rss_its_peak_and_the_boxs_headroom() {
let status = "Name:\tholger\nVmHWM:\t 412340 kB\nVmRSS:\t 301000 kB\n";
let meminfo =
"MemTotal: 2014000 kB\nMemFree: 100 kB\nMemAvailable: 1500000 kB\n";
assert_eq!(
memory_line(status, meminfo).unwrap(),
"memory: holger-server rss 293 MiB, peak 402 MiB; box 1966 MiB, available 1464 MiB"
);
assert_eq!(memory_line("VmRSS: 1 kB\n", meminfo), None);
}
use nordisk_pairing::join::Assigned;
/// ★ G3 (Gunnar f2e602dd, A7 row 11): `clock_fed` started ONE run per process, so a re-laid
/// box re-fed the SAME clock after its boot sync failed asked nobody for six hours. Now the
/// first call (the boot sync), a new clock, and a repeat while the clock is not right each
/// start a run; a repeat once the clock is right does not.
#[test]
fn a_repeated_clock_feed_starts_a_fed_run_until_the_clock_is_right() {
use server_lib::clock::Outcome;
assert!(
clock_feed_syncs(true, false, false, None),
"the first call in a process is the boot sync"
);
assert!(
clock_feed_syncs(false, true, true, Some(Outcome::AlreadyRight)),
"a new clock always syncs"
);
for unset in [
None,
Some(Outcome::NoQuorum),
Some(Outcome::Disagree),
Some(Outcome::ApplyFailed),
] {
assert!(
clock_feed_syncs(false, false, true, unset),
"the same clock re-fed after {unset:?} syncs again"
);
}
for right in [Outcome::Stepped, Outcome::Slewed, Outcome::AlreadyRight] {
assert!(
!clock_feed_syncs(false, false, true, Some(right)),
"a clock set right ({right:?}) is not asked again by a repeat"
);
}
assert!(
!clock_feed_syncs(false, false, false, Some(Outcome::NoQuorum)),
"a feed with no clock starts nothing"
);
}
static TUNNEL_UPS: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
fn count_tunnel_up() {
TUNNEL_UPS.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
}
/// ★ G3 (Gunnar 1115a4e6, A7 row 3): the pairing loop's tunnel says when it is up, so the
/// fed clock is asked at once rather than after the fed run's pause. Down says nothing.
#[test]
fn the_pairing_tunnel_says_when_it_comes_up() {
use nordisk_pairing::tunnel::{Identity, NoTunnel, Tunnel as _};
let mut t = ClockOnTunnelUp::new(Box::new(NoTunnel::default()), count_tunnel_up);
assert!(!t.status().up);
assert_eq!(
TUNNEL_UPS.load(std::sync::atomic::Ordering::SeqCst),
0,
"a tunnel not up asks nothing"
);
t.up(
&Identity {
overlay: Ipv4Addr::new(10, 44, 0, 3),
pubkey: String::new(),
},
&[],
)
.unwrap();
assert!(t.status().up);
assert_eq!(
TUNNEL_UPS.load(std::sync::atomic::Ordering::SeqCst),
1,
"up: the hook is called"
);
assert_eq!(t.kind(), TunnelKind::None, "the driver is the wrapped one");
assert_eq!(
t.set_preshared_key(None).ok(),
NoTunnel::default().set_preshared_key(None).ok(),
"a re-fed PSK reaches the wrapped driver"
);
}
/// ★ A twin re-fed with its recharged main's new address is the SAME role (a
/// re-point owed, said at WARN), never "a role is a bootstrap fact" at ERROR; a
/// twin fed `primary` (or the reverse) is still the refusal.
/// ★ A twin whose record names another primary (the recharged main's bind
/// address, or its old one) is re-pointed at the installer's; one that names it
/// already is left; a primary is never re-pointed.
#[test]
fn a_twin_is_re_pointed_at_the_installers_primary() {
use server_lib::twin::NodeRole;
let twin = |p: &str| NodeRole::Twin {
primary: p.into(),
poll_seconds: 60,
};
let at: Ipv4Addr = [10, 6, 0, 14].into();
assert_eq!(
repoint_to(&twin("0.0.0.0:50051"), at),
Some(format!("10.6.0.14:{}", nordisk_pairing::CONTROL_PORT))
);
assert_eq!(
repoint_to(&twin("10.6.0.12:50051"), at)
.as_deref()
.map(|s| s.starts_with("10.6.0.14:")),
Some(true)
);
assert_eq!(repoint_to(&twin("10.6.0.14:50051"), at), None);
assert_eq!(repoint_to(&NodeRole::Primary, at), None);
}
#[test]
fn a_twin_fed_a_moved_primary_keeps_its_role_and_a_flipped_role_is_refused() {
use server_lib::twin::NodeRole;
let twin = |p: &str| NodeRole::Twin {
primary: p.into(),
poll_seconds: 60,
};
assert!(same_role_moved_primary(
&twin("10.6.0.12:50051"),
&twin("10.6.0.14:50051")
));
assert!(
!same_role_moved_primary(&twin("10.6.0.12:50051"), &twin("10.6.0.12:50051")),
"unchanged: nothing to say"
);
assert!(
!same_role_moved_primary(&twin("10.6.0.12:50051"), &NodeRole::Primary),
"twin → primary is a flipped role"
);
assert!(
!same_role_moved_primary(&NodeRole::Primary, &twin("10.6.0.14:50051")),
"primary → twin is a flipped role"
);
}
/// ★ Gunnar f21d8d09 + 3eb19cd3 (2026-09-28/29), shared by lane G10: **the box's log keeps
/// its install for life, and the listener says each line at its level.** holger's
/// `BOOT_LOG` was `BootLog::new(DEFAULT_CAP)` — no head, so a box that talks for hours
/// lost its pairing lines — and the untoned `serve_installer` said every listener line at
/// WARN. nordisk-pairing 0.5.23 seals the head at the installer's close.
#[test]
fn the_boxs_log_keeps_its_head_and_the_listener_is_toned() {
let src = include_str!("paired.rs");
let code = &src[..src.find("#[cfg(test)]").unwrap()];
// Read with the whitespace out: rustfmt wraps a long line where it likes.
let flat: String = code.split_whitespace().collect();
assert!(
flat.contains("BootLog=nordisk_pairing::bootlog::BootLog::for_install();"),
"the log keeps its head"
);
assert!(
code.contains("boxside::serve_installer_toned(")
&& code.contains("boxside::serve_installer_after_close_toned("),
"the listener is toned"
);
assert!(
!code.contains("boxside::serve_installer(")
&& !code.contains("boxside::serve_installer_after_close("),
"no untoned serve"
);
let fresh = nordisk_pairing::bootlog::BootLog::for_install();
fresh.push("pairing: PAIRED");
fresh.seal_head();
for i in 0..(nordisk_pairing::bootlog::DEFAULT_CAP + 1) {
fresh.push(&format!("rpc {i}"));
}
assert_eq!(
fresh.since(0, 1).lines,
["pairing: PAIRED"],
"the head outlives the tail"
);
}
#[test]
fn the_admin_views_logs_are_the_newest_lines_and_the_door_says_its_state() {
let log = nordisk_pairing::bootlog::BootLog::new(3);
for l in ["one", "two", "three", "four"] {
log.push(l);
}
let open = admin_logs(&log, false, 2);
assert!(open.present);
assert_eq!(open.door, "install");
assert_eq!(open.tail, ["three", "four"], "the newest two, oldest first");
assert_eq!(
(open.lines_total, open.lines_dropped),
(4, 1),
"one line fell out of a ring of three"
);
let closed = admin_logs(&log, true, 100);
assert_eq!((closed.door.as_str(), closed.closed), ("log-door", true));
assert_eq!(
closed.tail,
["two", "three", "four"],
"never more than the ring holds"
);
}
pub(super) fn reply(role: Option<Role>) -> JoinReply {
JoinReply {
estate: "holger.test".into(),
me: Assigned {
name: "vm3".into(),
overlay: nordisk_pairing::OVERLAY_BOX_B,
},
cert_pem: None,
role,
primary_overlay: Some(nordisk_pairing::OVERLAY_BOX_A),
primary_utility: Some([10, 13, 8, 2].into()),
peers: Vec::new(),
feed: BTreeMap::new(),
}
}
/// A dead main laid again is RE-ADMITTED by the installer (nordisk-pairing
/// `installer::readmit`) and fed `recharge_from` = its twin's control plane:
/// the primary takes it as its own `recharge_from` (served nothing until the
/// lane filled it); nobody else does, and a blank value is no twin.
#[test]
fn a_readmitted_primary_takes_recharge_from_its_feed_and_nobody_else_does() {
let with = |role, v: &str| JoinReply {
feed: BTreeMap::from([(
nordisk_pairing::door::RECHARGE_FROM_FEED.to_string(),
v.to_string(),
)]),
..reply(role)
};
assert_eq!(
recharge_from_of(&with(Some(Role::Primary), " 10.13.8.3:50051 ")).as_deref(),
Some("10.13.8.3:50051")
);
assert_eq!(
recharge_from_of(&with(Some(Role::Twin), "10.13.8.3:50051")),
None,
"a twin is never filled from anybody"
);
assert_eq!(
recharge_from_of(&with(Some(Role::Primary), " ")),
None,
"blank is no twin"
);
assert_eq!(
recharge_from_of(&reply(Some(Role::Primary))),
None,
"a first install feeds none"
);
let root =
std::env::temp_dir().join(format!("holger-paired-recharge-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&root);
assert_eq!(fed_recharge_from_at(&root), None, "never fed");
std::fs::create_dir_all(root.join("pairing")).unwrap();
std::fs::write(
root.join(nordisk_pairing::boxside::JOINED_REL),
with(Some(Role::Primary), "10.13.8.3:50051")
.render()
.unwrap(),
)
.unwrap();
assert_eq!(
fed_recharge_from_at(&root).as_deref(),
Some("10.13.8.3:50051"),
"read off the kept feed, as the boot does"
);
let _ = std::fs::remove_dir_all(&root);
}
/// ★ **What the box was fed is SEALED at rest, and every reader of the box opens it** (lane
/// G5, A7 row 21; Gunnar 0a038157): the boot's `recharge_from`, the estate name, the
/// passkey plane and the tunnel wait read the sealed `joined.toml` as they read a plain one.
#[test]
fn every_reader_of_the_box_opens_its_sealed_feed() {
let root = tempfile::tempdir().unwrap();
let mut r = reply(Some(Role::Primary));
r.feed.insert(
nordisk_pairing::door::RECHARGE_FROM_FEED.into(),
"10.13.8.3:50051".into(),
);
nordisk_pairing::fed_at_rest::write(
root.path(),
nordisk_pairing::boxside::JOINED_REL,
&r.render().unwrap(),
)
.unwrap();
assert!(nordisk_pairing::fed_at_rest::is_sealed(
&std::fs::read(root.path().join(nordisk_pairing::boxside::JOINED_REL)).unwrap()
));
assert_eq!(
fed_recharge_from_at(root.path()).as_deref(),
Some("10.13.8.3:50051"),
"read off the SEALED feed, as the boot does"
);
assert_eq!(
kept_reply(root.path()).map(|k| k.me.name).as_deref(),
Some("vm3")
);
}
/// ★ **A keep boot of the same estate keeps the box's pairing** (lane G5, A7 row 6; Gunnar
/// ff58dad1): a new ESP onto the kept data, the same estate: the feed, the box's own key
/// and its certificate stay, the boot is marked once; another estate starts unpaired.
#[test]
fn a_keep_boot_of_the_same_estate_keeps_the_boxs_pairing() {
let root = tempfile::tempdir().unwrap();
nordisk_pairing::fed_at_rest::write(
root.path(),
nordisk_pairing::boxside::JOINED_REL,
&reply(Some(Role::Twin)).render().unwrap(),
)
.unwrap();
std::fs::write(
root.path().join(nordisk_pairing::boxside::OWN_KEY_REL),
"-----BEGIN PRIVATE KEY-----\nk\n",
)
.unwrap();
std::fs::write(
root.path().join(nordisk_pairing::boxside::OWN_CERT_REL),
"-----BEGIN CERTIFICATE-----\nc\n",
)
.unwrap();
let seed = |estate: &str| Seed {
estate: estate.into(),
tunnel: TunnelKind::Wireguard,
front: Default::default(),
kind: Kind::Holger,
installer_ca_pem: String::new(),
bootstrap_cert_pem: String::new(),
bootstrap_key_pem: String::new(),
peers_expected: 3,
mullvad: None,
tailscale: None,
installer_link: None,
};
let esp = tempfile::tempdir().unwrap();
std::fs::create_dir_all(esp.path().join("korp")).unwrap();
let cleared = first_boot(esp.path(), root.path(), &seed("holger.test"));
assert!(
cleared.as_ref().is_none_or(|c| c.is_empty()),
"a keep boot of the same estate clears nothing: {cleared:?}"
);
assert!(esp
.path()
.join(nordisk_pairing::boxside::ESP_CLEARED)
.exists());
assert!(
kept_reply(root.path()).is_some()
&& root
.path()
.join(nordisk_pairing::boxside::OWN_KEY_REL)
.exists()
&& root
.path()
.join(nordisk_pairing::boxside::OWN_CERT_REL)
.exists()
);
let esp2 = tempfile::tempdir().unwrap();
std::fs::create_dir_all(esp2.path().join("korp")).unwrap();
let cleared =
first_boot(esp2.path(), root.path(), &seed("other.test")).expect("a first boot");
assert!(
cleared.contains(&nordisk_pairing::boxside::JOINED_REL.to_string()),
"{cleared:?}"
);
}
/// ★ The ONE shop's installer waits for the primary's `ticket_pubkey`: a metered holger box
/// offers its fleet key under it (and as `fleet_pubkey`), the key its tickets are signed with.
#[cfg(feature = "metered")]
#[test]
fn a_metered_box_offers_its_fleet_key_as_its_ticket_key() {
let dir = tempfile::tempdir().unwrap();
let o = key_offers(dir.path());
let key = server_lib::holger_fleet::hex(
&server_lib::holger_fleet::BoxKey::load_or_mint(dir.path())
.unwrap()
.public(),
);
assert_eq!(o.get(OFFER_TICKET_KEY), Some(&key), "{o:?}");
assert_eq!(o.get(OFFER_FLEET_KEY), Some(&key));
assert_eq!(key.len(), 64);
}
/// ★ Lane M4: the estate's pay page the shop feeds every box (`refusal_link`, as Gunnar's).
/// ★ The shared medium's `{estate}` is the name the installer fed (lane W2).
#[test]
fn the_estate_name_is_the_installers_and_fills_the_base_url() {
let mut r = reply(None);
r.estate = " Test3.Holger.RS. ".into();
assert_eq!(estate_of(&r).as_deref(), Some("test3.holger.rs"));
for bad in ["", "10.0.0.1", "no-dot", "a..b"] {
r.estate = bad.into();
assert_eq!(estate_of(&r), None, "{bad:?}");
}
}
/// ★ NIGHT-PUBREPO: the estate's public repositories are the installer's feed's; unfed, none.
#[test]
fn the_public_repositories_are_read_off_the_feed() {
let mut r = reply(None);
r.feed.clear();
assert!(
public_repositories_of(&r.feed).is_empty(),
"not fed, nothing public"
);
r.feed.insert(
server_lib::posture::PUBLIC_REPOSITORIES_FEED.into(),
" crates, npm ".into(),
);
assert_eq!(
public_repositories_of(&r.feed),
vec!["crates".to_string(), "npm".to_string()]
);
}
#[test]
fn the_shop_s_pay_page_is_read_off_the_feed() {
let mut r = reply(None);
r.feed.clear();
assert_eq!(fed_pay_page(&r), None, "not fed, no pay page");
r.feed.insert(
"refusal_link".into(),
" https://holger.rs/private?renew=acme ".into(),
);
assert_eq!(
fed_pay_page(&r).as_deref(),
Some("https://holger.rs/private?renew=acme")
);
r.feed.insert("refusal_link".into(), " ".into());
assert_eq!(fed_pay_page(&r), None, "an empty one is no pay page");
}
#[test]
fn the_front_s_monetize_key_is_read_off_the_feed() {
let mut r = reply(None);
r.feed.clear();
assert_eq!(fed_monetize_key(&r), None, "no offer, no key");
r.feed.insert("offer.web.version".into(), "abc".into());
r.feed
.insert("offer.web.monetize_pubkey".into(), " 00ff ".into());
assert_eq!(fed_monetize_key(&r).as_deref(), Some("00ff"));
r.feed
.insert("offer.web.monetize_pubkey".into(), " ".into());
assert_eq!(fed_monetize_key(&r), None, "an empty offer is not a key");
}
#[test]
fn the_elected_role_becomes_holgers_role_and_a_twin_follows_the_primary_on_the_utility_network()
{
assert_eq!(
role_for(&reply(Some(Role::Primary)), false),
Some(server_lib::twin::NodeRole::Primary)
);
assert_eq!(
role_for(&reply(Some(Role::Twin)), false),
Some(server_lib::twin::NodeRole::Twin {
primary: "10.13.8.2:50051".into(),
poll_seconds: server_lib::twin::DEFAULT_POLL_SECONDS
})
);
assert_eq!(role_for(&reply(None), false), None, "not elected yet");
assert_eq!(
role_for(&reply(Some(Role::Front)), false),
None,
"never the front"
);
}
/// ★ **An estate box's twin follows the primary over wg0** (lane E1): under
/// WireGuard-only the role names the primary's OVERLAY address and never its
/// utility one; a fed utility address (`recharge_from`, the loop's primary) is
/// dialled at the same box's overlay address.
#[test]
fn an_estate_twin_follows_the_primarys_overlay_and_never_its_utility_address() {
let r = reply(Some(Role::Twin));
let role = role_for(&r, true).expect("a twin");
assert_eq!(role.primary(), Some("10.44.0.2:50051"));
let e = server_lib::tunnel_addr::Estate {
net: nordisk_pairing::OVERLAY_NET,
overlay: Some(nordisk_pairing::OVERLAY_BOX_B),
pairs: tunnel_pairs(&r),
};
assert_eq!(
e.route("the primary", "10.13.8.2:50051").unwrap(),
"10.44.0.2:50051",
"a fed utility address is dialled over the tunnel"
);
assert!(e.dial("the primary", role.primary().unwrap()).is_ok());
assert!(
e.dial(
"the primary",
role_for(&r, false).unwrap().primary().unwrap()
)
.is_err(),
"the utility address is not an estate link"
);
assert_eq!(
over_the_tunnel(Ipv4Addr::new(10, 13, 8, 2), &r),
nordisk_pairing::OVERLAY_BOX_A
);
assert_eq!(over_the_tunnel_at("10.13.8.2:50051", &r), "10.44.0.2:50051");
assert_eq!(
over_the_tunnel_at("10.9.9.9:50051", &r),
"10.9.9.9:50051",
"nobody's: as fed"
);
}
#[test]
fn pid1_brings_up_the_public_and_the_utility_nic_unless_holger_net_says_otherwise() {
let default = net_plans(None).unwrap();
assert_eq!(
default.iter().map(|c| c.interface()).collect::<Vec<_>>(),
["eth0", "eth1"],
"eth0 owns the default route, eth1 is the utility network"
);
assert_eq!(
net_plans(Some(" ")).unwrap(),
default,
"blank is the default, not no network"
);
let fixed = net_plans(Some("ens3:10.0.2.15/24:10.0.2.2")).unwrap();
assert_eq!(fixed.len(), 1);
assert!(
net_plans(Some("eth0:dhcp,eth0:dhcp"))
.unwrap_err()
.contains("[holger-net]"),
"a lying spec stops the boot by name"
);
}
#[test]
fn the_primary_is_ready_once_elected_and_the_twin_steps_wait_for_a_running_server_and_the_front(
) {
let data = std::env::temp_dir().join(format!("holger-paired-steps-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&data);
std::fs::create_dir_all(&data).unwrap();
let steps = HolgerSteps {
root: data.join("appliance"),
data_root: data.clone(),
wireguard: false,
server: || None,
};
assert_eq!(
steps.primary_ready(&BTreeMap::new()),
Ok(false),
"not elected yet: asked again"
);
let b = HolgerBox {
root: data.join("appliance"),
data_root: data.clone(),
tunnel: TunnelKind::None,
live: None,
};
boxside::Product::fed(
&b,
&JoinReply {
role: Some(Role::Primary),
..reply(None)
},
);
assert_eq!(steps.primary_ready(&BTreeMap::new()), Ok(true));
let p = Ipv4Addr::new(10, 13, 8, 2);
// No server has started in this test process: the twin steps are
// transients that say what they wait for, never a false "done".
assert!(steps.become_twin(p).unwrap_err().contains("not started"));
assert_eq!(
steps.certified(p),
Ok(false),
"no credential until the front certifies this twin"
);
assert!(steps.follow_state().unwrap_err().contains("not started"));
let _ = std::fs::remove_dir_all(&data);
}
/// ★ **Lane D5: a refused election is tried again until it is recorded, and the
/// step names the refusal.** test3's twin sat at "the installer's election is not
/// recorded yet" for ever after ONE refused `twin::elect` (holger-twin-5). Now
/// every later feed and every loop pass that carries the election elects again,
/// and `become_twin` says "could not be recorded: [holger-twin-5] …".
#[test]
fn a_refused_election_is_tried_again_on_the_next_pass_and_the_step_names_the_refusal() {
let base = std::env::temp_dir().join(format!("holger-paired-d5-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&base);
let p = Ipv4Addr::new(10, 13, 8, 2);
let twin = JoinReply {
role: Some(Role::Twin),
..reply(None)
};
let held = |data: &Path| {
std::fs::create_dir_all(data.join("holger-props")).unwrap();
let f = data.join("holger-props").join("pin");
std::fs::write(&f, b"history").unwrap();
f
};
for (lane, by_the_loop) in [("loop", true), ("feed", false)] {
let (root, data) = (
base.join(lane).join("appliance"),
base.join(lane).join("holger"),
);
std::fs::create_dir_all(root.join("pairing")).unwrap();
let block = held(&data);
let b = HolgerBox {
root: root.clone(),
data_root: data.clone(),
tunnel: TunnelKind::None,
live: None,
};
let steps = HolgerSteps {
root: root.clone(),
data_root: data.clone(),
wireguard: false,
server: || None,
};
// The feed that carries the election, kept where the box keeps it: refused.
std::fs::write(
root.join(nordisk_pairing::boxside::JOINED_REL),
twin.render().unwrap(),
)
.unwrap();
boxside::Product::fed(&b, &twin);
assert_eq!(
server_lib::twin::recorded_role(&data).unwrap(),
None,
"{lane}: the box holds state, the twin election is refused"
);
let said = steps.become_twin(p).unwrap_err();
assert!(
said.contains("the installer's election could not be recorded")
&& said.contains("[holger-twin-5]"),
"{lane}: the step names the refusal: {said}"
);
assert!(!said.contains("not recorded yet"), "{lane}: {said}");
// The blocking state goes; the next pass records the election.
std::fs::remove_file(&block).unwrap();
if by_the_loop {
assert!(
steps.become_twin(p).unwrap_err().contains("not started"),
"{lane}: recorded, then waits for the server"
);
} else {
boxside::Product::fed(&b, &twin);
}
let r = server_lib::twin::recorded_role(&data)
.unwrap()
.unwrap_or_else(|| panic!("{lane}: recorded on the next pass"));
assert!(
r.role.is_twin()
&& r.elected_by == "installer"
&& r.role.primary() == Some("10.13.8.2:50051"),
"{lane}: {r:?}"
);
// Recorded: later passes and feeds are no-ops, never "already elected".
boxside::Product::fed(&b, &twin);
assert!(
steps.become_twin(p).unwrap_err().contains("not started"),
"{lane}"
);
assert_eq!(
server_lib::twin::recorded_role(&data)
.unwrap()
.map(|x| x.elected_at),
Some(r.elected_at),
"{lane}: elected once"
);
}
let _ = std::fs::remove_dir_all(&base);
}
/// ★ L20: the box answers the shared live door off its own slot — what it runs, a signed
/// stage for its BASE — and a box with no slot refuses by name.
#[test]
fn the_box_answers_the_live_door_off_its_own_slot_or_refuses_by_name() {
let root = std::env::temp_dir().join(format!("holger-paired-live-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&root);
let app: Vec<u8> = (0..4096u32).map(|i| (i % 251) as u8).collect();
let bench =
holger_live::bench::BenchBox::lay(&root.join("esp"), "kernel", 42, &app).unwrap();
let next: Vec<u8> = app.iter().map(|b| b ^ 1).collect();
let stage = bench.stage_of(&next, 42);
let b = HolgerBox {
root: root.clone(),
data_root: root.clone(),
tunnel: TunnelKind::None,
live: Some(Arc::new(bench.slot)),
};
assert_eq!(boxside::Product::live_running(&b).unwrap().slot, "a");
// Lane B5: a declared stage reaches korp's own check (never the trait's default refusal).
let moved = nordisk_pairing::live::Declared {
release: None,
env: Some(vec!["korp.env.HOLGER_X=1".into()]),
};
let e = boxside::Product::live_stage_declared(&b, &stage, &moved).unwrap_err();
assert!(e.contains("[live-env-moved]"), "{e}");
assert!(boxside::Product::live_stage(&b, &stage)
.unwrap()
.contains("slot B"));
assert_eq!(boxside::Product::live_running(&b).unwrap().slot, "b trial");
let plain = HolgerBox {
root: root.clone(),
data_root: root.clone(),
tunnel: TunnelKind::None,
live: None,
};
assert!(boxside::Product::live_running(&plain)
.unwrap_err()
.contains("[live-unsupported]"));
assert!(boxside::Product::live_stage(&plain, &stage)
.unwrap_err()
.contains("[live-unsupported]"));
let _ = std::fs::remove_dir_all(&root);
}
#[test]
fn wg0_dials_with_the_key_this_box_presented() {
let root = std::env::temp_dir().join(format!("holger-paired-wg-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&root);
let b = HolgerBox {
root: root.clone(),
data_root: root.clone(),
tunnel: TunnelKind::Wireguard,
live: None,
};
let presented = boxside::Product::tunnel_pubkey(&b);
assert!(!presented.is_empty());
let seed = test_seed();
let t =
tunnel_factory(root.clone())(&seed, &BTreeMap::new()).unwrap_or_else(|e| panic!("{e}"));
assert_eq!(
t.public_key(),
presented,
"the relay knows this box by the key its join carried"
);
// The fed PSK rides the link (lane G5); one that is not a key is refused by name.
let psk = BTreeMap::from([(
nordisk_pairing::installer::FEED_WG_PSK.to_string(),
"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=".to_string(),
)]);
assert_eq!(
tunnel_factory(root.clone())(&seed, &psk)
.unwrap_or_else(|e| panic!("{e}"))
.public_key(),
presented
);
let bad = BTreeMap::from([(
nordisk_pairing::installer::FEED_WG_PSK.to_string(),
"short".to_string(),
)]);
let e = tunnel_factory(root.clone())(&seed, &bad)
.err()
.expect("refused")
.to_string();
assert!(e.contains("pre-shared key") && !e.contains("short"), "{e}");
let _ = std::fs::remove_dir_all(&root);
}
/// ★ **A re-pointed wg0 keeps the fed PSK** (lane HOLGER-WG; A5 §108 "PSK and re-pair"):
/// the box's driver (`box_via_front_with_psk`, what [`tunnel_factory`] raises) is
/// re-pointed at a front that moved (a front recharge) — its plan dials the new endpoint
/// WITH the same pre-shared key, and a blank re-fed PSK is refused by name rather than
/// read as "none". The box side of what the front's relay test proves for the twin.
#[test]
fn a_re_pointed_wg0_keeps_the_fed_psk() {
use nordisk_pairing::join::Peer;
use nordisk_pairing::tunnel::Tunnel as _;
const PSK: &str = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=";
let front = |endpoint: &str| Peer {
name: "vm1".into(),
role: Role::Front,
tunnel_pubkey: wg_appliance_core::encode_key(
&wg_appliance_core::keys::public_from_private_bytes(&[7u8; 32]),
),
overlay: nordisk_pairing::OVERLAY_FRONT,
endpoint: Some(endpoint.into()),
utility: None,
};
let mut wg =
nordisk_pairing::wireguard::WireGuard::box_via_front_with_psk([3u8; 32], Some(PSK))
.unwrap();
let me = wg.identity(nordisk_pairing::OVERLAY_BOX_B);
assert_eq!(
wg.repoint(&me, &[front("10.13.8.1:51820")]).unwrap(),
false,
"no pump yet: the peers are kept for the raise"
);
let moved = wg.repoint(&me, &[front("10.13.8.9:51820")]).unwrap();
assert!(!moved, "no pump to restart yet; the moved front is kept");
let spec = wg.spec().unwrap();
assert_eq!(spec.peers.len(), 1);
assert_eq!(
spec.peers[0].endpoint,
Some("10.13.8.9:51820".parse().unwrap()),
"dials the front where it lives now"
);
assert_eq!(
spec.peers[0].preshared_key,
Some(wg_appliance_core::decode_key(PSK).unwrap()),
"with the PSK it was fed"
);
assert!(
wg.set_preshared_key(Some("")).is_err(),
"a blank re-fed PSK is refused by name, never read as none"
);
assert_eq!(
wg.spec().unwrap().peers[0].preshared_key,
Some(wg_appliance_core::decode_key(PSK).unwrap()),
"the refused blank changed nothing"
);
}
fn test_seed() -> Seed {
let ca = nordisk_pairing::mtls::InstallerCa::mint(
"holger.test",
std::time::Duration::from_secs(3600),
)
.unwrap();
let boot = ca
.issue_bootstrap(std::time::Duration::from_secs(3600))
.unwrap();
Seed {
estate: "holger.test".into(),
tunnel: TunnelKind::Wireguard,
front: Default::default(),
kind: Kind::Holger,
installer_ca_pem: ca.cert_pem.clone(),
bootstrap_cert_pem: boot.cert_pem,
bootstrap_key_pem: boot.key_pem,
peers_expected: 3,
mullvad: None,
tailscale: None,
installer_link: None,
}
}
/// ★ A7 row 14 (Gunnar d1b12b15's pattern): the box ENROLS the front's offered console
/// key and writes the bearer it derives with its own key (0600) as its console login; it
/// offers its own key's public half. No shared bearer is read from the feed any more.
/// The vector is the front's (holger-estate-front `console_key`): seeds 0x11… and 0x22….
#[test]
fn the_box_enrols_the_fronts_console_key_and_derives_its_login() {
use std::os::unix::fs::PermissionsExt;
assert_eq!(
console_bearer(
&[0x22; 32],
&wg_appliance_core::keys::encode_key(
&wg_appliance_core::keys::public_from_private_bytes(&[0x11; 32])
)
)
.unwrap(),
"52140321f476b69df5fde703935f0682beaae18f61d8f12591d3bc0784485336"
);
let root = std::env::temp_dir().join(format!("holger-console-key-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&root);
let old = BTreeMap::from([(
"holger_console_token".to_string(),
"0123456789abcdef".repeat(4),
)]);
assert_eq!(
install_console_token(&root, &old),
Ok(false),
"a fed shared bearer is not taken"
);
assert!(!root.join(CONSOLE_TOKEN_FILE).exists());
let front_pub = wg_appliance_core::keys::encode_key(
&wg_appliance_core::keys::public_from_private_bytes(&[0x11; 32]),
);
let feed = BTreeMap::from([(
format!("offer.vm1.{OFFER_CONSOLE_PUBKEY}"),
front_pub.clone(),
)]);
assert_eq!(install_console_token(&root, &feed), Ok(true));
let path = root.join(CONSOLE_TOKEN_FILE);
let own = wg_appliance_core::ceremony::load_or_mint_identity(&root.join(CONSOLE_KEY_DIR))
.unwrap();
assert_eq!(
std::fs::read_to_string(&path).unwrap(),
console_bearer(&own.private, &front_pub).unwrap(),
"the bearer of the box's key and the front's"
);
assert_eq!(
std::fs::metadata(&path).unwrap().permissions().mode() & 0o777,
0o600
);
assert_eq!(
install_console_token(&root, &feed),
Ok(true),
"an unchanged offer is a no-op"
);
let e = install_console_token(
&root,
&BTreeMap::from([(
format!("offer.vm1.{OFFER_CONSOLE_PUBKEY}"),
"short".to_string(),
)]),
)
.unwrap_err();
assert!(e.contains("[holger-console-key-bad]"), "{e}");
// The box offers its own key's public half, through the Product seam; the fed offer
// writes the login through it too.
let b = HolgerBox {
root: root.clone(),
data_root: root.clone(),
tunnel: TunnelKind::None,
live: None,
};
assert_eq!(
boxside::Product::offer(&b, None).get(OFFER_BOX_PUBKEY),
Some(&own.public_b64)
);
let _ = std::fs::remove_file(&path);
let mut r = reply(None);
r.feed = feed.clone();
boxside::Product::fed(&b, &r);
assert!(path.is_file());
let _ = std::fs::remove_dir_all(&root);
}
#[test]
fn the_boot_waits_only_for_a_declared_and_missing_token_file() {
let cfg = server_lib::auth::AuthConfig {
methods: vec![server_lib::auth::AuthMethodConfig::StaticToken {
subject: "console".into(),
token: None,
token_env: None,
token_file: Some("/nonexistent/holger-console-token".into()),
}],
..Default::default()
};
assert!(declares_missing_token_file(
Some(&cfg),
Path::new("/nonexistent/holger-console-token")
));
assert!(
!declares_missing_token_file(Some(&cfg), Path::new("/other")),
"another file is not the console's"
);
assert!(!declares_missing_token_file(
None,
Path::new("/nonexistent/holger-console-token")
));
let open = server_lib::auth::AuthConfig::default();
assert!(!declares_missing_token_file(
Some(&open),
Path::new("/nonexistent/holger-console-token")
));
}
#[test]
fn a_fed_role_is_recorded_once_and_never_changed() {
let data = std::env::temp_dir().join(format!("holger-paired-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&data);
std::fs::create_dir_all(&data).unwrap();
let b = HolgerBox {
root: data.join("appliance"),
data_root: data.clone(),
tunnel: TunnelKind::None,
live: None,
};
boxside::Product::fed(&b, &reply(Some(Role::Twin)));
let r = server_lib::twin::recorded_role(&data)
.unwrap()
.expect("recorded");
assert!(r.role.is_twin() && r.elected_by == "installer");
boxside::Product::fed(&b, &reply(Some(Role::Primary)));
assert!(
server_lib::twin::recorded_role(&data)
.unwrap()
.unwrap()
.role
.is_twin(),
"a role is a bootstrap fact"
);
let c = boxside::Product::capacity(&b).unwrap().unwrap();
assert!(c.cores >= 1 && c.ram_bytes > 0);
assert_eq!(boxside::Product::kind(&b), Kind::Holger);
assert_eq!(
boxside::Product::tunnel_pubkey(&b),
"",
"tunnel = none presents no key"
);
let _ = std::fs::remove_dir_all(&data);
}
/// ★ **The relying party comes from the estate's feed** — the medium's RON is
/// shared by every estate, a passkey is bound to one name — and a declared
/// method, an open config or a name that is not one is never overridden.
#[test]
fn the_passkey_plane_is_turned_on_for_the_fed_console_host_only() {
let console = || server_lib::auth::AuthConfig {
methods: vec![server_lib::auth::AuthMethodConfig::StaticToken {
subject: "console".into(),
token: None,
token_env: None,
token_file: Some("/f".into()),
}],
..Default::default()
};
let fed = |v: &str| BTreeMap::from([(PASSKEY_RP_FEED.to_string(), v.to_string())]);
let mut auth = Some(console());
assert_eq!(
passkey_from_feed(&mut auth, &fed("Test2.Holger.rs")),
Some("test2.holger.rs".into())
);
let pk = auth.as_ref().unwrap().methods.iter().find_map(|m| match m {
server_lib::auth::AuthMethodConfig::Passkey {
rp_id,
rp_origin,
bootstrap_until_first_admin,
..
} => Some((
rp_id.clone(),
rp_origin.clone(),
*bootstrap_until_first_admin,
)),
_ => None,
});
assert_eq!(
pk,
Some((
"test2.holger.rs".into(),
"https://test2.holger.rs".into(),
true
))
);
assert_eq!(
passkey_from_feed(&mut auth, &fed("other.example")),
None,
"a declared method wins"
);
let mut open = Some(server_lib::auth::AuthConfig::default());
assert_eq!(
passkey_from_feed(&mut open, &fed("holger.rs")),
None,
"an open config stays open"
);
let mut c = Some(console());
for bad in ["10.0.0.7", "localhost", "a/b.c", ""] {
assert_eq!(passkey_from_feed(&mut c, &fed(bad)), None, "{bad:?}");
}
assert_eq!(
passkey_from_feed(&mut c, &BTreeMap::new()),
None,
"nothing fed, nothing changed"
);
}
/// ★ F12 (test3-holger-rs 2026-10-21, `installing (paired)`): the ONE box's shop feeds no
/// `passkey_rp_id`, so a shop-installed main must take its relying party from the estate
/// the installer named — else `/-/auth/passkey/config` answers 404 and the sale check never
/// passes. A fed `passkey_rp_id` still wins; a declared method and an open config are kept.
#[test]
fn a_shop_installed_box_opens_its_passkey_plane_on_the_estates_own_name() {
let console = || server_lib::auth::AuthConfig {
methods: vec![server_lib::auth::AuthMethodConfig::StaticToken {
subject: "console".into(),
token: None,
token_env: None,
token_file: Some("/f".into()),
}],
..Default::default()
};
let rp_of = |a: &Option<server_lib::auth::AuthConfig>| {
a.as_ref().unwrap().methods.iter().find_map(|m| match m {
server_lib::auth::AuthMethodConfig::Passkey {
rp_id,
rp_origin,
bootstrap_until_first_admin,
..
} => Some((
rp_id.clone(),
rp_origin.clone(),
*bootstrap_until_first_admin,
)),
_ => None,
})
};
let mut auth = Some(console());
assert_eq!(
passkey_for(&mut auth, &BTreeMap::new(), Some("Test3.Holger.rs.")),
Some("test3.holger.rs".into())
);
assert_eq!(
rp_of(&auth),
Some((
"test3.holger.rs".into(),
"https://test3.holger.rs".into(),
true
))
);
let mut fed = Some(console());
let feed = BTreeMap::from([(PASSKEY_RP_FEED.to_string(), "console.example".to_string())]);
assert_eq!(
passkey_for(&mut fed, &feed, Some("test3.holger.rs")),
Some("console.example".into()),
"a fed relying party wins"
);
let mut open = Some(server_lib::auth::AuthConfig::default());
assert_eq!(
passkey_for(&mut open, &BTreeMap::new(), Some("test3.holger.rs")),
None,
"an open config stays open"
);
let mut none = Some(console());
assert_eq!(
passkey_for(&mut none, &BTreeMap::new(), None),
None,
"no name at all: nothing changed"
);
assert_eq!(
passkey_for(&mut none, &BTreeMap::new(), Some("10.0.0.7")),
None,
"an address is no relying party"
);
}
/// ★ **The box keeps the key its front's report is opened under** (HOL-FLOW-FRONT): x25519
/// of its console key and the front's offered one, under the report's own context, the
/// same key the front derives from its half; written 0600 under the data root on every
/// feed, and none without an offer.
#[test]
fn the_box_keeps_the_key_its_fronts_report_is_opened_under() {
use std::os::unix::fs::PermissionsExt as _;
let root =
std::env::temp_dir().join(format!("holger-front-report-key-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&root);
assert_eq!(
front_report_key(&root, &BTreeMap::new()),
Ok(None),
"no offer, no key"
);
let front_private = [0x11; 32];
let front_pub = wg_appliance_core::keys::encode_key(
&wg_appliance_core::keys::public_from_private_bytes(&front_private),
);
let feed = BTreeMap::from([(format!("offer.vm1.{OFFER_CONSOLE_PUBKEY}"), front_pub)]);
let key = front_report_key(&root, &feed).unwrap().expect("a key");
let own = wg_appliance_core::ceremony::load_or_mint_identity(&root.join(CONSOLE_KEY_DIR))
.unwrap();
let theirs = boringtun::x25519::PublicKey::from(
wg_appliance_core::keys::decode_key(&own.public_b64).unwrap(),
);
let front_side =
boringtun::x25519::StaticSecret::from(front_private).diffie_hellman(&theirs);
assert_eq!(
key,
server_lib::front_report::front::key_of(front_side.as_bytes()),
"the front derives the same key from its half"
);
assert_ne!(
key.iter().map(|b| format!("{b:02x}")).collect::<String>(),
console_bearer(&own.private, &feed.values().next().unwrap().clone()).unwrap(),
"the report's key is no bearer"
);
let b = HolgerBox {
root: root.clone(),
data_root: root.clone(),
tunnel: TunnelKind::None,
live: None,
};
let mut r = reply(None);
r.feed = feed;
boxside::Product::fed(&b, &r);
let path = root
.join(server_lib::front_report::DIR)
.join(server_lib::front_report::KEY_FILE);
assert_eq!(
std::fs::read_to_string(&path).unwrap(),
key.iter().map(|b| format!("{b:02x}")).collect::<String>()
);
assert_eq!(
std::fs::metadata(&path).unwrap().permissions().mode() & 0o777,
0o600
);
let _ = std::fs::remove_dir_all(&root);
}
}
/// **The latest feed this box was given** (the ONE monetize's door and key ride it).
pub fn fed(installed: &Installed<HolgerBox>) -> std::collections::BTreeMap<String, String> {
installed
.latest()
.map(|r| r.feed.clone().into_iter().collect())
.unwrap_or_default()
}
#[cfg(test)]
mod b3_tests {
use super::*;
/// ★ **A STAGE line reaches the boot log past the WARN filter** (lane B3, A9 row 4): the
/// box's logger says only WARN, and `stage` still lands `STAGE pairing +<ms>ms …` in the
/// boot log the log door and DEV-NARRATE read.
#[test]
fn a_stage_reaches_the_boot_log_whatever_the_logger_filters() {
log::set_max_level(log::LevelFilter::Warn);
stage("pairing", "the installer's listener serves (b3 test)");
let lines = BOOT_LOG.since(0, 100_000).lines;
assert!(
lines
.iter()
.any(|l| l.starts_with("STAGE pairing +") && l.contains("(b3 test)")),
"{lines:?}"
);
assert_eq!(appliance_pid1::fault::progress().0, "pairing");
}
/// ★ **The election reads this box's disk** (lane B3, A9 row 5): with the start-up disk
/// test started, `capacity` holds hello until the test is done, then carries its MiB/s
/// (it was 0 on every Holger box).
#[test]
fn the_capacity_carries_the_start_up_disk_test() {
let d = tempfile::tempdir().unwrap();
start_disktest(d.path());
let b = HolgerBox {
root: d.path().join("appliance"),
data_root: d.path().to_path_buf(),
tunnel: TunnelKind::None,
live: None,
};
let mut cap = None;
for _ in 0..500 {
if let Ok(c) = boxside::Product::capacity(&b) {
cap = c;
break;
}
std::thread::sleep(std::time::Duration::from_millis(20));
}
let cap = cap.expect("hello answers once the disk test is done");
assert!(cap.disk_mib_s >= 1, "{cap:?}");
}
}
#[cfg(test)]
mod trial_tests {
use super::*;
use holger_live::bench::BenchBox;
use holger_live::trial::TrialWatch;
use holger_live::{ConfirmStep, CONFIRM_AFTER, CONFIRM_DEADLINE};
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::time::Duration;
fn app(n: u8) -> Vec<u8> {
(0..64 * 1024u32).map(|i| (i % 251) as u8 ^ n).collect()
}
/// A box booted on trial in slot B, its old app in slot A.
fn on_trial(d: &Path) -> BenchBox {
let b = BenchBox::lay(&d.join("esp"), "kernel-1", 42, &app(1)).unwrap();
b.slot.stage(&b.stage_of(&app(2), 42)).unwrap();
b.slot.boot_as_korp_start().unwrap();
assert!(b.slot.on_trial());
b
}
/// A port nothing listens on.
fn closed_port() -> std::net::SocketAddr {
let l = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
let a = l.local_addr().unwrap();
drop(l);
a
}
/// ★ **The box's trial looks at its doors** (lane W9; the predicate was `|| true`, so a new
/// app that never served was confirmed): a box whose doors take connections confirms after
/// serving; one whose doors never answer is rolled back at the deadline, and korp-start then
/// boots the old app in slot A.
#[test]
fn a_new_app_whose_doors_never_answer_is_rolled_back_and_one_that_serves_confirms() {
// Never serving: rolled back.
let d = tempfile::tempdir().unwrap();
let b = on_trial(d.path());
let rolled = Arc::new(AtomicBool::new(false));
let r2 = Arc::clone(&rolled);
let secs = Arc::new(AtomicU64::new(5));
let s2 = Arc::clone(&secs);
let slot = Arc::new(b.slot);
let w = TrialWatch::new(
Arc::clone(&slot),
box_serving(vec![closed_port()]),
Box::new(move || Duration::from_secs(s2.load(Ordering::SeqCst))),
Box::new(move || r2.store(true, Ordering::SeqCst)),
);
assert_eq!(
w.look().unwrap(),
ConfirmStep::Wait,
"not serving yet, before the deadline"
);
secs.store(CONFIRM_DEADLINE.as_secs() + 1, Ordering::SeqCst);
assert_eq!(
w.look().unwrap(),
ConfirmStep::RollBack,
"never served by the deadline"
);
assert!(
rolled.load(Ordering::SeqCst),
"the rollback re-exec was asked for"
);
let said = slot.boot_as_korp_start().unwrap();
assert!(said.contains("rolled back"), "{said}");
assert_eq!(slot.running().unwrap().slot, "a", "the old app again");
// Serving: confirmed.
let d = tempfile::tempdir().unwrap();
let b = on_trial(d.path());
let door = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
let at = door.local_addr().unwrap();
let secs = Arc::new(AtomicU64::new(5));
let s2 = Arc::clone(&secs);
let rolled = Arc::new(AtomicBool::new(false));
let r2 = Arc::clone(&rolled);
let slot = Arc::new(b.slot);
let w = TrialWatch::new(
Arc::clone(&slot),
box_serving(vec![at]),
Box::new(move || Duration::from_secs(s2.load(Ordering::SeqCst))),
Box::new(move || r2.store(true, Ordering::SeqCst)),
);
assert_eq!(
w.look().unwrap(),
ConfirmStep::Wait,
"serving, not yet for long enough"
);
secs.store(5 + CONFIRM_AFTER.as_secs() + 1, Ordering::SeqCst);
assert_eq!(w.look().unwrap(), ConfirmStep::Confirm);
assert!(!rolled.load(Ordering::SeqCst));
assert_eq!(slot.running().unwrap().slot, "b", "confirmed");
drop(door);
}
/// Every door must answer: one that does not is a box that does not serve, and no door at
/// all is not serving either.
#[test]
fn every_door_must_answer() {
let open = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
let up = open.local_addr().unwrap();
assert!(doors_answer(&[up], DOOR_LOOK));
assert!(!doors_answer(&[up, closed_port()], DOOR_LOOK));
assert!(!doors_answer(&[], DOOR_LOOK));
}
/// The doors are the endpoints as bound: gRPC and HTTP, a wildcard looked at on loopback.
#[test]
fn the_doors_are_every_endpoints_listeners() {
assert_eq!(
own_doors([
("0.0.0.0:50051", Some("10.44.0.2:8443")),
("0.0.0.0:50051", None),
("not an address", None)
]),
vec![
"127.0.0.1:50051".parse().unwrap(),
"10.44.0.2:8443".parse::<std::net::SocketAddr>().unwrap()
]
);
}
}
/// ★ **Lane D4 (2026-10-20): an estate twin of the shipped declaration records the
/// installer's election, becomes the twin, is certified and says PAIRED.**
///
/// test3-holger-rs's twin on the r4 media sat at "step become-twin (try 3583: the
/// installer's election is not recorded yet)": its role record (`holger-role.ron`) was
/// never written. The record is written by [`boxside::Product::fed`] (`twin::elect`)
/// and by nothing else on a twin. Here the REAL box steps run in order on a box that
/// boots `holger_recipe::estate_ron::medium()` under the estate posture: the first feed
/// (no role yet: the election waits for both product boxes), the server start, the
/// feed that carries the election, `HolgerSteps::become_twin`, the front's certify
/// steps, `certified` and the follow, to PAIRED. The box's globals (the posture, the
/// one live follower) are the process's, so it runs in a process of its own.
#[cfg(all(test, feature = "supply-chain"))]
mod d4_become_twin {
use super::*;
use holger_recipe::estate_ron;
use nordisk_pairing::join::{Assigned, Peer};
use server_lib::grpc::holger_proto::admin_service_client::AdminServiceClient;
use server_lib::grpc::holger_proto::TwinStatusRequest;
use std::io::Write as _;
use std::time::Duration;
const CHILD: &str = "HOLGER_D4_CHILD";
/// The console's login the twin derives from the front's offered key (A7 row 14), set
/// once the twin minted its own key; the primary's file is written with the same one.
static TOKEN: std::sync::OnceLock<String> = std::sync::OnceLock::new();
/// The front's console key (a fixed seed): what `feed()` offers.
const FRONT_SEED: [u8; 32] = [0x11; 32];
const ESTATE: &str = "test3.holger.rs";
/// The main's and the twin's loopback addresses, this run's own. The boxes dial each
/// other on the fixed `CONTROL_PORT` as the shipped code does, so a fixed pair
/// (127.0.0.7/8) met another run of this test, or `real_boot_estate_box`, on a shared
/// host (address in use at 127.0.0.8:50051). Two addresses in 127.0.0.0/8 whose control
/// port is free when the test starts.
fn pair() -> (Ipv4Addr, Ipv4Addr) {
static PAIR: std::sync::OnceLock<(Ipv4Addr, Ipv4Addr)> = std::sync::OnceLock::new();
*PAIR.get_or_init(|| {
let seed = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.subsec_nanos()
^ std::process::id().rotate_left(16);
let free = |ip: Ipv4Addr| {
std::net::TcpListener::bind((ip, nordisk_pairing::CONTROL_PORT)).is_ok()
};
for i in 0..4096u32 {
let n = seed.wrapping_add(i.wrapping_mul(2_654_435_761));
let (x, y, z) = (
16 + (n >> 24) as u8 % 224,
(n >> 16) as u8,
2 + (n >> 8) as u8 % 250,
);
let (main, twin) = (Ipv4Addr::new(127, x, y, z), Ipv4Addr::new(127, x, y, z + 1));
if free(main) && free(twin) {
return (main, twin);
}
}
panic!("no free pair of loopback addresses for the control port");
})
}
fn primary_ip() -> Ipv4Addr {
pair().0
}
fn twin_ip() -> Ipv4Addr {
pair().1
}
#[test]
fn an_estate_twin_records_the_installers_election_becomes_the_twin_and_pairs() {
let out = std::process::Command::new(std::env::current_exe().unwrap())
.args([
"paired::d4_become_twin::child",
"--exact",
"--ignored",
"--nocapture",
"--test-threads=1",
])
.env(CHILD, "1")
.env("RUST_LOG", "info")
.output()
.unwrap();
let text = format!(
"{}{}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
println!("{text}");
assert!(
out.status.success() && text.contains("1 passed"),
"the twin's own process:\n{text}"
);
}
fn shipped(d: &Path, ip: Ipv4Addr, http: u16) -> String {
let d = d.display();
estate_ron::medium()
.replace(
&format!("\"{}/", holger_recipe::packaging::BOX_HOLGER_DATA),
&format!("\"{d}/holger/"),
)
.replace(
&format!("\"{}/", estate_ron::BOX_APPLIANCE_ROOT),
&format!("\"{d}/"),
)
.replace(
&format!("\"{}/", estate_ron::RELEASE_ROOT),
&format!("\"{d}/"),
)
.replace(
"\"0.0.0.0:50051\"",
&format!("\"{ip}:{}\"", nordisk_pairing::CONTROL_PORT),
)
.replace("\"0.0.0.0:8443\"", &format!("\"{ip}:{http}\""))
}
/// One box as `main.rs` starts it: the declaration read, the feed's passkey plane,
/// estate and duties applied, then the start.
fn start(
d: &Path,
ip: Ipv4Addr,
paired: bool,
) -> (server_lib::Holger, tempfile::NamedTempFile) {
let mut cfg = tempfile::NamedTempFile::new().unwrap();
cfg.write_all(
shipped(d, ip, traits::test_support::hold_port_on(&ip.to_string())).as_bytes(),
)
.unwrap();
let mut h = server_lib::read_ron_config(cfg.path())
.unwrap_or_else(|e| panic!("the shipped declaration parses: {e:#}"));
if paired {
fed_passkey(&mut h.auth);
h.resolve_estate(fed_estate().as_deref());
h.resolve_public_repositories(&fed_public_repositories())
.unwrap_or_else(|e| panic!("public repositories: {e}"));
estate_duties(&mut h.auth);
} else {
h.resolve_estate(Some(ESTATE));
}
h.instantiate_backends()
.unwrap_or_else(|e| panic!("backends: {e:#}"));
server_lib::wire_holger(&mut h).unwrap_or_else(|e| panic!("wire: {e:#}"));
h.start().unwrap_or_else(|e| panic!("start: {e:#}"));
(h, cfg)
}
fn feed() -> BTreeMap<String, String> {
let front = wg_appliance_core::keys::encode_key(
&wg_appliance_core::keys::public_from_private_bytes(&FRONT_SEED),
);
BTreeMap::from([
(format!("offer.vm1.{OFFER_CONSOLE_PUBKEY}"), front),
(PASSKEY_RP_FEED.to_string(), ESTATE.to_string()),
])
}
/// What the installer feeds the twin, kept where the box keeps it, then handed to the product.
fn feed_the_twin(b: &HolgerBox, root: &Path, reply: &JoinReply) {
std::fs::create_dir_all(root.join("pairing")).unwrap();
std::fs::write(
root.join(nordisk_pairing::boxside::JOINED_REL),
reply.render().unwrap(),
)
.unwrap();
boxside::Product::fed(b, reply);
}
/// What the front derives for a box that offered `box_pub` (the same derivation, its end).
fn server_console_bearer_of_front(box_pub: &str) -> String {
console_bearer(&FRONT_SEED, box_pub).unwrap()
}
fn authed<T>(body: T) -> tonic::Request<T> {
let mut r = tonic::Request::new(body);
r.metadata_mut().insert(
"authorization",
format!("Bearer {}", TOKEN.get().expect("derived"))
.parse()
.unwrap(),
);
r
}
#[tokio::test(flavor = "multi_thread", worker_threads = 6)]
#[ignore = "run by an_estate_twin_records_the_installers_election_becomes_the_twin_and_pairs in a process of its own"]
async fn child() {
if std::env::var_os(CHILD).is_none() {
return;
}
let _ = env_logger::builder().is_test(true).try_init();
let (tw, pr) = (tempfile::tempdir().unwrap(), tempfile::tempdir().unwrap());
let (troot, data) = (tw.path().to_path_buf(), tw.path().join("holger"));
// SAFETY: set once, before this process reads it; the test runs alone in it.
std::env::set_var("HOLGER_APPLIANCE_DATA", &troot);
for d in [&troot, &pr.path().to_path_buf()] {
std::fs::write(
d.join(estate_ron::KORP_ROOT_STAGED),
estate_ron::KORP_ROOT_PEM,
)
.unwrap();
}
// An estate box (lane E1): WireGuard-only, as `paired::start` sets it for a WireGuard seed.
set_estate_posture(None, None);
let b = HolgerBox {
root: troot.clone(),
data_root: data.clone(),
tunnel: TunnelKind::Wireguard,
live: None,
};
// 0. The installer's first visit reads the box's offers (a metered box mints its
// fleet key under the data root here: `key_offers`), before any feed.
let offered = boxside::Product::offer(&b, None);
assert!(offered.contains_key(OFFER_VERSION), "{offered:?}");
// The front derives the twin's login from the twin's offered key (A7 row 14).
let twin_pub = offered
.get(OFFER_BOX_PUBKEY)
.expect("the twin offers its console key");
let derived = server_console_bearer_of_front(twin_pub);
TOKEN.set(derived.clone()).unwrap();
std::fs::write(pr.path().join(estate_ron::CONSOLE_TOKEN_FILE), &derived).unwrap();
std::fs::set_permissions(
&pr.path().join(estate_ron::CONSOLE_TOKEN_FILE),
<std::fs::Permissions as std::os::unix::fs::PermissionsExt>::from_mode(0o600),
)
.unwrap();
// 1. The first feed: the twin joined first, the election has not spoken yet.
let first = JoinReply {
estate: ESTATE.into(),
me: Assigned {
name: "vm3".into(),
overlay: twin_ip(),
},
cert_pem: None,
role: None,
primary_overlay: None,
primary_utility: None,
peers: Vec::new(),
feed: feed(),
};
feed_the_twin(&b, &troot, &first);
assert!(
troot.join(CONSOLE_TOKEN_FILE).is_file(),
"the console token is installed from the first feed"
);
// 2. The twin's server starts (the console token and wg0 are in), then the main's.
let (_t, _tc) = start(&troot, twin_ip(), true);
let (_p, _pc) = start(pr.path(), primary_ip(), false);
let (tctl, pctl) = (
format!("{}:{}", twin_ip(), nordisk_pairing::CONTROL_PORT),
format!("{}:{}", primary_ip(), nordisk_pairing::CONTROL_PORT),
);
// What reaches a serving box before the election: the console's and the front's reads.
let mut admin = None;
for _ in 0..300 {
if let Ok(c) = AdminServiceClient::connect(format!("http://{tctl}")).await {
admin = Some(c);
break;
}
tokio::time::sleep(Duration::from_millis(20)).await;
}
let mut admin = admin.expect("the twin's control door answers");
for _ in 0..3 {
let _ = admin.twin_status(authed(TwinStatusRequest {})).await;
The file goes on; the console shows all of it.