← Back to home@Chaos-Paradox

dsh-web-search-searxng

Self-hosted SearXNG metasearch web-search plugin for DeepSeek Harness — no API key, adds a Plugins-page settings card. | DeepSeek Harness 插件:通过自托管 SearXNG 元搜索提供网页搜索,无需 API Key,附带设置页插件配置卡片。

Stars
1
Language
TypeScript
Created
Oct 6, 2026
Updated
Oct 7, 2026
GitHub repo

Introduction

dsh-web-search-searxng

English | 中文

License: MIT Node SearXNG DeepSeek Harness

A DeepSeek Harness (dsh) plugin that gives your AI agent free, unlimited, privacy-friendly web search through your own self-hosted SearXNG metasearch instance — no API key, no per-search model cost, no query logs leaving your machine. Installing it also adds a SearXNG search card to the Settings → Plugins page of the Web and Desktop apps, so the endpoint, engine restriction, and result language are editable from the GUI.

SearXNG settings card in Settings → Plugins

The SearXNG search card on the Settings → Plugins page — endpoint, engines, and result language, applied to the next search without a restart.

In plain terms: what does this plugin do?

One line: it lets your AI assistant search the web for free — no paid search service, and what you search stays known only to you.

An AI assistant can't browse the web by itself. To let it look things up, you normally pay for a "search service" — billed per query, requiring an API key, with your queries passing through someone else's servers. This plugin takes a different route: you run a small "search relay" on your own machine. It works like an errand runner — give it a question and it asks Google, Bing, and 70+ other search sites at the same time, then hands the combined results to the AI.

As an analogy:

  • The AI assistant = you, wanting to look something up
  • This plugin = a phone line connecting you to the pickup point downstairs
  • Your self-run search relay = that pickup point, serving only you, fetching your packages from every courier (the big search engines)
  • The payoff: no handling fees, no membership card, and nobody else sees what you picked up

Why SearXNG instead of a search API?

Hosted search APIsThis plugin
CostPay per queryFree — your instance, your hardware
API keyRequired, rotates, leaksNone
PrivacyQueries go to a third partyQueries go to your SearXNG, which aggregates 70+ engines for you
Rate limitYesOnly what your instance allows
Works offline / intranetNoYes — loopback and private-network endpoints are supported by design

But does answer quality drop? We benchmarked it end-to-end — a real dsh agent answering with real web_search calls, same model and prompts on both sides, blinded judging: no quality difference observed (two 10-question runs: 6-2-2 and 3-4-3 win/loss/tie; average scores ≈4.8 vs ≈4.4). Methodology, per-question data, and honest limitations: docs/quality-benchmark.md.

