aboutsummaryrefslogtreecommitdiffstats
path: root/src/remake/api.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/remake/api.rs')
-rw-r--r--src/remake/api.rs68
1 files changed, 68 insertions, 0 deletions
diff --git a/src/remake/api.rs b/src/remake/api.rs
index 7888c37..79b551f 100644
--- a/src/remake/api.rs
+++ b/src/remake/api.rs
@@ -8,6 +8,11 @@
//! * `GET /admin/remakes` lists all remakes.
//! * `GET /admin/remakes/{id}` reports one; `?wait=true` long-polls until decided
//! (bounded by the proposal's own expiry).
+//! * `GET /admin/strict-manifest`: a manifest turning the swarm strict (`access::strict`),
+//! keeping every value at the places values are signed as they are now, for the
+//! caller to look over and sign; with those values and the heads they are at, the
+//! base to propose it with (`{"kind": "strict", "spec": {"manifest": …}, "baseHeads":
+//! …}`).
//! * `GET /admin/epochs` shows the local epoch, the archived previous ones and whether
//! the node is catching up with a newer epoch from a peer.
//! * `DELETE /admin/remakes/{id}` asks the coordinator to abort. Only possible while
@@ -374,6 +379,29 @@ async fn start_kind<H: Host>(
}
};
+ // A strict remake: proposed only with a manifest this node would vote for.
+ if kind.name() == "strict" {
+ let swarm = host
+ .read_doc(|doc| {
+ crate::document::store::read_path(
+ doc,
+ &crate::document::meta::path(&[crate::document::meta::SWARM]),
+ )
+ .ok()
+ .flatten()
+ .and_then(|swarm| swarm.as_str().map(str::to_owned))
+ .unwrap_or_default()
+ })
+ .await;
+ if let Err(why) = super::vote::strict_taken(&host, prepared.content.as_deref(), &swarm) {
+ return error_response(
+ StatusCode::FORBIDDEN,
+ format!("this node takes no such manifest: {why}"),
+ None,
+ );
+ }
+ }
+
if kind.base_at_commit() && request.base_heads.is_some() {
return error_response(
StatusCode::BAD_REQUEST,
@@ -782,6 +810,46 @@ async fn abort_expired<H: Host>(host: &H, current: Report, force: &Force) -> Res
}
}
+/// `GET /admin/strict-manifest`.
+pub(crate) async fn strict_manifest<H: Host>(
+ State(host): State<H>,
+ axum::Extension(user): axum::Extension<crate::api::auth::AuthenticatedUser>,
+) -> Response {
+ let at = host.now_millis().as_u64();
+ let caller = user.0.clone();
+ let (manifest, values, heads) = host
+ .read_doc(move |doc| {
+ let read = |path: &[&str]| {
+ crate::document::store::read_plain(doc, &crate::document::meta::path(path))
+ .ok()
+ .flatten()
+ };
+ let swarm = read(&[crate::document::meta::SWARM])
+ .and_then(|swarm| swarm.as_str().map(str::to_owned))
+ .unwrap_or_default();
+ let mut policy = match read(&[crate::document::meta::ACCESS, "policy"]) {
+ Some(JsonValue::Object(policy)) => JsonValue::Object(policy),
+ _ => json!({"version": 1}),
+ };
+ policy["values"] = json!("signed");
+ let manifest = crate::access::strict::manifest_of(doc, &swarm, policy, &caller, at);
+ let values: Vec<JsonValue> = crate::access::strict::held(doc)
+ .into_iter()
+ .map(|(path, value)| json!({ "path": path, "value": value }))
+ .collect();
+ let heads = records::encode_heads(&doc.get_heads());
+ (manifest, values, heads)
+ })
+ .await;
+ Json(json!({
+ "manifest": manifest,
+ "values": values,
+ "heads": heads,
+ "namespace": crate::access::strict::NAMESPACE,
+ }))
+ .into_response()
+}
+
/// `GET /admin/epochs`: the local epoch, the previous epochs this node still keeps, and
/// an ongoing catch-up with a peer's newer epoch (`null` if none).
pub(crate) async fn epochs<H: Host>(State(host): State<H>) -> Response {