* fix(hbbs): bound the punch-request record store
PUNCH_REQS grew without limit; its dedupe scan only covers the newest 30
entries, so cycling 31 online peer ids from one source appended a record
per request for the lifetime of the process. Keep at most 10,000 entries,
evicting the oldest.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PkpVy2ufxgEjKd3SjXPnJ8
* feat(hbbs): make the punch-request record cap configurable
MAX_PUNCH_REQUESTS sets how many records the punch-requests console
command keeps (default 10000); 0 disables recording.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PkpVy2ufxgEjKd3SjXPnJ8
* fix(hbbs): cap peer id length at 253 bytes
RegisterPk accepted ids up to a whole UDP datagram, so 31 registered
peers with ~64 KB ids still let the bounded punch-request store reach
hundreds of MB. Reject longer ids in RegisterPk and RegisterPeer, the
same bound the Pro server uses.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PkpVy2ufxgEjKd3SjXPnJ8
* feat(hbbs): keep 100000 punch-request records by default
With peer ids capped at 253 bytes a full store is roughly 45 MB at
worst, so a larger default keeps more console history.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PkpVy2ufxgEjKd3SjXPnJ8
* test(hbbs): assert which punch-request records survive eviction
The length check alone passed if the newest record were evicted or new
records were dropped once full.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PkpVy2ufxgEjKd3SjXPnJ8
* test(hbbs): cover MAX_PUNCH_REQUESTS=0 in the punch-request test
At zero capacity the loop never ran and the new front/back asserts
unwrapped an empty queue. Make at least one request so the disabled
path is exercised, and only check eviction order when records are kept.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PkpVy2ufxgEjKd3SjXPnJ8
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
`Duration::as_millis()` returns u128; casting it `as i32` in the
online-status and punch-hole checks keeps only the low 32 bits as a
signed integer. Once a peer has not registered for more than 2^31 ms
(~24.855 days), the value wraps negative and passes the
`elapsed < REG_TIMEOUT` test, so a long-offline peer is reported online
and connection attempts are relayed to its stale socket_addr. The
in-memory peer map is never evicted, so last_reg_time stays frozen until
hbbs restarts, making the false-online window 24.86-49.71 days (and it
repeats every ~49.71 days).
Widen REG_TIMEOUT and the two elapsed casts from i32 to i64 so the
comparison no longer overflows.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>