release(v1.7.22-alpha): honest anchor status + Reconnect works on all nodes
- fips::service::active_unit() picks whichever fips unit is running
(archipelago-fips.service vs upstream fips.service) so
handle_fips_restart and handle_fips_reconnect don't silently no-op
on hosts where the archipelago-managed unit was never created.
- peer_connectivity_summary(anchor_candidates) replaces the old
identity-cache check. anchor_connected is now true when at least
one authenticated peer's npub matches the public anchor OR any
entry in seed-anchors.json, which matches what the user actually
cares about ("am I in the mesh?") rather than what the card used
to claim ("is this one specific public anchor reachable?").
- FipsStatus::query takes data_dir now (so it can read seed-anchors)
rather than identity_dir. All call-sites updated.
- handle_fips_reconnect re-pushes seed anchors after restart so the
new daemon gets dialed without waiting for the 5-min apply loop.
- FipsNetworkCard label drops "(fips.v0l.io)" — misleading now that
multiple anchors may be configured.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -97,6 +97,27 @@ pub async fn restart(unit: &str) -> Result<()> {
|
||||
sudo_systemctl("restart", unit).await
|
||||
}
|
||||
|
||||
/// Resolve which systemd unit is actually supervising the fips daemon
|
||||
/// on this host. Nodes installed from the archipelago ISO run
|
||||
/// `archipelago-fips.service`; nodes that were apt-installed (or had
|
||||
/// fips running before archipelago took over) may only have the
|
||||
/// upstream `fips.service`. Restart/Reconnect must operate on whichever
|
||||
/// one is running, otherwise the UI button is a silent no-op.
|
||||
///
|
||||
/// Returns the archipelago-managed unit name if it's active,
|
||||
/// else the upstream unit name if that's active,
|
||||
/// else the archipelago-managed name as a default (so activate() can
|
||||
/// bring it up).
|
||||
pub async fn active_unit() -> &'static str {
|
||||
if unit_state(super::SERVICE_UNIT).await == "active" {
|
||||
return super::SERVICE_UNIT;
|
||||
}
|
||||
if unit_state(super::UPSTREAM_SERVICE_UNIT).await == "active" {
|
||||
return super::UPSTREAM_SERVICE_UNIT;
|
||||
}
|
||||
super::SERVICE_UNIT
|
||||
}
|
||||
|
||||
pub async fn mask(unit: &str) -> Result<()> {
|
||||
let _ = sudo_systemctl("stop", unit).await;
|
||||
let _ = sudo_systemctl("disable", unit).await;
|
||||
@@ -108,12 +129,19 @@ pub async fn mask(unit: &str) -> Result<()> {
|
||||
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) {
|
||||
/// Summarise peer connectivity from `fipsctl show peers`. Returns
|
||||
/// `(authenticated_peer_count, anchor_connected)`.
|
||||
///
|
||||
/// `anchor_candidates` is the operator-controlled list of npubs this
|
||||
/// node considers a valid mesh anchor — always includes the hard-coded
|
||||
/// public anchor, plus any entries from `seed-anchors.json`. A node is
|
||||
/// "anchor connected" when at least one currently-authenticated peer
|
||||
/// matches one of these npubs. We used to check the identity cache
|
||||
/// (which includes transient hearsay from other peers), but a cache
|
||||
/// hit on `fips.v0l.io` didn't mean we could actually route through
|
||||
/// it, and the card lied to users whose mesh was federated through
|
||||
/// their own seed anchors instead.
|
||||
pub async fn peer_connectivity_summary(anchor_candidates: &[String]) -> (u32, bool) {
|
||||
let peers_json = match Command::new("sudo")
|
||||
.args(["-n", "fipsctl", "show", "peers"])
|
||||
.output()
|
||||
@@ -122,39 +150,26 @@ pub async fn peer_connectivity_summary() -> (u32, bool) {
|
||||
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,
|
||||
let parsed: serde_json::Value =
|
||||
match serde_json::from_slice(&peers_json) {
|
||||
Ok(v) => v,
|
||||
Err(_) => return (0, false),
|
||||
};
|
||||
|
||||
// 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,
|
||||
};
|
||||
let peers = parsed
|
||||
.get("peers")
|
||||
.and_then(|p| p.as_array())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let authenticated_peer_count = peers.len() as u32;
|
||||
let anchor_connected = peers.iter().any(|p| {
|
||||
let npub = p.get("npub").and_then(|n| n.as_str()).unwrap_or_default();
|
||||
let connected = p
|
||||
.get("connectivity")
|
||||
.and_then(|c| c.as_str())
|
||||
.map(|s| s == "connected")
|
||||
.unwrap_or(true);
|
||||
connected && anchor_candidates.iter().any(|a| a == npub)
|
||||
});
|
||||
(authenticated_peer_count, anchor_connected)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user