Commit graph

43 commits

Author SHA1 Message Date
d67975a8c5 fix(settings): re-render panel when last engine unchecked to enforce minimum
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 03:00:12 +01:00
a4e77bd8b2 feat(settings): add JS module for localStorage preferences and panel
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 03:00:12 +01:00
39f8738361 fix(settings): add html[data-theme=light] explicit light mode reset 2026-03-22 03:00:12 +01:00
f530e84d71 feat(settings): add popover, toggle, and bottom-sheet CSS 2026-03-22 03:00:12 +01:00
162b423fa7 docs: fix plan issues — mobile FAB, RSS format, init wiring
Reviewed-by: plan-document-reviewer

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 03:00:12 +01:00
efbb9da108 docs: add settings UI implementation plan
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 03:00:12 +01:00
6e98b74d20 docs: add settings UI design spec
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 03:00:12 +01:00
Franz Kafka
fc6e6ada68 Merge branch 'feat/google-engine', remote-tracking branch 'origin/main'
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 7s
Mirror to GitHub / mirror (push) Failing after 3s
Tests / test (push) Failing after 23s
2026-03-22 01:35:20 +00:00
Franz Kafka
7d23f13dfa feat: add Google engine using GSA User-Agent scraping
SearXNG approach: use Google Search Appliance (GSA) User-Agent
pool — these are whitelisted enterprise identifiers Google trusts.

Key techniques:
- GSA User-Agent (iPhone OS + GSA/ version) instead of Chrome desktop
- CONSENT=YES+ cookie to bypass EU consent wall
- Parse /url?q= redirector URLs (unquote + strip &sa= params)
- div.MjjYud class for result containers (SearXNG selector)
- data-sncf divs for snippets
- detect sorry.google.com blocks
- Suggestions from ouy7Mc class cards
2026-03-22 01:29:46 +00:00
21b77f25bf refactor: remove SearXNG references and rename binary to kafka
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 8s
Mirror to GitHub / mirror (push) Failing after 3s
Tests / test (push) Successful in 38s
- Rename cmd/searxng-go to cmd/kafka
- Remove all SearXNG references from source comments while keeping
  "SearXNG-compatible API" in user-facing docs
- Update binary paths in README, CLAUDE.md, and Dockerfile
- Update log message to "kafka starting"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 01:47:03 +01:00
8ea318ad4a docs: update CLAUDE.md with autocomplete package and endpoint
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 7s
Mirror to GitHub / mirror (push) Failing after 3s
Tests / test (push) Successful in 36s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 01:27:10 +01:00
Franz Kafka
e90f6c0876 feat: add autocomplete dropdown UI with keyboard nav
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 11s
Mirror to GitHub / mirror (push) Failing after 3s
Tests / test (push) Successful in 54s
- Inline JS in base.html: debounced fetch from /autocompleter on keyup
- Keyboard nav: arrows to navigate, Enter to select, Esc to close
- Highlight matching prefix in suggestions
- Click to select and submit
- Dropdown positioned absolutely below search input
- Dark mode compatible via existing CSS variables
2026-03-22 00:20:43 +00:00
7a2ca8672e docs: add MIT license
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 5m42s
Mirror to GitHub / mirror (push) Failing after 3s
Tests / test (push) Has been cancelled
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 01:18:22 +01:00
33e1e0c3eb chore: ignore result binary
Some checks failed
Mirror to GitHub / mirror (push) Has been cancelled
Build and Push Docker Image / build-and-push (push) Has been cancelled
Tests / test (push) Failing after 17s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 01:07:12 +01:00
9b280ad606 feat: add /autocompleter endpoint for search suggestions
Some checks failed
Mirror to GitHub / mirror (push) Waiting to run
Tests / test (push) Waiting to run
Build and Push Docker Image / build-and-push (push) Has been cancelled
Proxies to upstream SearXNG /autocompleter if configured, otherwise
falls back to Wikipedia OpenSearch API. Returns a JSON array of
suggestion strings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 01:06:25 +01:00
90810cb934 fix(flake): set vendorHash and skip tests in build
Some checks failed
Build and Push Docker Image / build-and-push (push) Has started running
Mirror to GitHub / mirror (push) Failing after 4s
Tests / test (push) Has been cancelled
vendorHash was empty, causing build failures. The hash was
obtained by running the build once and using the error output.

