holger-estate-front/src/lib.rs
//! # holger-estate-front — vm1 of holger's estate, holger's OWN front program
//!
//! holger's estate is three VMs, the pattern gunnar's estate set: **vm1 the
//! front**, vm2 holger's primary, vm3 its twin. vm2 and vm3 boot the holger
//! medium (`holger-installer-metered`, one ISO, the role elected by the
//! installer); vm1 boots THIS program's medium, `holger-front-installer-metered`,
//! as PID 1. Like every box it reaches the installer over the installer's
//! WireGuard link, is fed, and carries on by itself (owner 2026-09-27: follow
//! gunnar's pattern, never gunnar's code — nothing here comes from the gunnar
//! repository; see the crate's Cargo.toml for which shared crate does what):
//!
//! * [`front`] — the installer's view of vm1 (Skidbladnir
//! `nordisk-pairing::frontside`, kind `front`): what holger's front offers
//! (its build, where its plugin door is, monetize's key) and hands its
//! successor (the web certificate);
//! * [`role`] — the estate's WireGuard relay (edda `estate-front::relay`) on the
//! front's own tunnel key, and the twin's certification by the front
//! ([`certify`]: `nordisk-twin`'s state machine over holger's own
//! AdminService, with the fed console bearer);
//! * the web face (edda `estate-front::web`): :80/:443 with the front's own
//! certificate, the repository route (`repo_hosts` → the primary's HTTP
//! :8443 and gRPC :50051 over the utility network) and — paid — the payment
//! vendors' `/callback/<vendor>` to monetize;
//! * the estate's clock (edda `estate-clock`, started by `main`): this front
//! keeps its own clock from the medium's `clock` sources and serves signed
//! time on `<utility>:50123`; [`front`] offers `clock_key` + `clock_peer`,
//! and every holger box follows it (holger `server_lib::clock`);
//! * [`console`] — holger's console (holger-ui's thin server, linked in): the
//! bundle and a pass-through to the primary's two doors;
//! * [`shop_door`] + [`sold`] + [`update`] (feature `metered`) — NO monetize runs here
//! (G16, ONE monetize): the front calls the ONE monetize's `SoldEstate` door,
//! signed by the estate's client key — the installer material kept in the
//! primary's sealed slot ([`sealed`]), the update the operator opened pulled box
//! by box; it relays the ONE monetize's door and key to every box, whose own pull
//! channel (holger-fleet) reports and takes orders.
//!
//! **A lost front comes back as Gunnar's does** (`EstateOrder.RestoreFront`, S8): the ONE
//! shop restores the provider's daily backup of the front's one disk, in place or onto a
//! new server, driven by the estate's lifecycle (`lifecycle::Holger`, front role `front`).
//! Nothing Holger-specific is needed: everything this front is keeps on that disk under its
//! data root (its tunnel key `wg/`, its clock key, the twin's certification record, the web
//! certificates), so a restored front keeps every key and nothing is re-trusted; the
//! installer material is in the primary's sealed slot, not on the front; and main and twin
//! follow a moved front through their own installer listener's re-feed
//! (`nordisk_pairing::boxside`, the same code Gunnar's boxes run).
/// A re-laid main filled from its twin by this front (Gunnar's autofill, lane G11).
pub mod autofill;
/// Holger's site in the shared buy flow (monetize-site-buy, A12): the relay on the front.
#[cfg(all(feature = "console", feature = "metered"))]
pub mod buy;
pub mod certify;
/// This front's certificates for holger-ui's admin page (owner 2026-10-25).
#[cfg(feature = "console")]
pub mod certs_page;
#[cfg(feature = "console")]
pub mod console;
pub mod console_key;
pub mod front;
/// The front's own live slot on trial (L20).
pub mod live;
/// The front proves the tunnel is the only path to its boxes (lane W9, Gunnar's mesh probe).
pub mod mesh_probe;
/// A lost main or twin re-admitted by the front (feature `metered`); the pulled order fills a main.
#[cfg(feature = "metered")]
pub mod recharge;
/// Every paired link renewed before it ends, as jera jobs (lane E2).
pub mod renew;
pub mod role;
/// The primary's sealed slot (feature `metered`).
#[cfg(feature = "metered")]
pub mod sealed;
/// The ONE monetize's `SoldEstate` door (feature `metered`).
#[cfg(feature = "metered")]
pub mod shop_door;
/// The sold front's duties as jera jobs (feature `metered`).
#[cfg(feature = "metered")]
pub mod sold;
/// The update the ONE box opened, decided box by box.
pub mod update;
/// The front keeps the certificate its installer renews (lane E2): the shared box half,
/// Skidbladnir `nordisk_pairing::boxside::renewing` (P4c, A28 M11).
pub use nordisk_pairing::boxside::renewing;
/// The front's own WireGuard interface for the downstreams declared `via: Wireguard`, and
/// the sync door on it (lane HOL-WGDS).
pub mod downstreams;
/// The front tells main its doors, the names it serves and where each goes (lane
/// HOL-FLOW-FRONT): main's flow unfolds the front to it.
pub mod front_report;
/// A panic on the front is a fault with Holger's code (lane G6, Gunnar's GN-99 hook).
pub mod pid1_fault;
/// The front reaches its boxes over the tunnel only (lane E1).
pub mod wg_only;
use std::net::Ipv4Addr;
use std::path::PathBuf;
use nordisk_pairing::join::JoinReply;
/// The product this front fronts.
pub const PRODUCT: &str = "holger";
/// The build this binary is (the short commit, `unknown` outside a checkout).
pub const GIT_HASH: &str = env!("HOLGER_FRONT_GIT_HASH");
/// Where the medium stages the seed, under the data root (as on a holger box).
pub const SEED_REL: &str = "pairing/seed.toml";
/// The data root's variable and default — the same as a holger box's.
pub const DATA_ENV: &str = "HOLGER_APPLIANCE_DATA";
/// The interface the front's public address is on (`HOLGER_NET`'s first entry): where a
/// WireGuard downstream reaches the front's downstream interface (lane HOL-WGDS).
pub const PUBLIC_INTERFACE: &str = "eth0";
/// The interface the front's utility address is on (`HOLGER_NET`'s second
/// entry, `eth1` — the network the boxes' control planes are on).
pub const UTILITY_INTERFACE: &str = "eth1";
/// holger's control plane (gRPC) on a box's utility address.
pub const CONTROL_PORT: u16 = 50051;
/// holger's repository HTTP door on a box's utility address (the estate's RON:
/// `ron_http_url: 0.0.0.0:8443`, the firewall admits it from vm1 only).
pub const HTTP_PORT: u16 = 8443;
/// **The gRPC services the front carries to the primary on the repository host**
/// (RA5-1, edda `Routed::grpc_services`): every `holger.*` service (each checks its
/// own caller: a bearer, a signed-in person, a repository token). Nothing else: the
/// identity core's `nornir.identity.v1.IdentityService` (sign-up, invites) is the
/// primary's own and is never reached from the internet through the front.
pub const CARRIED_GRPC: &[&str] = &["holger."];
/// **Where the front keeps its own copy of its boot log**, under its data root (Gunnar
/// 6dcbbe38, `installer_listener::KEEP_REL`): bounded (nordisk-pairing `KEEP_BYTES`,
/// rotated to `.1`), so a crash leaves its last lines on the disk and the next boot serves
/// them through the log door.
pub const KEEP_REL: &str = "log/door.log";
/// **Keep `log` on the front's disk** for the life of the process: the previous boot's last
/// lines carried in (marked `(previous boot) `), then every line written to
/// `<data_root>/`[`KEEP_REL`] within a second. Answers how many lines were carried.
pub fn keep_on_disk(
log: &'static nordisk_pairing::bootlog::BootLog,
data_root: &std::path::Path,
) -> usize {
log.keep_on_disk(
data_root.join(KEEP_REL),
nordisk_pairing::bootlog::KEEP_BYTES,
)
}
/// The data root: `HOLGER_APPLIANCE_DATA`, else `/data`.
pub fn data_root() -> PathBuf {
std::env::var(DATA_ENV)
.ok()
.filter(|v| !v.trim().is_empty())
.map(PathBuf::from)
.unwrap_or_else(|| PathBuf::from("/data"))
}
/// The primary's control plane (`<overlay>:50051` on an estate, else `<utility>:50051`)
/// by the verdict, under the process posture ([`wg_only::wg_only`]).
pub fn primary_control(reply: &JoinReply) -> Option<String> {
primary_control_in(reply, wg_only::wg_only())
}
/// Pure: [`primary_control`] under `wg`.
pub fn primary_control_in(reply: &JoinReply, wg: bool) -> Option<String> {
wg_only::box_addr(reply, nordisk_pairing::Role::Primary, wg)
.map(|ip| format!("{ip}:{CONTROL_PORT}"))
}
/// The primary's address the front reaches (overlay on an estate, else utility).
pub fn primary_addr(reply: &JoinReply) -> Option<Ipv4Addr> {
wg_only::box_addr(reply, nordisk_pairing::Role::Primary, wg_only::wg_only())
}
/// Pure: **the primary's HTTP door** (`<utility>:8443`) — where holger serves
/// its passkey plane (`/-/auth/passkey/*`), which the console carries every
/// person's login, invite and session to (holger-ui `door`, the thin-layer
/// move 2026-10-04). `None` until the verdict names a primary.
pub fn primary_door(reply: &JoinReply) -> Option<String> {
primary_door_in(reply, wg_only::wg_only())
}
/// Pure: [`primary_door`] under `wg`.
pub fn primary_door_in(reply: &JoinReply, wg: bool) -> Option<String> {
wg_only::box_addr(reply, nordisk_pairing::Role::Primary, wg)
.map(|ip| format!("{ip}:{HTTP_PORT}"))
}
/// The twin's address the front reaches (overlay on an estate, else utility), when
/// the verdict names one.
pub fn twin_addr(reply: &JoinReply) -> Option<Ipv4Addr> {
twin_addr_in(reply, wg_only::wg_only())
}
/// Pure: [`twin_addr`] under `wg`.
pub fn twin_addr_in(reply: &JoinReply, wg: bool) -> Option<Ipv4Addr> {
wg_only::box_addr(reply, nordisk_pairing::Role::Twin, wg)
}
/// The front, as the installer sees it.
pub type FrontVm = nordisk_pairing::frontside::Front<front::HolgerFront>;
/// **The pairing files a fresh installation of this front must not carry**: the shared ones
/// (Skidbladnir `nordisk_pairing::boxside::PAIRING_STATE`), the twin's certification record
/// and the renewal state.
pub fn pairing_state() -> Vec<&'static str> {
let mut state: Vec<&str> = nordisk_pairing::boxside::PAIRING_STATE.to_vec();
state.push(certify::RECORD_REL);
state.push(renew::STATE_REL);
state
}
/// **The first boot after an installation** (`main`): the previous installation's pairing
/// state ([`pairing_state`]) is cleared so a front installed for a NEW estate starts
/// unpaired — and a KEEP boot of the SAME estate clears nothing (lane G5, A7 row 6; Gunnar
/// ff58dad1 `vm::first_boot`): the shop keep-boots the front from the release's keep medium,
/// its data kept, and nobody feeds a front again (Skidbladnir
/// `nordisk_pairing::boxside::reset_if_fresh_install_of`). `Some(vec![])`: a keep boot, kept.
pub fn first_boot(
esp: &std::path::Path,
data: &std::path::Path,
seed: &nordisk_pairing::seed::Seed,
mark: &dyn Fn(&std::path::Path) -> bool,
) -> Option<Vec<String>> {
nordisk_pairing::boxside::reset_if_fresh_install_of(esp, data, &pairing_state(), seed, mark)
}
/// **Holger's TLS on :443** (edda `Hooks::tls`, lane WG-PSK 2026-10-30): the provider the web
/// face is served with — Gunnar's, X25519MLKEM768 offered FIRST, the classical groups after it.
pub fn tls() -> std::sync::Arc<rustls::crypto::CryptoProvider> {
std::sync::Arc::new(nordisk_pairing::pqtls::provider())
}
#[cfg(test)]
pub(crate) mod tests {
use super::*;
use nordisk_pairing::join::{Assigned, Peer};
use nordisk_pairing::Role;
const TOKEN: &str = "c0nsole-bearer-0123456789abcdefghij";
/// ★ **The front's :443 offers X25519MLKEM768 first** (lane WG-PSK, 2026-10-30: holger.rs
/// negotiated plain X25519, gunnar.rs the hybrid): the same groups, in the same order, as
/// Gunnar's front (`gunnar_front::web::tls`), so an older client still connects on X25519.
#[test]
fn the_fronts_tls_offers_x25519mlkem768_first_and_keeps_the_classical_groups() {
let names: Vec<rustls::NamedGroup> = tls().kx_groups.iter().map(|g| g.name()).collect();
assert_eq!(
names,
vec![
rustls::NamedGroup::X25519MLKEM768,
rustls::NamedGroup::X25519,
rustls::NamedGroup::secp256r1,
rustls::NamedGroup::secp384r1
]
);
}
fn peer(name: &str, role: Role, utility: Option<[u8; 4]>) -> Peer {
Peer {
name: name.into(),
role,
tunnel_pubkey: String::new(),
overlay: nordisk_pairing::OVERLAY_BOX_A,
endpoint: None,
utility: utility.map(Ipv4Addr::from),
}
}
pub(crate) fn fed(feed: &[(&str, &str)], peers: Vec<Peer>) -> JoinReply {
JoinReply {
estate: "holger.test".into(),
me: Assigned {
name: "vm1".into(),
overlay: nordisk_pairing::OVERLAY_FRONT,
},
cert_pem: None,
role: Some(Role::Front),
primary_overlay: None,
primary_utility: None,
peers,
feed: feed
.iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect(),
}
}
fn estate() -> Vec<Peer> {
vec![
peer("vm1", Role::Front, None),
peer("vm2", Role::Primary, Some([10, 13, 8, 2])),
peer("vm3", Role::Twin, Some([10, 13, 8, 3])),
]
}
/// **The feed, read**: the console bearer (a weak one refused by name, never
/// presented), the primary's control plane and the twin by the verdict, the
/// certification target (the twin reads the primary's door; no second
/// election), and the repository route to the primary's 8443/50051.
#[test]
fn the_front_reads_its_feed_the_verdict_the_bearer_the_certification_and_the_route() {
// No shared bearer is fed any more (A7 row 14): an old feed's token is not read.
assert_eq!(
console_key::bearers_with(
Some([0x11; 32]),
&fed(&[("holger_console_token", TOKEN)], estate())
),
Ok(None)
);
let r = fed(
&[
("web_hosts", "holger.test,repo.holger.test"),
("repo_hosts", "repo.holger.test"),
],
estate(),
);
assert_eq!(
primary_control_in(&r, false).as_deref(),
Some("10.13.8.2:50051")
);
assert_eq!(twin_addr_in(&r, false), Some(Ipv4Addr::new(10, 13, 8, 3)));
assert_eq!(
primary_control_in(&fed(&[], vec![peer("vm1", Role::Front, None)]), false),
None,
"no verdict yet"
);
let t = certify::target_in(&r, false).expect("the verdict names both");
assert_eq!(
(
t.reader_ctl.as_str(),
t.source_ctl.as_str(),
t.become_twin_of.is_none(),
t.principal.as_str(),
t.purpose.as_str()
),
(
"10.13.8.3:50051",
"10.13.8.2:50051",
true,
"holger-twin",
"twin"
)
);
assert!(
certify::target_in(
&fed(
&[],
vec![
peer("vm2", Role::Primary, Some([10, 13, 8, 2])),
peer("vm3", Role::Twin, None)
]
),
false
)
.is_none(),
"a twin with no utility address is not dialled"
);
let web = estate_front::web::WebConfig::from("holger.test", &r.feed);
let route = estate_front::route::RepoRoute::of(
&r.feed,
&web,
PRODUCT,
Some((HTTP_PORT, CONTROL_PORT)),
CARRIED_GRPC,
);
let routed = route
.routed(nordisk_pairing::frontside::primary_utility(&r))
.expect("routed");
assert_eq!(
(
routed.hosts.clone(),
routed.http.to_string(),
routed.grpc.to_string()
),
(
vec!["repo.holger.test".to_string()],
"10.13.8.2:8443".to_string(),
"10.13.8.2:50051".to_string()
)
);
// RA5-1: Holger's services are carried, the identity core is not.
for rpc in [
"/holger.v1.RepositoryService/Get",
"/holger.grow.v1.GrowService/Status",
"/holger.token.v1.TokenService/Mint",
] {
assert!(
estate_front::web::serve::carries(&routed.grpc_services, rpc),
"{rpc}"
);
}
for rpc in [
"/nornir.identity.v1.IdentityService/SignUp",
"/nornir.identity.v1.IdentityService/CreateInvite",
"/grpc.reflection.v1.ServerReflection/ServerReflectionInfo",
] {
assert!(
!estate_front::web::serve::carries(&routed.grpc_services, rpc),
"{rpc} is not carried"
);
}
}
/// The console's wiring: the primary's two doors and the bearer (a weak one
/// refused by name, never presented).
#[cfg(feature = "console")]
#[test]
fn the_console_is_wired_from_the_feed() {
// The primary (vm2) offered its console key; the front derives its bearer (A7 row 14).
let primary_pub = wg_appliance_core::keys::encode_key(
&wg_appliance_core::keys::public_from_private_bytes(&[0x22; 32]),
);
let offered = fed(
&[("offer.vm2.console_box_pubkey", primary_pub.as_str())],
estate(),
);
let w = console::Wiring::of_with(Some([0x11; 32]), &offered);
let want = console_key::bearer(&[0x11; 32], &primary_pub).unwrap();
assert_eq!(
(
w.appliance.as_deref(),
w.door.as_deref(),
w.token.as_deref()
),
(
Some("10.13.8.2:50051"),
Some("10.13.8.2:8443"),
Some(want.as_str())
)
);
let unminted = console::Wiring::of_with(None, &offered);
assert!(
unminted.token.is_none(),
"no console key adopted yet: no bearer"
);
let bad = console::Wiring::of_with(
Some([0x11; 32]),
&fed(&[("offer.vm2.console_box_pubkey", "short")], estate()),
);
assert!(
bad.token.is_none()
&& bad
.refused
.iter()
.any(|l| l.contains("[holger-console-key-bad]")),
"{:?}",
bad.refused
);
assert!(
console::Wiring::of(&fed(&[], vec![peer("vm1", Role::Front, None)]))
.appliance
.is_none(),
"no verdict yet"
);
}
/// ★ The shop's sale check reads `/-/releases` with the bearer it fed the front; the
/// front passes it on with the primary's derived bearer, and touches no other bearer
/// (measured on the mock gate 2026-10-22: the main refused the shop's own, holger-front-3).
#[cfg(feature = "console")]
#[test]
fn the_shops_bearer_reaches_the_primary_as_the_primarys_own() {
let primary_pub = wg_appliance_core::keys::encode_key(
&wg_appliance_core::keys::public_from_private_bytes(&[0x22; 32]),
);
let offered = fed(
&[
("offer.vm2.console_box_pubkey", primary_pub.as_str()),
(console::SHOP_BEARER_FEED, TOKEN),
],
estate(),
);
let w = console::Wiring::of_with(Some([0x11; 32]), &offered);
let want = format!(
"Bearer {}",
console_key::bearer(&[0x11; 32], &primary_pub).unwrap()
);
assert_eq!(w.shop_bearer.as_deref(), Some(TOKEN));
assert_eq!(
console::present(&w, Some(&format!("Bearer {TOKEN}"))),
Some(want)
);
assert_eq!(
console::present(&w, Some("Bearer someone-elses-session")),
None,
"a person's bearer is passed on as it is"
);
assert_eq!(console::present(&w, None), None);
assert_eq!(console::present(&w, Some(TOKEN)), None, "only a bearer");
let unoffered = console::Wiring::of_with(
Some([0x11; 32]),
&fed(&[(console::SHOP_BEARER_FEED, TOKEN)], estate()),
);
assert_eq!(
console::present(&unoffered, Some(&format!("Bearer {TOKEN}"))),
None,
"no derived bearer yet: nothing to swap to"
);
assert_eq!(
console::present(
&console::Wiring::of_with(
Some([0x11; 32]),
&fed(
&[("offer.vm2.console_box_pubkey", primary_pub.as_str())],
estate()
)
),
Some(&format!("Bearer {TOKEN}"))
),
None,
"no shop bearer fed"
);
}
fn a_seed() -> nordisk_pairing::seed::Seed {
nordisk_pairing::seed::Seed {
estate: "holger.test".into(),
tunnel: nordisk_pairing::seed::TunnelKind::Wireguard,
front: Default::default(),
kind: nordisk_pairing::seed::Kind::Front,
installer_ca_pem: String::new(),
bootstrap_cert_pem: String::new(),
bootstrap_key_pem: String::new(),
peers_expected: 3,
mullvad: None,
tailscale: None,
installer_link: None,
}
}
/// ★ **A front KEEP-BOOTED onto its own data comes up FED** (lane G5, A7 row 6; Gunnar
/// ff58dad1 `a_front_keep_booted_onto_its_kept_data_comes_up_fed_and_can_reach_its_shop`):
/// a new ESP onto the kept data volume, the same estate: the feed (sealed), its own key,
/// the twin's certification record are kept, and the boot is marked once. A front
/// installed for ANOTHER estate onto kept data still starts unpaired.
#[test]
fn a_front_keep_booted_onto_its_kept_data_comes_up_fed() {
let data = tempfile::tempdir().unwrap();
let mut reply = fed(&[("web_hosts", "holger.test")], estate());
reply.feed.insert(
"shop_door.toml".into(),
"door = \"https://one.example:443\"\n".into(),
);
nordisk_pairing::fed_at_rest::write(
data.path(),
nordisk_pairing::boxside::JOINED_REL,
&reply.render().unwrap(),
)
.unwrap();
std::fs::write(
data.path().join(nordisk_pairing::boxside::OWN_KEY_REL),
"-----BEGIN PRIVATE KEY-----\nk\n",
)
.unwrap();
std::fs::write(data.path().join(certify::RECORD_REL), "kept = true\n").unwrap();
let mark = |p: &std::path::Path| nordisk_pairing::boxside::mark_esp(p);
let esp = tempfile::tempdir().unwrap();
std::fs::create_dir_all(esp.path().join("korp")).unwrap();
let cleared = first_boot(esp.path(), data.path(), &a_seed(), &mark);
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(),
"decided once"
);
let kept =
nordisk_pairing::fed_at_rest::read(data.path(), nordisk_pairing::boxside::JOINED_REL)
.and_then(|t| JoinReply::parse(&t).ok())
.expect("still fed");
assert!(
kept.feed.contains_key("shop_door.toml"),
"the shop door to pull UpdateTake from"
);
assert!(
data.path()
.join(nordisk_pairing::boxside::OWN_KEY_REL)
.exists()
&& data.path().join(certify::RECORD_REL).exists()
);
let esp2 = tempfile::tempdir().unwrap();
std::fs::create_dir_all(esp2.path().join("korp")).unwrap();
let other = nordisk_pairing::seed::Seed {
estate: "other.test".into(),
..a_seed()
};
let cleared = first_boot(esp2.path(), data.path(), &other, &mark).expect("a first boot");
assert!(
cleared.contains(&nordisk_pairing::boxside::JOINED_REL.to_string())
&& cleared.contains(&certify::RECORD_REL.to_string()),
"{cleared:?}"
);
}
}