crates/gunnar-front/src/keys.rs
//! **The keys the front mints itself** and offers the installer — never fed.
//!
//! Moved here from gunnar-server's `front_role` (lane A 54fa7bd), where the
//! front's role wrongly lived on the gunnar medium.
use std::path::Path;
/// Under the data root: the front's certify key (OpenSSH, 0600).
pub const CERTIFY_KEY_REL: &str = "pairing/certify.key";
/// Under the data root: the certify record (what the old door kept beside
/// its ledger).
pub const CERTIFY_RECORD_REL: &str = "pairing/certify.toml";
/// **The front's certify key**, minted (0600) here if absent. Its public half
/// is offered as `certify_pubkey`; the elected primary installs it as the
/// machine admin `front-certify`, and the twin's enrolment and certificate
/// are signed with it.
pub fn certify_key(root: &Path) -> anyhow::Result<gunnar_core::ssh_key::PrivateKey> {
// ONE writer (release-3, MEASURED on the mock): the installer's first visit
// (`offer`) and the role thread asked at once, both wrote `certify.key.tmp`,
// and the role kept a key that was never offered. The mint is serialized,
// and every caller returns the key that is ON DISK after it.
static MINT: std::sync::Mutex<()> = std::sync::Mutex::new(());
let _one = MINT.lock().unwrap_or_else(|p| p.into_inner());
let path = root.join(CERTIFY_KEY_REL);
if !std::fs::read(&path).map(|b| !b.is_empty()).unwrap_or(false) {
let pem = gunnar_core::mint_ed25519()?;
gunnar_server::pairing::write_secret(root, CERTIFY_KEY_REL, std::str::from_utf8(&pem)?)?;
}
let pem = std::fs::read(&path).map_err(|e| anyhow::anyhow!("{}: {e}", path.display()))?;
gunnar_core::ssh_key::PrivateKey::from_openssh(&pem)
.map_err(|e| anyhow::anyhow!("{}: {e}", path.display()))
}
/// Its public key line — what the front offers the installer.
pub fn certify_pubkey(root: &Path) -> anyhow::Result<String> {
let mut line = certify_key(root)?
.public_key()
.to_openssh()
.map_err(|e| anyhow::anyhow!("{e}"))?;
line.push(' ');
line.push_str(gunnar_pairing_door::front::CERTIFY_PRINCIPAL);
Ok(line)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_certify_key_is_minted_once_0600_and_offered_as_one_line() {
use std::os::unix::fs::PermissionsExt as _;
let dir = tempfile::tempdir().unwrap();
let line = certify_pubkey(dir.path()).unwrap();
assert!(
line.starts_with("ssh-ed25519 ") && line.ends_with(" front-certify"),
"{line}"
);
assert_eq!(certify_pubkey(dir.path()).unwrap(), line, "minted once");
let mode = std::fs::metadata(dir.path().join(CERTIFY_KEY_REL))
.unwrap()
.permissions()
.mode()
& 0o777;
assert_eq!(mode, 0o600);
// What the primary installs from the offer is a valid fed principal.
let feed = std::collections::BTreeMap::from([(
format!(
"offer.vm1.{}",
gunnar_pairing_door::front::OFFER_CERTIFY_PUBKEY
),
line,
)]);
let p = gunnar_server::fed_principals::parse(&feed).unwrap();
assert_eq!(p[0].id, gunnar_pairing_door::front::CERTIFY_PRINCIPAL);
}
/// ★ release-3 (MEASURED on the dwarf mock 2026-09-29, install-orig-5):
/// the installer's first visit (`offer`) and the front's role thread minted
/// the certify key AT ONCE; both wrote `certify.key.tmp`, one rename lost
/// ("replacing /data/pairing/certify.key: No such file"), and the role
/// signed with a key that was never offered — every BecomeTwin refused
/// [bootstrap-unauthenticated]. However many ask at once, ONE key exists:
/// every caller gets the key that is on disk.
#[test]
fn a_certify_key_minted_by_many_at_once_is_one_key_and_the_one_on_disk() {
for _ in 0..20 {
let dir = tempfile::tempdir().unwrap();
let root = dir.path().to_path_buf();
let barrier = std::sync::Arc::new(std::sync::Barrier::new(8));
let got: Vec<String> = (0..8)
.map(|_| {
let (root, barrier) = (root.clone(), std::sync::Arc::clone(&barrier));
std::thread::spawn(move || {
barrier.wait();
certify_key(&root)
.map(|k| k.public_key().to_openssh().unwrap())
.unwrap_or_else(|e| format!("ERR {e:#}"))
})
})
.collect::<Vec<_>>()
.into_iter()
.map(|h| h.join().unwrap())
.collect();
let on_disk = certify_key(&root)
.unwrap()
.public_key()
.to_openssh()
.unwrap();
assert!(
got.iter().all(|g| *g == on_disk),
"one key, the one on disk: {got:?} vs {on_disk}"
);
}
}
}
#[cfg(test)]
mod kept_tests {
/// **A new installation on a kept /data keeps the front's certify key**
/// (stinkies upgrade-front-certify-reminted + recharge-front-new-key-deadlock,
/// estate-box U3): the first boot of a keep-upgrade cleared `pairing/certify.key`
/// (gunnar-front PAIRING_STATE), so the front offered a NEW certify_pubkey, main
/// refused it (a second key on `front-certify` needs its attestation) and the twin
/// could not be certified; its fresh record re-certified a twin that was certified.
/// The front keeps every identity key where its disk survives (certify, monetize,
/// console, guest, clock) — as a gunnar box keeps `wg0.key` — and clears only
/// what the installer fed and the box's own certificate.
#[test]
fn a_fresh_installation_on_a_kept_disk_keeps_the_certify_key_and_its_record() {
let esp = tempfile::tempdir().unwrap();
std::fs::create_dir_all(esp.path().join("korp")).unwrap();
let data = tempfile::tempdir().unwrap();
let before = super::certify_pubkey(data.path()).unwrap();
std::fs::write(
data.path().join(super::CERTIFY_RECORD_REL),
"certified = true\n",
)
.unwrap();
let cleared = gunnar_server::installer_listener::reset_if_fresh_install_of(
esp.path(),
data.path(),
&crate::PAIRING_STATE,
&|p| std::fs::write(p, b"x").is_ok(),
)
.expect("a first boot");
assert!(
!cleared
.iter()
.any(|c| c == super::CERTIFY_KEY_REL || c == super::CERTIFY_RECORD_REL),
"{cleared:?}"
);
assert_eq!(
super::certify_pubkey(data.path()).unwrap(),
before,
"the same certify key after the new installation"
);
assert!(
data.path().join(super::CERTIFY_RECORD_REL).exists(),
"the record kept: an unchanged pair is not certified again"
);
}
}