From 83171090a0ff9cb9aeca3c1f91edff769076a5a7 Mon Sep 17 00:00:00 2001 From: tv Date: Sun, 8 Nov 2026 19:26:29 +0000 Subject: state: limit refused writes per user A write every node would refuse is rolled back by forking the document at the heads before it, under the write lock, at a cost that grows with the document's history. A user could ask for such writes in a loop. Count each user's refused writes over the last minute. Past --refused-writes-per-minute (default 10), its writes are answered 429 without being tried, until the oldest refusal leaves the minute. --- src/config/mod.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src/config/mod.rs') diff --git a/src/config/mod.rs b/src/config/mod.rs index f6d3e15..572960b 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -179,6 +179,13 @@ pub(crate) const OPTIONS: &[OptionSpec] = &[ None, "With bootstrap-users: their SSH keys, as authorized_keys lines (ssh-ed25519 [comment], named by the comment), recorded in their entries: to log in by key and sign values (docs/access.md), so a swarm can be strict from its start.", ), + option( + "refused-writes-per-minute", + Kind::Integer, + "COUNT", + Some("10"), + "Writes a user may have refused within a minute because every node would refuse them (docs/access.md); past it, its writes are answered 429 untried for a while. Each refusal costs a copy of the document.", + ), option( "genesis-root", Kind::Text, @@ -633,6 +640,8 @@ pub(crate) struct Settings { pub(crate) challenge_lifetime: Duration, #[serde(default = "default_key_logins_per_user")] pub(crate) key_logins_per_user: usize, + #[serde(default = "default_refused_writes_per_minute")] + pub(crate) refused_writes_per_minute: usize, #[serde(default)] pub(crate) debug_api: bool, #[serde(default = "default_log_format")] @@ -799,6 +808,9 @@ fn default_session_lifetime() -> Duration { fn default_challenge_lifetime() -> Duration { Duration::from_secs(5 * 60) } +fn default_refused_writes_per_minute() -> usize { + 10 +} fn default_key_logins_per_user() -> usize { 100 } -- cgit v1.3.1