summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-10-26 22:57:47 +0100
committertv <tv@krebsco.de>2026-10-26 22:57:47 +0100
commitc11324bef3e93aac3748c6bb62c4a60872ea3f62 (patch)
treebda81563aad5877f5f4fa3b8bc8bcac92ed60658
parent282eda719a303b4f5ae8ae05f0c11cac7b4a52ea (diff)
peers in /peers is an attrset
-rw-r--r--docs/openapi.yaml11
-rw-r--r--src/api/peers.rs52
2 files changed, 33 insertions, 30 deletions
diff --git a/docs/openapi.yaml b/docs/openapi.yaml
index ae28c91..bfb297d 100644
--- a/docs/openapi.yaml
+++ b/docs/openapi.yaml
@@ -210,7 +210,7 @@ paths:
summary: List known peers
responses:
'200':
- description: Peers, sorted by public key.
+ description: Peers, by public key.
content:
application/json:
schema:
@@ -1080,16 +1080,15 @@ components:
type: integer
description: 'Heads of the local document (more than one: concurrent changes not yet merged).'
peers:
- type: array
- items:
+ type: object
+ description: By public key.
+ additionalProperties:
$ref: '#/components/schemas/Peer'
Peer:
type: object
- required: [publicKey, status, handshakeComplete, hasPendingDelivery, neighbour, traffic, incidents]
+ required: [status, handshakeComplete, hasPendingDelivery, neighbour, traffic, incidents]
properties:
- publicKey:
- type: string
alias:
type: [string, 'null']
description: The name the peer gave itself (`--alias`); not unique.
diff --git a/src/api/peers.rs b/src/api/peers.rs
index f0fd1d9..bc32ae1 100644
--- a/src/api/peers.rs
+++ b/src/api/peers.rs
@@ -1,5 +1,7 @@
//! /peers handlers.
+use std::collections::BTreeMap;
+
use axum::{
Json,
extract::{Path, Query, State},
@@ -44,7 +46,6 @@ impl From<Epoch> for EpochInfo {
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct PeerStatus {
- public_key: String,
/// The name the peer gave itself in the member list, if any (not unique).
alias: Option<String>,
validated_swarm: Option<String>,
@@ -58,7 +59,7 @@ pub(crate) struct PeerStatus {
/// Whether that epoch equals ours, i.e. whether we sync with this peer.
same_epoch: Option<bool>,
/// Number of heads in its last keepalive; absent before one, or when it had more
- /// than 32 (then only their digest is sent).
+ /// than 32 (then only their digest and some of them are sent).
heads: Option<usize>,
/// We keep it alive and sync with it on our own; other peers we only answer.
neighbour: bool,
@@ -92,7 +93,8 @@ pub(crate) struct PeerListResponse {
/// Number of heads of our document. Several heads are concurrent changes no later
/// change has merged yet.
local_heads: usize,
- peers: Vec<PeerStatus>,
+ /// By public key.
+ peers: BTreeMap<String, PeerStatus>,
}
#[derive(Debug, Deserialize)]
@@ -110,7 +112,7 @@ pub(crate) async fn list_peers(
let now = Instant::now();
let local_epoch = state.local_epoch();
let mut aliases = crate::swarm::members::aliases(&state.doc.read().await.doc);
- let mut peers = Vec::with_capacity(snapshot.len());
+ let mut peers = BTreeMap::new();
for (public_key, peer_state_mutex) in snapshot {
let neighbour = crate::swarm::topology::is_neighbour(&state, &public_key);
@@ -138,29 +140,31 @@ pub(crate) async fn list_peers(
),
};
- peers.push(PeerStatus {
- alias: aliases.remove(&public_key),
+ let alias = aliases.remove(&public_key);
+ peers.insert(
public_key,
- validated_swarm: ps.standing.swarm_id().map(str::to_owned),
- status,
- handshake_complete: ps.hello_state == HelloState::Established,
- last_seen_secs_ago: ps
- .last_seen
- .map(|t| now.saturating_duration_since(t).as_secs()),
- dormant_for_secs,
- has_pending_delivery: ps.sync.sending.is_some() || ps.sync.retry_at.is_some(),
- epoch: ps.epoch.map(EpochInfo::from),
- same_epoch: ps.epoch.map(|epoch| epoch == local_epoch),
- heads: ps.reported_heads,
- neighbour,
- rtt_ms,
- traffic,
- incidents: ps.incidents,
- sync_throughput,
- });
+ PeerStatus {
+ alias,
+ validated_swarm: ps.standing.swarm_id().map(str::to_owned),
+ status,
+ handshake_complete: ps.hello_state == HelloState::Established,
+ last_seen_secs_ago: ps
+ .last_seen
+ .map(|t| now.saturating_duration_since(t).as_secs()),
+ dormant_for_secs,
+ has_pending_delivery: ps.sync.sending.is_some() || ps.sync.retry_at.is_some(),
+ epoch: ps.epoch.map(EpochInfo::from),
+ same_epoch: ps.epoch.map(|epoch| epoch == local_epoch),
+ heads: ps.reported_heads,
+ neighbour,
+ rtt_ms,
+ traffic,
+ incidents: ps.incidents,
+ sync_throughput,
+ },
+ );
}
- peers.sort_by(|a, b| a.public_key.cmp(&b.public_key));
let local_heads = state.doc.read().await.heads().len();
Ok(Json(PeerListResponse {
local_alias: state.alias.clone(),