4.6 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
kafka is a privacy-respecting metasearch engine written in Go. It provides a SearXNG-compatible /search API and an HTML frontend (HTMX + Go templates). 9 engines are implemented natively in Go; unlisted engines can be proxied to an upstream SearXNG instance. Responses from multiple engines are merged into a single JSON/CSV/RSS/HTML response.
Build & Run Commands
# Enter Nix dev shell (provides Go 1.24 toolchain + curl)
nix develop
# Run all tests
go test ./...
# Run a single test
go test -run TestWikipedia ./internal/engines/
# Run tests in a specific package with verbose output
go test -v ./internal/engines/
# Run the server (requires config.toml)
go run ./cmd/searxng-go -config config.toml
There is no Makefile. There is no linter configured.
Architecture
Request flow: HTTP request -> middleware chain (global rate limit -> burst rate limit -> per-IP rate limit -> CORS) -> HTTP handler -> search.Service (cache check) -> engines.Planner (splits into local vs upstream) -> parallel local engine execution + upstream proxy -> MergeResponses -> cache write -> serialize (JSON/CSV/RSS/HTML).
Key packages:
internal/contracts— Shared types:SearchRequest,SearchResponse,MainResult,OutputFormat.MainResultpreserves unknown JSON keys from upstream via araw map[string]anyfield and round-trips them faithfully.internal/config— TOML-based configuration with env var fallbacks.Load(path)readsconfig.toml; env vars override zero-value fields. Seeconfig.example.tomlfor all settings.internal/engines—Engineinterface and all 9 Go-native implementations.factory.goregisters engines viaNewDefaultPortedEngines().planner.goroutes engines to local or upstream based onLOCAL_PORTED_ENGINESenv var.internal/search—Serviceorchestrates the pipeline: cache check, planning, parallel engine execution via goroutines/WaitGroup, upstream proxying, response merging. Individual engine failures are reported asunresponsive_enginesrather than aborting the search. Qwant has fallback logic to upstream on empty results.internal/httpapi— HTTP handlers for/,/search,/healthz,/opensearch.xml. Detects HTMX requests viaHX-Requestheader to return fragments instead of full pages.internal/upstream— Client that proxies requests to an upstream SearXNG instance via POST.internal/cache— Valkey/Redis-backed cache with SHA-256 cache keys. No-op if unconfigured.internal/middleware— Three rate limiters (per-IP sliding window, burst+sustained, global) and CORS. All disabled by default.internal/views— HTML templates and static files embedded via//go:embed. Renders full pages or HTMX fragments. Templates:base.html,index.html,results.html,results_inner.html,result_item.html.cmd/searxng-go— Entry point. Loads TOML config, seeds env vars for engine code, wires up middleware chain, starts HTTP server.
Engine interface (internal/engines/engine.go):
type Engine interface {
Name() string
Search(ctx context.Context, req contracts.SearchRequest) (contracts.SearchResponse, error)
}
Adding a new engine:
- Create a new struct implementing the
Engineinterface ininternal/engines/(single file, e.g.,newengine.go) - Add a test file alongside it (use
roundTripperFuncandhttpResponsehelpers inhttp_mock_test.gofor mocking HTTP) - Register it in
NewDefaultPortedEngines()infactory.go - Add its name to
defaultPortedEnginesinplanner.go - Add category mappings in
inferFromCategories()if applicable
Configuration
Config is loaded from config.toml (see config.example.toml). All fields can be overridden via environment variables (env vars take precedence over zero-value TOML fields). Key sections: [server], [upstream], [engines], [cache], [cors], [rate_limit], [global_rate_limit], [burst_rate_limit].
Conventions
- Module path:
github.com/metamorphosis-dev/kafka - Tests use shared mock helpers in
internal/engines/http_mock_test.go(roundTripperFunc,httpResponse) - Engine implementations are single files under
internal/engines/(e.g.,wikipedia.go,duckduckgo.go) - Response merging de-duplicates by
engine|title|urlkey; suggestions/corrections are merged as sets MainResultuses customUnmarshalJSON/MarshalJSONto preserve unknown upstream JSON keys- HTML templates and static files are embedded at build time via
//go:embedininternal/views/ - Structured logging via
log/slogwith JSON handler