Preface

DeepSeek Harness (dsh) is an agent framework open-sourced by DeepSeek AI. The official repository summarizes its architecture in one sentence: Everything is a plugin. Web access is no exception. The official documentation places this capability under the optional ctx.web seam: between web_search and web_fetch. The model only submits query terms or URLs, while the actual search and fetch destinations are determined by registered Providers.

The search backends bundled with Harness are implementations that require cloud API keys, such as DeepSeek, Exa, and Perplexity; for fetching, there is a separate HTTP Provider. Users who already run SearXNG locally and use Crawl4AI for page extraction often do not want to purchase a separate search interface for their agents. The surfing-plugin was built for this purpose: it connects native tools to self-hosted services, while retaining the original DSH tool names, parameters, timeouts, cancellation, and result truncation logic.

First, a note on sources. This article is based on the community plugin directory and the maintainer’s repository, not the official app store of DeepSeek / HyperGAN. The directory site operates independently and has no affiliation with the official repository. At the time of writing, the official repository name is cyijun/dsh-surfing-plugin (12 stars on GitHub), and the old address cyijun/surfing-plugin will redirect to it; the directory’s installation command still uses github:cyijun/surfing-plugin, and the following content will follow the original wording of the directory page.

What It Is

surfing-plugin is a DeepSeek Harness plugin maintained by cyijun, with the npm package name dsh-surfing-plugin. The current package.json version is 0.1.0, licensed under MIT, and the primary language is TypeScript. The community directory categorizes it under the “Interface Enhancement” section, with the description “SearXNG search and Crawl4AI fetch provider”. Looking at the source code, it does not add sidebars or skins, but registers two Providers to ctx.web:
- Search: surfing-searxng, which requests SearXNG’s /search endpoint
- Fetch: surfing-crawl4ai, which requests Crawl4AI’s /crawl endpoint

The plugin depends on @deepseek-ai/dsh-web and declares inject = ['web']. That is, it hooks into DSH’s existing web access service without creating a separate set of tool names. The official Web Access documentation also states: changing the search backend will not change how the model submits query prompts, and changing the fetch backend will not change how the model submits URLs.

Its specific goal is: under the premise that you can already access SearXNG and Crawl4AI, make web_search / web_fetch use these self-hosted services instead of the default cloud search Provider.

Core Features

The repository’s README and source code clearly define its behavior, which can be divided into four parts.

1. SearXNG Search. The Provider sends a form-encoded POST request to /search, and fixes format=json. It only retains absolute HTTP(S) result URLs, deduplicates them by URL, and maps title, content, and publishedDate to DSH’s source field; if SearXNG returns a non-empty answers, they will be appended to the content of the search results. maxResults is enforced on both the Provider and the DSH web service side.

2. Crawl4AI Fetch. The Provider only sends a minimal request body { "urls": [url] }, and does not allow the model to inject browser or crawler configurations. The target must be HTTP(S). Non-2xx status codes from the target site will be returned as a successful fetch result; failures of the Crawl4AI API itself, failed crawls, or unrepresentable responses will be converted into a structured WebError. The markdown priority mode defaults to raw, with options fit or citations; the latter two will fall back to raw if the corresponding fields are empty. If there is no markdown but there is cleaned_html or html, HTML will be returned. There is a character limit before returning to DSH, with a default of 100000.

3. The bundled cordis.patch.yml. It does three things: mounts this plugin; fixes the search and fetch Providers to surfing-searxng and surfing-crawl4ai respectively; then inserts a line for @deepseek-ai/dsh-tool-web that only registers the native web_fetch. The README explains why this line is split: headless continues to use the host layer’s web_search, and the Web UI continues to use web_search from each Agent Preset, avoiding duplicate registrations. Existing DeepSeek search Providers can still be mounted, but will not be selected.

4. Configuration overrides environment variables. The service address can be written as either the service root path or the full /search and /crawl endpoints. No authentication header is sent when there is no API key, which is suitable for locally deployed services without authentication; when an API key exists, apiKeyEnv takes precedence, and the literal apiKey will override the environment variable.

Installation and Enablement

The installation command given on the directory page is:

dsh plugin add github:cyijun/surfing-plugin

The plugin runs with the permissions of the current dsh process, and may execute code during installation. You should inspect the source code repository and license before installing. The directory page also notes: for reproducible installations, pin the commit hash.

The maintainer’s README recommends installing it into the web profile and pinning the commit. The corresponding command is as follows (replace COMMIT_SHA with the actual hash):

dsh plugin --profile web add github:cyijun/surfing-plugin#COMMIT_SHA

Installing from Git will run the prepare script in the package to build. pnpm 10 and above block build scripts from Git dependencies by default. If the first installation is blocked, add the exact package key from the prompt to the profile’s pnpm-workspace.yaml and retry:

allowBuilds:
  dsh-surfing-plugin: true

The environment requirements are based on the repository’s README and package.json:
- Node.js ^22.19.0 or >=24.0.0
- DeepSeek Harness >=0.1.0-rc.6 <0.2.0
- Accessible SearXNG and Crawl4AI on the local machine or internal network
- SearXNG must enable json in search.formats; otherwise, requests with format=json will be rejected. The SearXNG documentation also points out that many public instances disable JSON output by default

