feat: build Go-based SearXNG-compatible search service

Implement an API-first Go rewrite with local engine adapters, upstream fallback, and Nix-based tooling so searches can run without matching the original UI while preserving response compatibility.

Made-with: Cursor
This commit is contained in:
Franz Kafka 2026-03-20 20:34:08 +01:00
parent 7783367c71
commit dc44837219
32 changed files with 3330 additions and 0 deletions

View file

@ -0,0 +1,76 @@
## gosearch (SearXNG rewrite in Go)
This repository contains a standalone Go HTTP service that implements a SearXNG-compatible
API-first `/search` endpoint and proxies unported engines to an upstream SearXNG instance.
### Endpoints
- `GET /healthz` -> `OK`
- `GET|POST /search`
- Required form/body parameter: `q`
- Optional: `format` (`json` | `csv` | `rss`; default: `json`)
### Supported `format=...`
- `json`: SearXNG-style JSON response (`query`, `number_of_results`, `results`, `answers`, `corrections`, `infoboxes`, `suggestions`, `unresponsive_engines`)
- `csv`: CSV with header `title,url,content,host,engine,score,type`
- `rss`: RSS 2.0 feed based on the `opensearch_response_rss.xml` template fields
### Request parameters
The server accepts SearXNG form parameters (both `GET` query string and `POST` form-encoded):
- `q` (required): search query
- `format` (optional): `json`/`csv`/`rss`
- `pageno` (optional, default `1`): positive integer
- `safesearch` (optional, default `0`): integer `0..2`
- `time_range` (optional): `day|week|month|year` (or omitted/`None`)
- `timeout_limit` (optional): float, seconds (or omitted/`None`)
- `language` (optional, default `auto`): `auto` or a BCP-47-ish language code
- `engines` (optional): comma-separated engine names (e.g. `wikipedia,arxiv`)
- `categories` / `category_<name>` (optional): used for selecting the initial ported subset
- `engine_data-<engine>-<key>=<value>` (optional): per-engine custom parameters
### Environment variables
- `PORT` (optional, default `8080`)
- `UPSTREAM_SEARXNG_URL` (optional for now, but required if you expect unported engines)
- When set, unported engines are proxied to `${UPSTREAM_SEARXNG_URL}/search` with `format=json`.
- `LOCAL_PORTED_ENGINES` (optional, default `wikipedia,arxiv,crossref,braveapi,qwant`)
- Controls which engine names are executed locally (Go-native adapters).
- `HTTP_TIMEOUT` (optional, default `10s`)
- Timeout for both local engine API calls and upstream proxy calls.
- Brave Search API:
- `BRAVE_API_KEY` (optional): enables the `braveapi` engine when set
- `BRAVE_ACCESS_TOKEN` (optional): if set, requests must include a token
(header `Authorization: Bearer <token>`, `X-Search-Token`, `X-Brave-Access-Token`, or form field `token`)
### Ported vs proxied strategy
1. The service plans which engines should run locally vs upstream using `LOCAL_PORTED_ENGINES`.
2. It executes local ported engines using Go-native adapters:
- `wikipedia`, `arxiv`, `crossref`
3. Any remaining requested engines are proxied to upstream SearXNG (`format=json`).
4. Responses are merged:
- `results` are de-duplicated by `engine|title|url`
- `suggestions`/`corrections` are treated as sets
- other arrays are concatenated
### Running with Nix
This repo uses `flake.nix` to provide the Go toolchain.
```bash
nix develop
go test ./...
go run ./cmd/searxng-go
```
Example:
```bash
export UPSTREAM_SEARXNG_URL="http://127.0.0.1:8888"
export PORT="8080"
nix develop -c go run ./cmd/searxng-go
```