feat(fips): surface anchor connectivity + peer count in FipsStatus
Two new fields on the /rpc fips.status payload: - authenticated_peer_count: how many FIPS peers the daemon has an authenticated session to right now. 0 means isolated / not on the mesh; >0 means traffic to any known npub can DHT-route. - anchor_connected: true when the public anchor (fips.v0l.io, npub1zv58cn7…) is present in the daemon's identity cache. The anchor bootstraps DHT routing for general-case deployments, so this is the best single-value indicator the UI can show for "will federation traffic over FIPS work between previously- unknown peers?" Implementation: fips::service::peer_connectivity_summary shells out to `sudo -n fipsctl show peers` + `... show identity-cache` (archipelago user already has NOPASSWD:ALL per the ISO sudoers and live fleet nodes, confirmed). Failure returns (0, false) so the UI degrades to "unknown" state without crashing. Only queried when service_active — pre-onboarding / daemon-down nodes skip the fipsctl call entirely. UI side (FipsNetworkCard) consumes the full status JSON, so the two new fields are available via existing prop plumbing; visual treatment can come later. Also fixes ISO build (commit 3e04456c wasn't sufficient): the Dockerfile needs `cargo build --release --bins` — upstream FIPS added a `fips-gateway` binary target, and plain `cargo build --release` only builds the default bin list, which caused `cargo deb --no-build` to fail hunting for the missing binary. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ec5f14166a
commit
122d00f81e
@ -82,6 +82,18 @@ pub struct FipsStatus {
|
|||||||
/// present; falls back to the upstream daemon's own key on legacy
|
/// present; falls back to the upstream daemon's own key on legacy
|
||||||
/// nodes where `/etc/fips/fips.pub` is readable.
|
/// nodes where `/etc/fips/fips.pub` is readable.
|
||||||
pub npub: Option<String>,
|
pub npub: Option<String>,
|
||||||
|
/// Number of currently authenticated FIPS peers, per
|
||||||
|
/// `fipsctl show peers`. 0 → isolated / anchor unreachable;
|
||||||
|
/// >0 → DHT routing is viable.
|
||||||
|
#[serde(default)]
|
||||||
|
pub authenticated_peer_count: u32,
|
||||||
|
/// True when at least one peer in the identity cache is a known
|
||||||
|
/// public anchor (currently `fips.v0l.io`). Anchors bootstrap DHT
|
||||||
|
/// routing for general-case deployments, so a red anchor status is
|
||||||
|
/// the top UX indicator of "FIPS traffic will probably degrade to
|
||||||
|
/// Tor until the anchor is reachable."
|
||||||
|
#[serde(default)]
|
||||||
|
pub anchor_connected: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FipsStatus {
|
impl FipsStatus {
|
||||||
@ -106,6 +118,12 @@ impl FipsStatus {
|
|||||||
_ => service::read_upstream_npub().await.ok().flatten(),
|
_ => service::read_upstream_npub().await.ok().flatten(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let (authenticated_peer_count, anchor_connected) = if service_active {
|
||||||
|
service::peer_connectivity_summary().await
|
||||||
|
} else {
|
||||||
|
(0, false)
|
||||||
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
installed,
|
installed,
|
||||||
version,
|
version,
|
||||||
@ -114,6 +132,8 @@ impl FipsStatus {
|
|||||||
service_active,
|
service_active,
|
||||||
key_present,
|
key_present,
|
||||||
npub,
|
npub,
|
||||||
|
authenticated_peer_count,
|
||||||
|
anchor_connected,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -103,6 +103,61 @@ pub async fn mask(unit: &str) -> Result<()> {
|
|||||||
sudo_systemctl("mask", unit).await
|
sudo_systemctl("mask", unit).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Known public anchor npub (fips.v0l.io as of 2026-04). Used to decide
|
||||||
|
/// whether the `anchor_connected` badge in the dashboard lights up.
|
||||||
|
pub const PUBLIC_ANCHOR_NPUB: &str =
|
||||||
|
"npub1zv58cn7v83mxvttl70w5fwjwuclfmntv9cnmv5wmz2nzz88u5urqvdx96n";
|
||||||
|
|
||||||
|
/// Summarise peer connectivity from `fipsctl show peers` + `identity-cache`.
|
||||||
|
/// Returns `(authenticated_peer_count, anchor_connected)`. Shells out rather
|
||||||
|
/// than embedding a fips client because fipsctl is the daemon's own ground
|
||||||
|
/// truth — the daemon can always rewrite its internal routing and we'd
|
||||||
|
/// rather be consistent with `fipsctl` than snapshot it ourselves.
|
||||||
|
pub async fn peer_connectivity_summary() -> (u32, bool) {
|
||||||
|
let peers_json = match Command::new("sudo")
|
||||||
|
.args(["-n", "fipsctl", "show", "peers"])
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(o) if o.status.success() => o.stdout,
|
||||||
|
_ => return (0, false),
|
||||||
|
};
|
||||||
|
let authenticated_peer_count =
|
||||||
|
match serde_json::from_slice::<serde_json::Value>(&peers_json) {
|
||||||
|
Ok(v) => v
|
||||||
|
.get("peers")
|
||||||
|
.and_then(|p| p.as_array())
|
||||||
|
.map(|a| a.len() as u32)
|
||||||
|
.unwrap_or(0),
|
||||||
|
Err(_) => 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Anchor check: look in identity-cache (known node pubkeys the daemon
|
||||||
|
// has heard about) rather than authenticated peers — the anchor may be
|
||||||
|
// in the cache but not currently at session depth.
|
||||||
|
let cache_json = match Command::new("sudo")
|
||||||
|
.args(["-n", "fipsctl", "show", "identity-cache"])
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(o) if o.status.success() => o.stdout,
|
||||||
|
_ => return (authenticated_peer_count, false),
|
||||||
|
};
|
||||||
|
let anchor_connected = match serde_json::from_slice::<serde_json::Value>(&cache_json) {
|
||||||
|
Ok(v) => v
|
||||||
|
.get("entries")
|
||||||
|
.and_then(|e| e.as_array())
|
||||||
|
.map(|entries| {
|
||||||
|
entries
|
||||||
|
.iter()
|
||||||
|
.any(|e| e.get("npub").and_then(|n| n.as_str()) == Some(PUBLIC_ANCHOR_NPUB))
|
||||||
|
})
|
||||||
|
.unwrap_or(false),
|
||||||
|
Err(_) => false,
|
||||||
|
};
|
||||||
|
(authenticated_peer_count, anchor_connected)
|
||||||
|
}
|
||||||
|
|
||||||
/// Read the upstream daemon's public key at `/etc/fips/fips.pub` and return
|
/// Read the upstream daemon's public key at `/etc/fips/fips.pub` and return
|
||||||
/// it as a bech32 npub. Returns `Ok(None)` if the file doesn't exist — used
|
/// it as a bech32 npub. Returns `Ok(None)` if the file doesn't exist — used
|
||||||
/// as a fallback on legacy/dev nodes where no seed-derived key exists.
|
/// as a fallback on legacy/dev nodes where no seed-derived key exists.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user