aboutsummaryrefslogtreecommitdiffstats
path: root/src/api
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-07 22:35:21 +0100
committertv <tv@krebsco.de>2026-11-07 22:35:21 +0100
commit5471e5285e94ce7576e6c86f260fea15ed9503d1 (patch)
tree310be18d26611d6aaf6d8b02a118ba2fb6ef4aa1 /src/api
parentc3fde0fdd410902125f006f0a7d77ca030cb9061 (diff)
signatures: verified changes
GET /admin/changes shows whether a signature by its node vouches for each change. Auth plan M5.
Diffstat (limited to 'src/api')
-rw-r--r--src/api/changes.rs26
1 files changed, 16 insertions, 10 deletions
diff --git a/src/api/changes.rs b/src/api/changes.rs
index 419f5ed..c3a847d 100644
--- a/src/api/changes.rs
+++ b/src/api/changes.rs
@@ -2,8 +2,9 @@
//! or of what is at a path (as `/store/<path>` names it). Every change names the node that
//! committed it (its author) and, for a write through the API, the user who wrote it (in
//! its message, `User:`), with when (its writer's clock) and the store paths it touched
-//! (`document::replica::Writer`). Every node holds every change, so any node answers
-//! for the whole swarm, as far as it has synced.
+//! (`document::replica::Writer`), and whether a signature by that node vouches for it
+//! (`document::signatures`). Every node holds every change, so any node answers for the
+//! whole swarm, as far as it has synced.
use axum::{
Json,
@@ -39,6 +40,9 @@ pub(crate) struct ChangeReport {
/// The user it was written for through the API, if any.
#[serde(skip_serializing_if = "Option::is_none")]
user: Option<String>,
+ /// Whether a signature by its node vouches for it, as far as this node has the
+ /// signatures (`document::signatures`).
+ verified: bool,
/// The store paths it touched (a list's items by index).
paths: Vec<String>,
}
@@ -82,18 +86,19 @@ async fn report(
return Err(StatusCode::SERVICE_UNAVAILABLE);
}
let limit = query.limit.unwrap_or(100).clamp(1, MAX_LIMIT);
- Ok(Json(
- state
- .render(
- move |doc| changes(doc, limit, path.as_deref()),
- |report| report,
- )
- .await,
- ))
+ let doc = std::sync::Arc::clone(&state.doc);
+ let report = tokio::task::spawn_blocking(move || {
+ let replica = doc.blocking_read();
+ changes(&replica.doc, &replica.signatures, limit, path.as_deref())
+ })
+ .await
+ .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
+ Ok(Json(report))
}
fn changes(
doc: &crate::document::Document,
+ signatures: &crate::document::signatures::Signatures,
limit: usize,
path: Option<&[String]>,
) -> ChangesReport {
@@ -128,6 +133,7 @@ fn changes(
.as_deref()
.and_then(crate::document::replica::user_of)
.map(str::to_owned),
+ verified: signatures.verified(&meta.actor, meta.seq),
paths: paths.iter().map(|path| path.join("/")).collect(),
});
}