#9172: Fix: Add rate limiting to boot-md hook to prevent spam during rapid restarts
stale
Cluster:
Hook and Gateway Improvements
## Summary
Fixes issue #9167 where the boot-md hook fires multiple times in rapid succession during gateway startup, causing duplicate messages to be sent.
## Problem
The `boot-md` hook was firing 8-9 times within ~30 seconds when the gateway restarted frequently. Each execution of BOOT.md resulted in duplicate messages being sent to channels, spamming users.
**Evidence from production:**
```
18:16:12 -> First boot check
18:16:25 -> Second boot check (13s later)
22:01:01 -> Boot check
22:01:13 -> Again (12s later)
22:01:21 -> Again (8s)
22:01:30 -> Again (9s)
22:02:23 -> Again
22:02:41 -> Again
22:06:17 -> Again
22:06:25 -> Again
```
## Root Cause
Each gateway restart (due to config changes, crashes, or external restarts) triggers a new `gateway:startup` event, which causes the boot-md hook to execute BOOT.md again. The function is named `runBootOnce` but it actually runs once *per gateway startup*, not once *per session*.
## Solution
Added **60-second rate limiting** to the boot-md hook handler. The handler now tracks the last execution timestamp and skips subsequent triggers within the rate limit window.
### Changes Made
1. **Rate limiting logic** (`src/hooks/bundled/boot-md/handler.ts`):
- Track last boot time in module-scoped variable
- Skip execution if called within 60 seconds of previous execution
- Only affects rapid restart scenarios - normal use unaffected
2. **Comprehensive test suite** (`src/hooks/bundled/boot-md/handler.test.ts`):
- Verifies rate limiting behavior
- Tests edge cases (missing config, wrong event type, etc.)
- Ensures proper execution after rate limit expires
## Impact
✅ **Prevents spam:** BOOT.md executes at most once per minute, even during rapid restarts
✅ **No config changes:** Works automatically with existing setups
✅ **Minimal overhead:** Single timestamp check per trigger
✅ **Backward compatible:** Normal startup behavior unchanged
## Test Plan
- [x] Unit tests pass (rate limiting verification)
- [ ] Manual testing: Restart gateway multiple times rapidly
- [ ] Verify only one boot message is sent within 60 seconds
- [ ] Verify boot message IS sent after 60 seconds
## Alternative Considered
The issue reporter suggested making BOOT.md itself idempotent by checking timestamps. While that's a valid workaround, fixing it in the hook handler is better because:
- Centralizes the logic (don't require every user to implement this)
- More reliable (doesn't depend on filesystem state)
- Cleaner (BOOT.md remains simple and declarative)
---
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
This PR adds a 60s rate limiter to the bundled `boot-md` hook handler so BOOT.md won’t run repeatedly during rapid gateway restarts, and introduces a new Vitest suite to validate the handler’s behavior on `gateway:startup`.
The handler change is a simple module-scoped `lastBootTime` timestamp check before calling `runBootOnce`, and the test suite covers non-gateway events, missing context, and the intended “only once per minute” behavior.
<h3>Confidence Score: 2/5</h3>
- This PR should not be merged until the new tests are made deterministic and isolated.
- The production change is small, but the new test suite is order-dependent due to module-scoped `lastBootTime` persisting across test cases, and it relies on fake timers without explicitly controlling `Date.now()`, making rate-limit assertions flaky across environments/configs.
- src/hooks/bundled/boot-md/handler.test.ts
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#21728: fix(boot): deduplicate startup notification within restart cycle
by irchelper · 2026-02-20
78.7%
#16963: fix: enable auth rate limiting by default
by StressTestor · 2026-02-15
74.1%
#22424: fix: prevent crash when onUpdate is truthy but not callable (fixes ...
by mcaxtr · 2026-02-21
73.5%
#20541: fix(hooks): clear internal hooks before plugins register
by ramarnat · 2026-02-19
72.9%
#10679: fix(hooks): invoke gateway_start and gateway_stop in lifecycle
by yassinebkr · 2026-02-06
72.8%
#14574: fix: gentler rate-limit cooldown backoff + clear stale cooldowns on...
by JamesEBall · 2026-02-12
72.8%
#21944: feat(gateway): crash-loop protection with escalating backoff
by Protocol-zero-0 · 2026-02-20
72.2%
#21529: Gateway: allow node health and throttle repeated unauthorized role ...
by doomsday616 · 2026-02-20
71.8%
#13084: fix(daemon): multi-layer defense against zombie gateway processes
by openperf · 2026-02-10
71.7%
#12953: fix: defer gateway restart until all replies are sent
by zoskebutler · 2026-02-10
70.9%