You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(search): add Serper as a keyed fallback behind SearXNG (ADR-002)
Stage 2. SearXNG stays primary and the fallback engages only when a query
returns nothing -- which is what a fully blocked engine pool looks like -- so
the private path remains the default and the paid path is insurance.
Serper was chosen because it returns organic URLs and snippets and nothing
else. This stack already owns crawling, so providers bundling page content
(Tavily, Exa, Firecrawl) would be paid for output it discards. Brave remains
rejected pending a licensing question: its terms restrict storing API
results, and this system exists to build a persistent corpus.
Inert without SERPER_API_KEY, so acquisition behaves exactly as before until
a key is present. Never raises: a quota error or transport failure returns no
results, exactly as SearXNG does, so a dead fallback degrades acquisition
rather than failing a job -- and unlike a silent CAPTCHA, a quota error is
logged legibly. The key is excluded from the config repr, matching the judge
key, and a test asserts it cannot reach a log that way.
Fallback URLs go through the unchanged source policy and SSRF vetting, so a
new provider cannot bypass either. Job progress records which provider served
each query.
339 tests green in-container, covering result mapping, inert-without-key,
quota errors, transport failure, entries without a link, and repr exclusion.
ruff clean, compose valid.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0 commit comments