Tests are skipped because they require network access; CI runs
them via the test workflow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 00:47:10 +01:00
0d3f3c19d7 fix: add missing engines to defaultPortedEngines
duckduckgo, github, reddit, and bing were registered in factory.go
and config.go but missing from planner.go, so they were silently
skipped when LOCAL_PORTED_ENGINES was not set.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 00:13:57 +01:00
b85a06cf11 ci: add test workflow
Runs go test -race -v ./... on push and PRs to main.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 00:11:25 +01:00
6001979d7f docs: add CLAUDE.md for Claude Code onboarding
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 23:52:23 +01:00
52395c7cd0 fix(ci): use GHCR_TOKEN for GitHub mirror
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 9m27s
Mirror to GitHub / mirror (push) Failing after 3s
Reuse the same token secret for both Docker push and git mirror.
2026-03-21 21:05:43 +00:00
8a7b840b69 fix(ci): use raw git push for GitHub mirror
ad-m/github-push-action chokes on secret substitution in act_runner.
Use plain git commands instead — no third-party action needed.
2026-03-21 21:04:31 +00:00
6346fb7155 chore: update Go module path to github.com/metamorphosis-dev/kafka
Module path now matches the GitHub mirror location.
All internal imports updated across 35+ files.
2026-03-21 19:42:01 +00:00
9a6a40c817 fix(ci): rename GITHUB_TOKEN to GHCR_TOKEN for Forgejo compat
Forgejo reserves GITHUB_TOKEN as a protected secret name.
Use GHCR_USERNAME + GHCR_TOKEN instead.
2026-03-21 19:31:20 +00:00
fed5fa469e fix(ci): use full GitHub URLs for actions
data.forgejo.org is unreachable from the runner. Using https://github.com/
prefix for all action uses to fetch directly from GitHub instead of the
Forgejo marketplace mirror.
2026-03-21 19:26:45 +00:00
c761805b56 ci: add Docker build + push workflow
Forgejo Actions workflow that builds and pushes Docker images on:
- Push to main → tagged 'latest' + short SHA
- Tags matching 'v*' → semver tags (1.0.0, 1.0, latest)
- PRs → build only (no push)

Pushes to both GHCR and Docker Hub with GitHub Actions cache.

Required secrets:
- DOCKERHUB_USERNAME: Docker Hub username
- DOCKERHUB_PASSWORD: Docker Hub access token
- GITHUB_TOKEN: automatically provided by Forgejo (or set manually for GHCR)

Note: Requires a Forgejo Actions runner to be registered. See:
https://forgejo.org/docs/latest/user/actions/#runner-setup
2026-03-21 19:24:04 +00:00
e5295fa69d chore: rename project from gosearch to kafka
A search engine named after a man who proved answers don't exist.

Renamed everywhere user-facing:
- Brand name, UI titles, OpenSearch description, CSS filename
- Docker service name, NixOS module (services.kafka)
- Cache key prefix (kafka:), User-Agent strings (kafka/0.1)
- README, config.example.toml, flake.nix descriptions

Kept unchanged (internal):
- Go module path: github.com/ashie/gosearch
- Git repository URL: git.ashisgreat.xyz/penal-colony/gosearch
- Binary entrypoint: cmd/searxng-go
2026-03-21 19:20:47 +00:00
be7ba66a09 docs: complete README rewrite
- Updated to reflect all current features (9 engines, HTMX frontend, Valkey cache, 3-layer rate limiting, CORS, OpenSearch)
- Added quick start for binary, Docker Compose, and NixOS
- Documented all endpoints, API parameters, and response format
- Configuration reference with environment variable table
- Engine table with source and notes
- ASCII architecture diagram
- Docker and NixOS deployment sections
2026-03-21 18:51:14 +00:00
13040268d6 feat: add global and burst rate limiters
Three layers of rate limiting, all disabled by default, opt-in via config:

1. Per-IP (existing): 30 req/min per IP
2. Global: server-wide limit across all IPs
   - Lock-free atomic counter for minimal overhead
   - Returns 503 when exceeded
   - Prevents pool exhaustion from distributed attacks
3. Burst: per-IP burst + sustained windows
   - Blocks rapid-fire abuse within seconds
   - Returns 429 with X-RateLimit-Reason header
   - Example: 5 req/5s burst, 60 req/min sustained

Config:
[global_rate_limit]
requests = 0  # disabled by default
window = "1m"

[burst_rate_limit]
burst = 0  # disabled by default
burst_window = "5s"
sustained = 0
sustained_window = "1m"