Features

  • 🔍 Metasearch provider — registers a provider with the stable id searxng into the dsh ctx.web seam; every agent web search is served by GET {baseURL}/search?format=json.
  • 🖥️ GUI settings card — a SearXNG search card appears under Settings → Plugins; edit endpoint, engines, and language without touching config files. Changes apply to the next search, no restart.
  • 🔒 Safe by default — no credentials to leak; HTTP redirects fail closed (WEB_PROVIDER_ERROR) so a redirect can never forward your query text to another origin; a 403 response tells you exactly that the instance's JSON format is disabled.
  • 🏠 Self-host friendly — loopback (http://localhost:8080), private IPs, and subpath mounts (http://host/searxng) all work; /search is appended correctly.
  • 🌐 Engine & language control — restrict to specific engines (bing,duckduckgo) and prefer a result language (zh-CN, en, ja…) via SearXNG's native parameters.
  • 📎 Citeable sources — each result maps to a source with URL, title, engine excerpt as snippet, and publication date when present, ready for the agent to cite.
  • ⚡ Zero build step for consumers — lib/ is committed, so installing from the git URL works immediately.

How it works

┌─────────────┐   web search   ┌──────────────┐   JSON API   ┌────────────────┐
│  dsh agent  │ ─────────────▶ │ ctx.web seam │ ───────────▶ │ your SearXNG   │
│  (LLM)      │ ◀───────────── │  (searxng    │ ◀─────────── │ instance       │
└─────────────┘  sources only  │  provider)   │   results[]  └───────┬────────┘
                               └──────────────┘                      │ aggregates
                                                          ┌──────────▼──────────┐
                                                          │ Google / Bing / DDG │
                                                          │ Brave / 70+ engines │
                                                          └─────────────────────┘

SearXNG returns no generated answer, so results carry sources only — the agent reads the pages itself with fetch when it needs content.

Requirements

  • A DeepSeek Harness installation whose ctx.web seam is present (any dsh release carrying dsh-web).
  • Node.js ^22.19 || >=24 (for development; consumers just need dsh).
  • A SearXNG instance with JSON output enabled — its settings.yml must list json under search.formats (SearXNG's default serves HTML only).

Quick SearXNG setup with Docker

mkdir -p searxng && cd searxng
cat > settings.yml <<'EOF'
use_default_settings: true
server:
  secret_key: "change-me-to-a-long-random-string"
search:
  formats:
    - html
    - json   # ← required by this plugin
EOF
docker run -d --name searxng -p 8080:8080 \
  -v "$PWD/settings.yml:/etc/searxng/settings.yml" \
  searxng/searxng

Verify JSON is enabled:

curl "http://localhost:8080/search?q=test&format=json"

Install (import into dsh)

Option 1 — from GitHub (tracks latest):

dsh plugin --profile <name> add https://github.com/Chaos-Paradox/dsh-web-search-searxng

Option 2 — pin a release version (recommended for reproducibility):

dsh plugin --profile <name> add https://github.com/Chaos-Paradox/dsh-web-search-searxng#v0.1.0

See all versions on the Releases page.

Option 3 — from a local clone or tarball: the same command takes an absolute path, e.g. dsh plugin --profile <name> add /path/to/dsh-web-search-searxng. No build step needed — lib/ is committed.

Installing activates the bundle's patch layer, which registers the provider row. Verify the import:

dsh plugin --profile <name> list        # dsh-web-search-searxng should appear
# remove
dsh plugin --profile <name> remove dsh-web-search-searxng

Configure and select

Registration alone does not route searches. Two switches, both yours:

1. Point it at your instance

Option A — GUI (recommended): open Settings → Plugins → SearXNG 搜索 and fill in the fields. All fields apply to the next search without a restart.

Option B — environment variable before launching dsh:

export SEARXNG_BASE_URL="http://localhost:8080"
FieldGUI labelEnv fallbackDescription
baseURLEndpoint / 实例地址SEARXNG_BASE_URLSearXNG instance base; /search is appended. Empty → provider reports unavailable.
enginesEngines / 引擎限制—Comma-separated engine restriction, e.g. bing,duckduckgo.
languageLanguage / 结果语言—Preferred result language, e.g. zh-CN, en, ja.

2. Select it for search

Patch the profile's web row (a patch replaces the row's whole config, so restate fetchProvider):

# $DSH_HOME/profiles/<name>/cordis.patch.yml
- id: web
  config:
    searchProvider: searxng
    fetchProvider: http

To switch back, drop the patch (or set searchProvider: deepseek-official). With no endpoint configured the provider reports itself unavailable and nothing changes.

What a search returns

Each SearXNG result maps to a citeable source:

SearXNG fielddsh source fieldNotes
urlurlentries without one are dropped
titletitleomitted when blank
contentsnippetthe engine's excerpt
publishedDatepublishedAtwhen the engine provides it

truncated is always false (the web service owns maxResults truncation), and no generated content answer is attached because SearXNG has none the seam could vouch for.

Troubleshooting

SymptomCauseFix
SearXNG error (HTTP 403); the instance may refuse JSON outputsettings.yml lacks json in search.formatsAdd it as shown above and restart the container
Provider unavailable / nothing changesNo endpoint configuredSet the card field or SEARXNG_BASE_URL
search request failed / ECONNREFUSEDInstance down or wrong portCheck docker ps, try the curl verify command
WEB_PROVIDER_ERROR mentioning a redirectA proxy in front of SearXNG redirectsPoint baseURL at the final address; redirects fail closed by design
Empty sourcesEngines returned nothing usable (or all entries lacked URLs)Loosen engines, check the instance in a browser

Develop

pnpm install        # dependencies come from npm (@deepseek-ai/* 0.2.1-alpha.1 train)
pnpm run build      # tsdown (host + browser bundles) + tsc (browser declarations)
pnpm test           # vitest: provider behavior, redirect policy, proxy egress, card form
pnpm run typecheck  # tsc --noEmit
src/
  index.ts      plugin entry: config schema, env fallback, provider registration
  provider.ts   SearxngSearchProvider: JSON API call, result mapping, error policy
  types.ts      SearXNG response types
  client/       browser bundle: the Settings → Plugins card (React)
tests/          vitest suites incl. redirect & egress policy

lib/ is committed on purpose: installing from a git URL gives the consumer the built artifacts without a build step. Rebuild and recommit lib/ whenever src/ changes.

Known gap: the card's apply-level registration test lives upstream for now — the published @deepseek-ai/dsh-client-test-runtime references source files its npm package does not ship, so this repo keeps local stand-ins (tests/helpers.ts) for the two helpers the remaining card specs use.

Contributing

Issues and pull requests are welcome. Please keep the provider credential-free and fail-closed on redirects — those are design constraints, not missing features.

License

MIT © Chaos-Paradox

Links