Class: ActiveHarness::RateLimit::RiskHoldback
- Inherits:
-
Object
- Object
- ActiveHarness::RateLimit::RiskHoldback
- Defined in:
- lib/active_harness/rate_limit/risk_holdback.rb
Overview
Progressive hold-back for users who repeatedly submit risky requests.
Every RISKY_THRESHOLD blocked requests trigger a hold. The hold duration escalates with each offense:
Offense 1 (3rd risky request) → 5 minutes
Offense 2 (6th risky request) → 30 minutes
Offense 3+ (9th+ risky request) → 2 hours
Storage is in-memory (per-process). Thread-safe via Mutex.
Usage:
holdback = RiskHoldback.new
holdback.check!(user_id) # before each request; raises if held
holdback.record_risky!(user_id) # after guard blocks a request
Constant Summary collapse
- RISKY_THRESHOLD =
3- HOLD_SCHEDULE =
5 min / 30 min / 2 h
[5 * 60, 30 * 60, 2 * 60 * 60].freeze
Instance Method Summary collapse
-
#check!(user_id) ⇒ Object
Raises if the user is currently held back due to risky behaviour.
-
#initialize(risky_threshold: RISKY_THRESHOLD) ⇒ RiskHoldback
constructor
A new instance of RiskHoldback.
-
#record_risky!(user_id) ⇒ Object
Records a risky (guard-blocked) request for the user.
Constructor Details
#initialize(risky_threshold: RISKY_THRESHOLD) ⇒ RiskHoldback
Returns a new instance of RiskHoldback.
24 25 26 27 28 |
# File 'lib/active_harness/rate_limit/risk_holdback.rb', line 24 def initialize(risky_threshold: RISKY_THRESHOLD) @risky_threshold = risky_threshold @state = {} @mutex = Mutex.new end |
Instance Method Details
#check!(user_id) ⇒ Object
Raises if the user is currently held back due to risky behaviour.
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/active_harness/rate_limit/risk_holdback.rb', line 52 def check!(user_id) return if user_id.nil? key = user_id.to_s @mutex.synchronize do s = @state[key] return unless s&.dig(:held_until) return if Time.now.to_f >= s[:held_until] remaining = (s[:held_until] - Time.now.to_f).ceil raise Errors::UserHoldbackError, "Requests paused due to repeated safety violations. " \ "Retry in #{remaining}s (user: #{key})" end end |
#record_risky!(user_id) ⇒ Object
Records a risky (guard-blocked) request for the user. Applies a hold when the count reaches the next threshold multiple.
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/active_harness/rate_limit/risk_holdback.rb', line 33 def record_risky!(user_id) return if user_id.nil? key = user_id.to_s @mutex.synchronize do s = @state[key] ||= { risky_count: 0, offense_count: 0, held_until: nil } s[:risky_count] += 1 if (s[:risky_count] % @risky_threshold).zero? offense_idx = [s[:offense_count], HOLD_SCHEDULE.size - 1].min s[:held_until] = Time.now.to_f + HOLD_SCHEDULE[offense_idx] s[:offense_count] += 1 end end end |