aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-08 18:23:04 +0000
committertv <tv@krebsco.de>2026-11-08 19:29:26 +0100
commite9813db05f2cc8590b785639a3c41ba60812909a (patch)
tree01e2fb07f69147dac684eb1c5a4301aff9661eff /src
parentd14290ab6d0ea40b97cdb72f5e07354cc2721c45 (diff)
strict: no strict remake of a swarm strict already
Review 2, N2: nothing checked that a strict remake's base wasn't strict already. In a strict swarm the registry, the policy and the bans change only as a user who may change them signs; a strict remake committed by a majority of members, each with keys of its own, could drop any of them (unban, remove managers, empty userSigned and the sealed declarations), and the honest minority would follow. A node now takes a strict remake (proposing or voting) only in a swarm not strict yet: turning strict is done once, by a remake; after it, by signatures.
Diffstat (limited to 'src')
-rw-r--r--src/remake/api.rs8
-rw-r--r--src/remake/vote.rs17
2 files changed, 18 insertions, 7 deletions
diff --git a/src/remake/api.rs b/src/remake/api.rs
index d3df908..1da0539 100644
--- a/src/remake/api.rs
+++ b/src/remake/api.rs
@@ -383,22 +383,24 @@ 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
+ let (swarm, strict) = host
.read_doc(|doc| {
- crate::document::store::read_path(
+ let swarm = 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()
+ .unwrap_or_default();
+ (swarm, crate::access::policy::of(doc).strict())
})
.await;
if let Err(why) = super::vote::strict_taken(
&host,
prepared.content.as_deref(),
&swarm,
+ strict,
&EpochRecord::from(host.current_epoch()),
request.base_heads.as_deref(),
) {
diff --git a/src/remake/vote.rs b/src/remake/vote.rs
index 7e678e6..ac94306 100644
--- a/src/remake/vote.rs
+++ b/src/remake/vote.rs
@@ -281,7 +281,7 @@ pub(super) async fn cast<H: Host>(host: &H) {
// Usually there is nothing to vote on: look first, without writing.
let (voter, wanted) = (me.clone(), epoch.clone());
let now = host.now_millis();
- let (due, missed, swarm) = host
+ let (due, missed, (swarm, strict)) = host
.read_doc(move |doc| {
let all = records::load_all(doc);
let register = records::load_register(doc);
@@ -304,7 +304,8 @@ pub(super) async fn cast<H: Host>(host: &H) {
.flatten()
.and_then(|swarm| swarm.as_str().map(str::to_owned))
.unwrap_or_default();
- (due, missed, swarm)
+ let strict = crate::access::policy::of(doc).strict();
+ (due, missed, (swarm, strict))
})
.await;
for id in missed {
@@ -336,6 +337,7 @@ pub(super) async fn cast<H: Host>(host: &H) {
host,
bytes.as_deref().map(Vec::as_slice),
&swarm,
+ strict,
&proposal.from_epoch,
proposal.base_heads.as_deref(),
)
@@ -381,15 +383,22 @@ pub(super) async fn cast<H: Host>(host: &H) {
}
/// Whether this node takes a strict remake's manifest (`content`, for `swarm`): signed
-/// by one of its `--strict-keys`, for the remake's epoch and base (`epoch`, `base`). Why
-/// not, if not.
+/// by one of its `--strict-keys`, for the remake's epoch and base (`epoch`, `base`), in
+/// a swarm not strict yet (`strict`: once it is, its registry, policy and bans change by
+/// their users' signatures, not by a majority of members). Why not, if not.
pub(super) fn strict_taken<H: Host>(
host: &H,
content: Option<&[u8]>,
swarm: &str,
+ strict: bool,
epoch: &EpochRecord,
base: Option<&[String]>,
) -> Result<(), String> {
+ if strict {
+ return Err(
+ "the swarm is strict already: its registry changes by its users' signatures".to_owned(),
+ );
+ }
let text = content
.and_then(|bytes| std::str::from_utf8(bytes).ok())
.ok_or("no manifest")?;