Skip to content

fix(telegram): bound the per-user API key cache - #13166

Merged
diegosouzapw merged 1 commit into
diegosouzapw:release/v3.8.51from
huuhungn:fix/telegram-keycache-unbounded-13165
Sep 11, 2026
Merged

diegosouzapw merged 1 commit into
diegosouzapw:release/v3.8.51from
huuhungn:fix/telegram-keycache-unbounded-13165

Conversation

@anhtahaylove

Copy link
Copy Markdown
Contributor

Fixes #13165.

Problem

resolveUserApiKey() cached a plaintext OmniRoute API key per Telegram user id in a module-level Map with no cap, no TTL and no eviction path. Every distinct id ever seen was retained for the lifetime of the process.

The id is not bounded by the real user population: POST /api/telegram/update reaches proxyChat(chat.chatId, ...) on the webhook path with the id taken straight from the request body, gated only by isTelegramEnabled().

Measurement

Driving the real module with getApiKeys/createApiKey stubbed (no DB access):

distinct users  : 20000
heap growth     : 4.9 MB
per-user cost   : 254 bytes
evictions       : 0

Fix

All three write sites now go through a rememberUserApiKey() helper backed by a 1000-entry LRU. Map insertion order is the recency order: a cache hit re-inserts the id, so an active user is not evicted by a burst of new ids, and the oldest entry is dropped once the cap is reached.

Tests

tests/unit/telegram-keycache-bounded-13165.test.ts (node:test, matching this directory's runner):

  • evicts a cold id once the cap is exceeded — red before the fix (got 0 mint(s)), green after.
  • keeps a recently used id alive across a burst of new ids — guards the LRU recency contract, so the fix cannot regress into a plain "clear when full" cache.

Verified in both directions on this branch: with the patch stashed the first test fails, with it restored both pass. tsc -p tsconfig.json --noEmit is clean.

Note for maintainers (not addressed here)

A cache miss falls through to createApiKey(), so a distinct id on the unauthenticated webhook path also mints and persists a new API key row, not just a memory entry. I did not change auth behaviour in a leak fix; flagged in #13165 for a separate decision.

resolveUserApiKey() cached a plaintext API key per Telegram user id in a
module-level Map with no cap, no TTL and no eviction. The webhook path of
POST /api/telegram/update passes a caller-supplied chat id, so the key space is
not bounded by the real user population and the map grew for the lifetime of
the process (measured 254 bytes per distinct id, zero evictions).

Route every write through a small LRU helper capped at 1000 entries. A cache
hit re-inserts the id so an active user is not evicted by a burst of new ids.

Refs diegosouzapw#13165
@diegosouzapw
diegosouzapw merged commit 30c96d4 into diegosouzapw:release/v3.8.51 Sep 11, 2026
8 of 16 checks passed
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
`resolveUserApiKey()` keyed an uncapped Map on an id taken straight from the webhook body. The LRU's recency test is what keeps this from regressing into a clear-when-full cache.

---

Validated in one consolidated worktree cut from `release/v3.8.51`, boarded together with the other 13 PRs of this batch — zero merge conflicts between them.

- `typecheck:core` clean
- complexity 2799 / baseline 3218 and cognitive-complexity 1265 / baseline 1437 — both under baseline
- 71 focused assertions green across the 13 test files this batch adds or touches

⚠️ base-red inherited: diegosouzapw#12732 — `Docs Gates (fast-path)`, `Merge integrity`, `No new ESLint warnings`, `Unit Tests fast-path` and `Fast Quality Gates` all reproduce on the pure `release/v3.8.51` tip (provider count 356 vs the 358 the modules define, SKILL.md drift, and `open-sse/utils/stream.ts` at 3115 > frozen 3098). None of them touch this diff.

Thanks @anhtahaylove — the root-cause write-up, the measured before/after numbers and the red-before-green proof on every one of these made the batch reviewable as a unit.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(backend): Unbounded per-user key cache in Telegram chat proxy (keyCache)

2 participants