Env overrides: GLOBAL_RATE_LIMIT_REQUESTS, GLOBAL_RATE_LIMIT_WINDOW,
BURST_RATE_LIMIT_BURST, BURST_RATE_LIMIT_BURST_WINDOW,
BURST_RATE_LIMIT_SUSTAINED, BURST_RATE_LIMIT_SUSTAINED_WINDOW

Full test coverage: concurrent lock-free test, window expiry, disabled states,
IP isolation, burst vs sustained distinction.
2026-03-21 18:35:31 +00:00
91ab76758c feat: add NixOS module for native deployment
- flake.nix now exports packages, NixOS module, and dev shell
- NixOS module: services.gosearch
  - Configurable port, base URL, user/group, state dir
  - Creates system user automatically
  - Runs as systemd service with auto-restart
  - Optional firewall opening
- To deploy on your NixOS VPS:
  1. Get the vendor hash: nix build .#packages.x86_64-linux.default (copy the hash)
  2. Add to your flake inputs and imports
  3. Enable in configuration.nix
2026-03-21 17:42:05 +00:00
4ec600f6c0 feat: add OpenSearch XML endpoint
- Serve /opensearch.xml with configurable base URL
- Browsers can now add gosearch as a search engine from the address bar
- Configurable via [server] base_url or BASE_URL env var
- XML template embedded in the binary via go:embed
- Added base_url to config.example.toml
2026-03-21 17:40:05 +00:00
3caf702c4f Merge pull request 'feat: add DuckDuckGo, GitHub, Reddit, and Bing engines' (#1) from feat/more-engines into main
Reviewed-on: penal-colony/gosearch#1
2026-03-21 17:35:53 +00:00
a8ab29b23a fix: fix DDG and Bing parsers — verified with live tests
DuckDuckGo:
- Fixed parser to handle single-quoted class attributes (class='result-link')
- Decode DDG tracking URLs (uddg= parameter) to extract real URLs
- Match snippet extraction to actual DDG Lite HTML structure (</td> terminator)

Bing:
- Switched from HTML scraping (blocked by JS detection) to RSS endpoint
  (?format=rss) which returns parseable XML
- Added JSON API response parsing as fallback
- Returns graceful unresponsive_engines entry when blocked

Live test results:
- DuckDuckGo: 9 results 
- GitHub: 10 results (14,768 total) 
- Bing: 10 results via RSS 
- Reddit: skipped (403 from sandbox, needs browser-like context)
2026-03-21 16:57:02 +00:00
df8fe9474b feat: add DuckDuckGo, GitHub, Reddit, and Bing engines
- DuckDuckGo: scrapes Lite HTML endpoint for results
  - Language-aware region mapping (de→de-de, ja→jp-jp, etc.)
  - HTML parser extracts result links and snippets from DDG Lite markup
  - Shared html_helpers.go with extractAttr, stripHTML, htmlUnescape

- GitHub: uses public Search API (repos, sorted by stars)
  - No auth required (10 req/min unauthenticated)
  - Shows stars, language, topics, last updated date
  - Paginated via GitHub's page parameter

- Reddit: uses public JSON search API
  - Respects safesearch (skips over_18 posts)
  - Shows subreddit, score, comment count
  - Links self-posts to the thread URL

- Bing: scrapes web search HTML (b_algo containers)
  - Extracts titles, URLs, and snippets from Bing's result markup
  - Handles Bing's tracking URL encoding

- Updated factory, config defaults, and config.example.toml
- Full test suite: unit tests for all engines, HTML parsing tests,
  region mapping tests, live request tests (skipped in short mode)

9 engines total: wikipedia, arxiv, crossref, braveapi, qwant,
duckduckgo, github, reddit, bing
2026-03-21 16:52:11 +00:00
28b61ff251 feat: HTMX + Go Templates HTML frontend
- Add internal/views/ package with embedded templates and static files
- Go html/template with SearXNG-compatible CSS class names
- Dark mode via prefers-color-scheme, responsive layout, print styles
- HTMX integration:
  - Debounced instant search (500ms) on the search input
  - Form submission targets #results via hx-post
  - Pagination buttons are HTMX-powered (swap results div only)
  - HX-Request header detection for fragment vs full page rendering
- Template structure:
  - base.html: full page layout with HTMX script, favicon, CSS
  - index.html: homepage with centered search box
  - results.html: full results page (wraps base + results_inner)
  - results_inner.html: results fragment (HTMX partial + sidebar + pagination)
  - result_item.html: reusable result article partial
- Smart format detection: browser requests (Accept: text/html) default to HTML,
  API clients default to JSON
- Static files served at /static/ from embedded FS (CSS, favicon SVG)
- Index route at GET /
- Empty query on HTML format redirects to homepage
- Custom CSS (gosearch.css): clean, minimal, privacy-respecting aesthetic
  with light/dark mode, responsive breakpoints, print stylesheet
- Add views package tests
2026-03-21 16:10:42 +00:00
ebeaeeef21 feat: add CORS and rate limiting middleware
CORS:
- Configurable allowed origins (wildcard "*" or specific domains)
- Handles OPTIONS preflight with configurable methods, headers, max-age
- Exposed headers support for browser API access
- Env override: CORS_ALLOWED_ORIGINS

Rate Limiting:
- In-memory per-IP sliding window counter
- Configurable request limit and time window
- Background goroutine cleans up stale IP entries
- HTTP 429 with Retry-After header when exceeded
- Extracts real IP from X-Forwarded-For and X-Real-IP (proxy-aware)
- Env overrides: RATE_LIMIT_REQUESTS, RATE_LIMIT_WINDOW, RATE_LIMIT_CLEANUP_INTERVAL
- Set requests=0 in config to disable

Both wired into main.go as middleware chain: rate_limit → cors → handler.
Config example updated with [cors] and [rate_limit] sections.
Full test coverage for both middleware packages.
2026-03-21 15:54:52 +00:00
4c54ed5b56 feat: add multi-stage Dockerfile and docker-compose.yml
- Multi-stage Dockerfile: golang:1.24-alpine builder → alpine:3.21 runtime
  - CGO_ENABLED=0 for static binary, stripped with -ldflags="-s -w"
  - Only copies ca-certificates and tzdata to runtime image
  - Config via volume mount at /etc/gosearch/config.toml
- docker-compose.yml: gosearch + Valkey 8
  - Valkey healthcheck ensures gosearch starts after cache is ready
  - Persistent Valkey volume
  - config.toml mounted read-only
- Update .gitignore with Go build artifacts
2026-03-21 15:46:33 +00:00
94322ceff4 feat: Valkey cache for search results
- Add internal/cache package using go-redis/v9 (Valkey-compatible)
- Cache keys are deterministic SHA-256 hashes of search parameters
- Cache wraps the Search() method: check cache → miss → execute → store
- Gracefully disabled if Valkey is unreachable or unconfigured
- Configurable TTL (default 5m), address, password, and DB index
- Environment variable overrides: VALKEY_ADDRESS, VALKEY_PASSWORD,
  VALKEY_DB, VALKEY_CACHE_TTL
- Structured JSON logging via slog throughout cache layer
- Refactored service.go: extract executeSearch() from Search() for clarity
- Update config.example.toml with [cache] section
- Add cache package tests (key generation, nop behavior)
2026-03-21 15:43:47 +00:00
385a7acab7 feat: concurrent engine execution with graceful degradation
- Run all local engines in parallel using goroutines + sync.WaitGroup
- Individual engine failures are captured as unresponsive_engines entries
  instead of aborting the entire search request
- Context cancellation is respected: cancelled engines report as unresponsive
- Upstream proxy failure is also gracefully handled (single unresponsive entry)
- Extract unresponsiveResponse() and emptyResponse() helpers for consistency
- Add comprehensive tests:
  - ConcurrentEngines: verifies parallelism (2x100ms engines complete in ~100ms)
  - GracefulDegradation: one engine fails, one succeeds, both represented
  - AllEnginesFail: no error returned, all engines in unresponsive_engines
  - ContextCancellation: engine respects context timeout, reports unresponsive
2026-03-21 15:39:00 +00:00
5181073a95 fix: clear env vars in TestLoadFromFile to test pure config values
The sandbox had BRAVE_API_KEY set, causing the env override to win
over the config file value in the test assertion.
2026-03-21 15:34:06 +00:00
8649864971 feat: migrate from env vars to config.toml
- Add internal/config package with TOML parsing (BurntSushi/toml)
- Create config.example.toml documenting all settings
- Update main.go to load config via -config flag (default: config.toml)
- Environment variables remain as fallback overrides for backward compat
- Config file values are used as defaults; env vars override when set
- Add comprehensive tests for file loading, defaults, and env overrides
- Add config.toml to .gitignore (secrets stay local)
2026-03-21 14:56:00 +00:00
dc44837219 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
2026-03-20 20:34:08 +01:00
7783367c71 chore: update gitignroe 2026-03-20 17:15:36 +01:00
d2ca4190a3 first commit 2026-03-20 17:14:32 +01:00