diff options
| author | tv <tv@krebsco.de> | 2026-11-08 18:17:14 +0000 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-11-08 19:29:10 +0100 |
| commit | 96ba15cf1a6f8725f70f429695a5a9661f706eb0 (patch) | |
| tree | 36feecb42a8bfa5317f27a052266f74489312dfc /src/remake/api.rs | |
| parent | 4ce3bde36bb8283d82b914f0f73e30d89c935113 (diff) | |
strict: a manifest serves one remake, at its base
Review 2, N1: a strict manifest wasn't bound to its base. Any member
could propose an old, legitimately signed one again at the current
heads; nodes would verify it and vote, and every value at a signed
place changed or added since (a ban, a new user, a rotated key, a
changed userSigned or sealed declaration) would be deleted, nothing
after the base being left to replay.
The manifest now names the epoch and the heads it was made at, and a
node takes it (proposing or voting) only for a remake of that epoch
with exactly that base. GET /admin/strict-manifest makes it so;
continuum-signed strict proposes at those heads already.
Diffstat (limited to 'src/remake/api.rs')
| -rw-r--r-- | src/remake/api.rs | 15 |
1 files changed, 12 insertions, 3 deletions
diff --git a/src/remake/api.rs b/src/remake/api.rs index 79b551f..7aeb675 100644 --- a/src/remake/api.rs +++ b/src/remake/api.rs @@ -393,7 +393,13 @@ async fn start_kind<H: Host>( .unwrap_or_default() }) .await; - if let Err(why) = super::vote::strict_taken(&host, prepared.content.as_deref(), &swarm) { + if let Err(why) = super::vote::strict_taken( + &host, + prepared.content.as_deref(), + &swarm, + &EpochRecord::from(host.current_epoch()), + request.base_heads.as_deref(), + ) { return error_response( StatusCode::FORBIDDEN, format!("this node takes no such manifest: {why}"), @@ -817,6 +823,7 @@ pub(crate) async fn strict_manifest<H: Host>( ) -> Response { let at = host.now_millis().as_u64(); let caller = user.0.clone(); + let epoch = EpochRecord::from(host.current_epoch()); let (manifest, values, heads) = host .read_doc(move |doc| { let read = |path: &[&str]| { @@ -832,12 +839,14 @@ pub(crate) async fn strict_manifest<H: Host>( _ => json!({"version": 1}), }; policy["values"] = json!("signed"); - let manifest = crate::access::strict::manifest_of(doc, &swarm, policy, &caller, at); + let heads = doc.get_heads(); + let manifest = + crate::access::strict::manifest_of(doc, &swarm, epoch, &heads, 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()); + let heads = records::encode_heads(&heads); (manifest, values, heads) }) .await; |