The README also covers local checkout installation:

dsh plugin --profile web add .
dsh --profile web --dump-config
dsh --profile web

The uninstall command is dsh plugin --profile web remove dsh-surfing-plugin.

Regarding npm: the README states that after publishing to npm, you can use dsh plugin --profile web add dsh-surfing-plugin, and mentions confirming that the package name is still available before the first publish. At the time of writing this article, we could not confirm that the package was listed on the npm registry, so the current installation path is subject to the GitHub command, and do not treat the npm package name as an already available installation entry.

Configuration and Usage

First, prepare the two backend addresses. The value can be the service root or the full endpoint:

export SEARXNG_URL=http://127.0.0.1:8080
export CRAWL4AI_URL=http://127.0.0.1:11235

# The current version of Crawl4AI enables Bearer token by default; omit this for unauthenticated deployments.
export CRAWL4AI_API_TOKEN=replace-with-your-token

To override the default values, modify the entry for this plugin in $DSH_HOME/profiles/web/cordis.patch.yml. Explicit configuration takes precedence over environment variables. The example provided by the repository is as follows (the Chinese README writes the language as zh-CN):

- id: surfing-plugin
  config:
    searxng:
      url: https://search.example.com
      apiKeyEnv: MY_SEARXNG_KEY
      authHeader: X-API-Key
      authScheme: ''
      language: zh-CN
      categories: general,news
      safeSearch: 1
      timeRange: month
    crawl4ai:
      url: https://crawl.example.com
      apiKeyEnv: CRAWL4AI_API_TOKEN
      authHeader: Authorization
      authScheme: Bearer
      markdownMode: raw
      maxContentChars: 100000

The correspondence between common fields is as follows:
- searxng.url: corresponds to the environment variable SEARXNG_URL; service root or /search endpoint
- searxng.apiKeyEnv: defaults to reading SEARXNG_API_KEY
- searxng.language / categories / safeSearch / timeRange: passed as query parameters to SearXNG; safeSearch can only be 0, 1, or 2, and timeRange can only be day, month, or year
- crawl4ai.url: corresponds to the environment variable CRAWL4AI_URL; service root or /crawl endpoint
- crawl4ai.apiKeyEnv: defaults to reading CRAWL4AI_API_TOKEN
- crawl4ai.markdownMode: raw, fit, or citations, defaults to raw
- crawl4ai.maxContentChars: defaults to 100000

After installing and configuring the backends, the agent still calls the native web_search and web_fetch without changing the tool names or needing to write plugin-specific instructions in the conversation. You can use dsh --profile web --dump-config to verify that the patch has pointed searchProvider / fetchProvider to surfing-searxng and surfing-crawl4ai.

Applicable Scenarios and Notes

It is suitable for the following use cases: users who have built their own SearXNG and Crawl4AI and want DSH’s web tools to use the same backend; users who want search and fetch to remain in their controlled network instead of the default cloud search Provider; users who use both headless and Web UI and need the split web_fetch Consumer as described in the README to avoid duplicate registration of web_search.

There are several boundaries to clarify before use.

First, this is not a “install and search” plugin. Both backend services must be accessible first; SearXNG must also have JSON format enabled. Public SearXNG instances often disable JSON, making them unsuitable for direct use as an API.

Second, it is not a general-purpose crawler controller. The Crawl4AI request body is intentionally hardcoded to a single URL, and the model cannot specify browser parameters, extraction policies, or concurrency. Crawling policies, browser isolation, target network segments, and SSRF policies are all handled on the Crawl4AI side. Before public deployment, you should restrict network and authentication according to Crawl4AI’s own security guidelines.

Third, authentication and transmission. The repository recommends prioritizing apiKeyEnv and never hardcoding keys into Git; use HTTPS for non-local services; Provider requests prohibit redirects to avoid authentication headers being sent to other backends. No authentication header is sent when there is no API key.

Fourth, the version window is narrow. The currently declared DSH range is >=0.1.0-rc.6 <0.2.0. The official repository is still in developer preview, and the README notes that there will be breaking changes. You should verify whether this plugin is still compatible after upgrading Harness.

Fifth, as the security reminder on the directory page states: the plugin runs with the permissions of the current dsh process and may execute code during installation. Installing from Git will also run the prepare build step. You should read the source code and MIT license before installation, and pin the commit hash as much as possible to avoid silent changes to the actual executed code in future pushes.

Summary

surfing-plugin connects DSH’s native web_search and web_fetch to self-hosted SearXNG and Crawl4AI, while the tool protocol is still handled by Harness’s own web seam. The directory categorizes it under “Interface Enhancement”, but its actual capability is Provider replacement. The installation command is subject to the directory page, and the configuration mainly relies on backend addresses and key environment variables.

Directory page: https://deepseek-harness-plugin.com/zh-CN/plugins/surfing-plugin/

GitHub (directory entry address, redirects to the new canonical name): https://github.com/cyijun/surfing-plugin

New canonical GitHub repository: https://github.com/cyijun/dsh-surfing-plugin