ADR-0003 — Stealth runtime guard + CI whitelist
**Status.** Accepted.
Status. Accepted. Date. 2026-05-18.
Context
The product exists because tracked Telegram users cannot detect Helios.
A single regression — a stray client.send_reaction, a messages.setTyping,
a stories.incrementStoryViews — invalidates the entire value proposition.
Pure code review is not enough. We need to make a wrong call hard at three independent layers.
Decision
- Runtime.
apps/userbot/stealth/guard.pywraps every Pyrogram client. Every method call passes throughassert_stealth_safe(...)first. - CI.
scripts/stealth_whitelist_check.pyAST-scansapps/userbot/and fails the build on any method outsideALLOWED_METHODS. - Tests.
tests/stealth/cover 100 % of the guard and assert that each forbidden method raisesStealthViolation.
The whitelist is generated from config/stealth_whitelist.yaml — a single
editable file with a justification comment per entry.
Consequences
- The cost of a stealth-bug regression is paid at PR time, not in production.
- New methods are deliberately added: YAML edit + ADR-style justification.
tests/stealth/is the most important test file in the project; its coverage is enforced at 100 %.
Alternatives considered
- CI-only check. Rejected: at runtime, a developer could still attach an unguarded client and bypass the AST scan; the runtime layer makes that fail loudly.
- Runtime-only check. Rejected: regressions could lurk in untested code paths; the AST scan ensures we never ship a forbidden call site.