Files
esh-pfi-infrastructure/scripts/searxng-health.sh
T
vh 0f748ea54e feat(searxng): move to nh3-docker, update, and expose as an MCP tool
The ana-docker instance was returning zero results for every query while
reporting healthy — 4.5 months stale (2026.4.17 against a current 2026.9.3),
its engine scrapers rotted against sites that had changed. /healthz proves
the web app answers and says nothing about whether search works, so seven
days of green sat on top of a search box that found nothing.

Moved to nh3-docker rather than updated in place, because the colo egress is
the other half of the problem: 38.120.12.42 is a datacenter address that
DuckDuckGo and Startpage CAPTCHA, while nh3-docker egresses residentially at
70.230.226.88. Same reasoning as the fleet's residential proxy for yt-dlp,
applied at the source instead of around it.

Config corrected along the way: base_url said searxng.pfi.local, a name
retired on 2026-08-19, while the environment said something else — the env
won so nothing broke and the file quietly lied. The karmasearch.videos
removal key never matched, because the engine's real name has a space.

scripts/searxng-health.sh asserts results > 0 across three unrelated
queries. That is the check that would have caught this, and the only kind
that can: the mechanism was healthy throughout.

services/searxng-mcp exposes it as `web_search` at user scope, so every
Claude Code session has it. Zero results raise rather than returning an
empty list — an empty list is indistinguishable from a broken aggregator,
which is precisely how this hid.

Old instance stopped and removed; DNS alias repointed to searxng.nh3.internal.
2026-09-03 14:07:06 -07:00

64 lines
2.9 KiB
Bash
Executable File

#!/usr/bin/env bash
# Is SearXNG actually searching?
#
# scripts/searxng-health.sh check the fleet instance
# scripts/searxng-health.sh --url http://host:9996
#
# ⚠ THIS EXISTS BECAUSE /healthz CANNOT ANSWER THE QUESTION. On 2026-09-03 the
# old ana-docker instance was found returning ZERO results for every query, for
# an unknown number of weeks, while its container reported `healthy` for 7 days
# straight and its dashboard card was green. /healthz proves the web app
# answers; it says nothing about whether a single engine works.
#
# SearXNG rots quietly: engine scrapers break as upstream sites change markup,
# and the project ships near-daily releases to keep up. An instance pinned to
# `:latest` that nobody re-pulls is frozen at whatever `latest` meant on the day
# it was created — that one was 4.5 months behind.
#
# So this asserts the property, not the mechanism: RESULTS > 0.
set -euo pipefail
URL="http://10.100.50.40:9996"
[[ "${1:-}" == "--url" ]] && { URL="${2:?--url needs a value}"; shift 2; }
fail=0
say() { printf '%s\n' "$*"; }
say "── ${URL}"
# 1. reachable at all? An unreachable instance is an OUTAGE, not "no results".
if ! ver=$(curl -s -m 15 "$URL/config" | python3 -c 'import sys,json;print(json.load(sys.stdin)["version"])' 2>/dev/null); then
say " ✗ unreachable — this is an outage, not an empty index"
exit 1
fi
say " version: $ver"
# 2. is that version current? `:latest` is only latest at pull time.
if latest=$(curl -s -m 20 "https://hub.docker.com/v2/repositories/searxng/searxng/tags/?page_size=1&ordering=last_updated" \
| python3 -c 'import sys,json;print(json.load(sys.stdin)["results"][0]["last_updated"][:10])' 2>/dev/null); then
say " registry :latest last pushed: $latest (running build predates any later push)"
fi
# 3. THE CHECK THAT MATTERS. Three unrelated queries, because one query
# returning nothing can legitimately mean nothing matched; three cannot.
for q in "proxmox backup" "python asyncio" "linux kernel"; do
out=$(curl -s -m 45 --get --data-urlencode "q=$q" --data "format=json" "$URL/search" 2>/dev/null) || out=""
n=$(printf '%s' "$out" | python3 -c 'import sys,json;print(len(json.load(sys.stdin).get("results") or []))' 2>/dev/null || echo 0)
errs=$(printf '%s' "$out" | python3 -c 'import sys,json;print(",".join(e[0] for e in (json.load(sys.stdin).get("unresponsive_engines") or [])) or "-")' 2>/dev/null || echo "?")
if [[ "$n" -gt 0 ]]; then
say " ✓ '$q' -> $n results (failed engines: $errs)"
else
say " ✗ '$q' -> ZERO results (failed engines: $errs)"
fail=1
fi
done
if (( fail )); then
say ""
say " ✗ SearXNG answers HTTP but finds nothing. Almost always staleness:"
say " ssh infra-ops@nh3-docker 'cd /opt/docker/compose/searxng && \\"
say " sudo docker compose pull && sudo docker compose up -d'"
exit 1
fi
say " ✓ searching"