From 8a217a77f949adf603de32f104dd2c38df32d9d6 Mon Sep 17 00:00:00 2001 From: RoomWithOutRoof <166608075+Jah-yee@users.noreply.github.com> Date: Sat, 21 Mar 2026 06:22:19 +0800 Subject: [PATCH] fix(config): add challenge_max_attempts field to OtpConfig (#3921) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(docker): default CMD to daemon instead of gateway - Change default Docker CMD from gateway to daemon in both dev and release stages - gateway only starts the HTTP/WebSocket server — channel listeners (Matrix, Telegram, Discord, etc.) are never spawned - daemon starts the full runtime: gateway + channels + heartbeat + scheduler - Users who configure channels in config.toml and run the default image get no response because the channel sync loops never start * fix(config): add challenge_max_attempts field to OtpConfig Add missing challenge_max_attempts field to support OTP challenge attempt limiting. This field allows users to configure the maximum number of OTP challenge attempts before lockout. Fixes #3919 --------- Co-authored-by: Jah-yee Co-authored-by: Jah-yee --- src/config/schema.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/config/schema.rs b/src/config/schema.rs index f38298b34..50e8d2e93 100644 --- a/src/config/schema.rs +++ b/src/config/schema.rs @@ -5412,6 +5412,10 @@ fn default_otp_gated_actions() -> Vec { ] } +fn default_otp_challenge_max_attempts() -> u32 { + 3 +} + impl Default for OtpConfig { fn default() -> Self { Self { @@ -7418,6 +7422,9 @@ impl Config { "security.otp.cache_valid_secs must be greater than or equal to security.otp.token_ttl_secs" ); } + if self.security.otp.challenge_max_attempts == 0 { + anyhow::bail!("security.otp.challenge_max_attempts must be greater than 0"); + } for (i, action) in self.security.otp.gated_actions.iter().enumerate() { let normalized = action.trim(); if normalized.is_empty() {