Request lifecycle
The CheckerRegistry
CheckerRegistry is a small registry class that holds a list of Checker objects and runs them all in parallel.
- All checkers run concurrently via
Promise.all— latency is bounded by the slowest checker, not the sum of all checkers. - Each checker races against a 2500 ms timeout via
Promise.race. A timed-out checker contributes a score of0and does not fail the request. - Per-checker execution time is recorded in the
timingmap and returned in theScanResultasexecutionTimeMs.
Checker registration
Checkers are registered inScanner.ts at startup:
Checker interface:
Result caching
Scan results are cached in Redis to avoid re-running the full checker pipeline for recently seen URLs.scan_results). A companion sorted set (scan_results_expiry) stores each field ID with its expiry timestamp as the score, enabling efficient batch cleanup.
Cache read — On a cache hit, Phisherman checks the exp field against Date.now(). Expired entries are deleted opportunistically before the fresh scan runs.
Caching safe results — By default, URLs that resolve to a safe verdict are not cached, because they are high-volume and low-value to retain. Set SCAN_CACHE_SAFE_RESULTS=true to cache them:
Background feed refresh
CacheManager runs a background loop that keeps all threat feed data current. It is started once at server startup:
runAll() invokes every registered RefreshTask in sequence, then runs three cleanup routines:
Each feed source registers its own refresh task with a source-specific interval: