Skip to content

Repository files navigation

Evresis

Private search. Local intelligence. Full citations.

Evresis is a private, local-first federated search engine. It fuses self-hosted SearXNG web results with your Obsidian vault, local documents, and an opt-in browser-history index, then answers with a locally run llama.cpp model and full source citations.

The name comes from Greek εὕρεσις—“the act of finding, discovery,” from the same root as εὕρηκα, “I have found it.” The binary and short form are evres. At evres.is, the dot completes the word: evres.is/private · evres.is/local · evres.is/yours.

Start

./start.sh

This starts SearXNG with Docker Compose, then binds Evresis’s UI and API to 0.0.0.0:5173.

Windows / WSL2

Double-click launch-evres.bat, or create a shortcut targeting evres.vbs for a hidden launcher. The launcher uses the default user in the Ubuntu WSL distro, discovers its own project directory, waits for /api/health, and opens Chrome in standalone app mode at 1280×850. Change DISTRO if your distribution has a different name. EVRES_URL is the address the launcher polls and opens.

Evresis also ships a web app manifest and a service worker that caches the app shell, so it is installable as a PWA. A cached shell can survive a redeploy until the new service worker activates; hard-reload once after upgrading.

Local engines

Service Default
Evresis UI + API http://0.0.0.0:5173
SearXNG http://127.0.0.1:8888
llama-server http://127.0.0.1:8080
Model First model returned by llama.cpp /v1/models
Model catalog Every model returned by llama.cpp /v1/models

The UI exposes the complete live server catalog and records the model actually used for each response. Set LLM_MODEL or LLM_FALLBACK_MODEL only when you want to pin a particular advertised model.

Environment

Variable Default Effect
SEARXNG_URL http://127.0.0.1:8888 Web retrieval endpoint
LLM_URL http://127.0.0.1:8080 llama.cpp server
LLM_MODEL (first advertised) Optional preferred model
LLM_FALLBACK_MODEL (empty) Optional fallback when the selected model is unavailable
EVRES_DB_PATH server/db.sqlite SQLite location
EVRES_ALLOWED_ORIGINS (empty) Extra comma-separated browser origins allowed to call /api/*
EVRES_BROWSER_HISTORY_PATHS (empty) Extra browser history databases to offer for import
EVRES_SEARCH_MAX_RETRIES 2 Retries per SearXNG query, clamped 0..5. Set 0 for exactly one live request per query

For a non-breaking upgrade, the former KOUGLE_DB_PATH, KOUGLE_ALLOWED_ORIGINS, KOUGLE_BROWSER_HISTORY_PATHS, and KOUGLE_SEARCH_MAX_RETRIES names remain supported as fallbacks. The correctness-corpus runner likewise accepts its former URL and model variable names.

Homelab API

Use the host address reachable from your trusted LAN.

Search

curl -s http://<your-host>:5173/api/search \
  -H 'content-type: application/json' \
  -d '{"query":"site:github.com sqlite bm25","focus":"all","target":"all","semantic":true,"count":10}'

GET /api/search?q=... remains available for browser and shell use. target accepts all, web, files, vault, documents, or history. A target that excludes the web makes no outbound request and returns no web results. focus accepts all, news, academic, videos, images, code, social, reddit, and x; an unrecognized value falls back to all.

Federated responses include source kinds, per-provider counts, semantic-expansion status, weighted reciprocal-rank fusion scores, and a degraded flag. counts.web and counts.local partition the returned rows; counts.history deliberately overlaps counts.web, because a live page you have also visited is one card carrying a private corroboration signal.

Grounded answer (SSE)

curl -N http://<your-host>:5173/api/ask \
  -H 'content-type: application/json' \
  -d '{"query":"How should I structure my reverse proxy?","focus":"all","target":"all","semantic":true}'

The stream carries a retrieval event with { degraded, retrievalDiagnostics } immediately after sources, so a client can tell a blocked or partially down engine fleet apart from a corpus that genuinely held nothing.

Deep research (SSE)

curl -N http://<your-host>:5173/api/research \
  -H 'content-type: application/json' \
  -d '{"query":"Compare practical zero-trust patterns for a small homelab","target":"all"}'

Research emits live thinking_delta, plan, searching, reading, search_results, analyzing, analysis, gap_fill, sources, synthesizing, delta, answer_replace, warning, quality, metrics, and done events. Individual rate-limited search branches degrade without terminating the report.

A single research run issues up to five plan queries plus two gap-fill queries. With the default retry setting that is up to 21 SearXNG requests; set EVRES_SEARCH_MAX_RETRIES=0 to cap it at seven.

Knowledge query

curl -s 'http://<your-host>:5173/api/knowledge/query?q=traefik&limit=8'

Add one or more local disks, Obsidian vaults, project directories, or WSL-mounted SMB/NFS resources from the UI, or with:

curl -s http://127.0.0.1:5173/api/knowledge/index \
  -H 'content-type: application/json' \
  -d '{"path":"/home/you/vault"}'

Roots must resolve under /home/ or /mnt/. Windows C:\... and \\wsl$\... paths are translated. Roots may not overlap an existing root, and indexing is capped at 24 resources, 8000 files, and 80000 chunks.

Evresis extracts text/code directly, uses pdftotext for PDFs and Pandoc for DOCX/ODT/RTF/EPUB, and retains a safe metadata-only record for unsupported files. Local query operators include type:, ext:, path: (alias folder:), tag:, before:, after:, quoted phrases, and -excluded terms. A type: value outside note, document, code, and file is treated as an extension, so type:pdf behaves as ext:pdf. Concept search uses the selected local model for bounded query expansion and falls back to BM25 keyword retrieval.

before: and after: both reject a chunk whose modification time is unknown, so neither can silently match a file whose date was never recorded.

Local retrieval is an in-memory BM25 scan over chunks loaded from SQLite, not an FTS5 index. Term frequencies and document frequencies are rebuilt in the process on startup and after every index mutation, so memory and first-query cost scale with the whole index. Only the browser-history index is FTS5.

In a resource Evresis detects as an Obsidian vault, meaning a .obsidian directory at its root, frontmatter aliases, frontmatter and inline tags, and [[wiki links]] in .md/.markdown files are folded into the search index. They are searchable as terms, as tag: filters, and inside quoted phrases.

Re-index after upgrading. An index written by an older build stores no tags, aliases, wiki links, or modification times, and the load path backfills only source kind and extension. On such an index tag:, before:, and after: return nothing until the affected resources are re-indexed. Re-indexing also applies the current credential-file exclusion below.

Excluded files

Dotfiles and files whose names look like credentials are never indexed and never appear in search. The pattern covers .env, credential(s), secret(s), passwd, passphrase, private key(s), api key(s), access token(s), token(s), recovery/backup codes, id_rsa, id_ed25519, and the .pem, .key, .p12, and .pfx extensions, bounded by whitespace, dot, underscore, or hyphen. A note genuinely named recovery codes.md is therefore skipped; the per-resource skippedSensitiveFiles counter reports how many.

Private browser history

Evresis discovers common Chrome, Edge, Brave, Chromium, and Firefox profiles on Linux and Windows mounts. Import is opt-in from Knowledge resources → Private browser memory. The browser database is copied to a temporary directory and opened read-only; Evresis never modifies it. Only HTTP(S) URL/title/visit metadata is copied into the local FTS5 index, and credential material is stripped from URLs on the way in: userinfo, credential-bearing query parameters, and OAuth-style token fragments are removed.

curl -s http://127.0.0.1:5173/api/browser-history/status
curl -s -X POST http://127.0.0.1:5173/api/browser-history/import \
  -H 'content-type: application/json' -d '{}'
curl -s -X DELETE http://127.0.0.1:5173/api/browser-history

No browser page content, cookies, passwords, form data, or session data is imported. Clearing this index does not touch the browser's own history.

Shared persistence

server/db.sqlite runs in WAL mode and stores:

  • shared search history;
  • collections/bookmarks;
  • the latest shared session;
  • indexed resource metadata, extracted document chunks, and line ranges;
  • opt-in browser-history URL/title/visit metadata and its FTS5 index;
  • search/answer/research telemetry;
  • a durable per-request query record for every /api/search, /api/ask, and /api/research call, carrying the answer text, the exact source pack, citation identifiers, the grounding assessment, retrieval diagnostics, token/timing metrics, and the outcome (succeeded, no_evidence, failed, aborted, or interrupted).

Runtime SQLite files are ignored by Git. Browser storage remains an offline/recovery cache and is merged into the server database on startup. Settings → Recovery & data can export or restore a portable JSON recovery bundle. Interrupted streams are checkpointed locally and recovered as explicitly partial output.

Grounding and query vitals

Grounded answers and reports receive a deterministic citation-coverage assessment. Invalid citation identifiers are surfaced rather than silently trusted, and Evresis refuses to synthesize a factual answer when no web or local evidence is available. Raw model HTML is disabled.

Evresis also gates the answer after generation. If a completed answer resolves no citation into the evidence pack, it is recorded as no_evidence with the error Answer cited no retrievable source and the stream terminates with done { grounded: false }, rather than shipping as an ordinary success.

Coverage is a citation-completeness measure, not a factuality guarantee. It grades claim segments of five content words or more, excluding headings, questions, code fences, list scaffolding, and Markdown table header rows. Each table data row is graded independently.

When llama.cpp supplies timing/usage data, Evresis displays tokens per second, TTFT, and token counts. It also shows clearly labelled estimates for local energy, carbon, electricity cost, and an illustrative cloud-API price range. Hardware wattage and regional assumptions are configurable; these values are comparisons, not power-meter readings.

Useful endpoints:

  • GET /api/health
  • GET /api/history
  • GET /api/collections
  • GET /api/session
  • GET /api/telemetry
  • GET /api/queries and GET /api/queries/:requestId
  • GET /api/knowledge/status
  • GET /api/knowledge/search and GET /api/knowledge/file
  • GET /api/browser-history/status
  • GET /api/models
  • GET /api/diagnostics/database

The UI additionally drives POST /api/models/select, POST /api/knowledge/index, POST /api/knowledge/clear, DELETE /api/knowledge/resources/:id, POST /api/journey/snapshot, POST /api/chat/conversation, POST /api/related, POST /api/takeaways, POST /api/diagnostics/database/run, and the mutating history, session, collection, and telemetry routes. All of them are reachable from any host that can reach port 5173.

Request bodies are capped at 3 MB and answer with 413 beyond it. Every API response carries X-Content-Type-Options: nosniff, X-Frame-Options: DENY, and Cache-Control: no-store unless set otherwise. Reusing a requestId returns 409 rather than re-running the request.

Verification

bun run typecheck
bun test server src
bun run build

The five-case live correctness corpus covers world knowledge, frozen recent information, a tricky normative question, deep research, and local+web fusion:

bun run test:corpus
bun run test:corpus -- --case recent-bun-release
bun run test:corpus -- --list

It grades the persisted query record for each request—the source pack, citations, gold facts, and retrieval provenance—not just whether the model produced plausible prose. Date-sensitive gold answers are frozen at August 28, 2026. Override the target with EVRES_URL and the fixed run model with EVRES_CORPUS_MODEL.

For browser coverage:

bun run test:e2e

The script reuses a dev server already listening on 127.0.0.1:5173 and starts one itself otherwise, so a separate bun run dev is not needed.

Security posture

Evresis binds to every interface for homelab use and has no API authentication. Cross-origin API calls are restricted to loopback and RFC1918 origins, with EVRES_ALLOWED_ORIGINS to add others, so an arbitrary website cannot read your data through a browser you have open. That restriction does not help against direct access: any host that can reach port 5173 can read your vault, your browser-history index, and your search log. Limit port 5173 to trusted LAN hosts with the Windows/Linux firewall before exposing it beyond the private network.

Further reading

  • docs/RETRIEVAL-HARDENING-2026-08-29.md — the evidence-driven retrieval hardening pass, what was wrong, and what changed.
  • docs/ROADMAP.md — verified remaining work, ranked.

About

Private, local-first federated search across SearXNG, Obsidian, local documents, and opt-in browser history—with locally generated answers and full citations.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages