A fast, private, multi-source search engine by XPDevs. Gateway searches a bundled local web index (50,000 real websites across 50,000 domains) plus live Wikipedia / Wikidata open data — with no API keys, no build step, and no generative AI.
Live behaviour: main results paint from the local index first (typically milliseconds), while Quick Wiki, spell-check, and web enrichment resolve asynchronously afterwards.
Your searches are recorded to a local-only search history you can replay, filter, switch off, or delete. It never leaves the browser and never affects ranking.
- Instant main results (faster than Google for the common case)
- Two-stage search: Stage 1 renders
gatewaySearchLocal()(local IndexedDB/memory index only, no network) insiderequestAnimationFrame; Stage 2 enriches withgatewayCrawl()(Wikipedia live, official sites, cited web links) without replacing the visible list. - Result stats show measured time, e.g.
120 ranked results (0.03 seconds). content-visibility: autoon.result-item, 20-per-page pagination, in-memory +localStorage+ IndexedDB caches, request dedup via_once()/_inflight.
- Two-stage search: Stage 1 renders
- Quick Wiki (lazy, sourced, with image)
- Deterministic answers from Wikipedia extracts + Wikidata claims (
P577,P571,P36,P1082, …); date-intent parsing, never generative AI. - Renders after the main list: skeleton placeholder first, replaced asynchronously when
gatewayQuickWiki()resolves; failures — or queries Wikipedia cannot answer — clear the skeleton and show no box at all without touching results. Can be disabled entirely via Settings (toggleQuickWiki()/setQuickWikiEnabled()/gatewayQuickWikiEnabled()). - Summary-only answers are gated on a strong title match (exact or ≥75% subject-word overlap); weak matches return
null. - Answer/context duplicates eliminated:
_dedupeAnswerContext()strips any context repeating the answer (plus a render-side guard), so the answer sentence never appears twice. - Representative image:
pageimagesthumbnail|originalat 500px, with sibling-article fallback; floated right so body text wraps around it and continues underneath;<img loading="lazy" decoding="async" fetchpriority="low">with caption andonerrorcollapse (stacks full-width on mobile). - Header badge
No generative AI, property chip (e.g.P577), source links (official + cited, deduped per-domain).
- Deterministic answers from Wikipedia extracts + Wikidata claims (
- XPDevs profile (local, deterministic)
- Searching
XPDevs— or any query containing the token (xpdevs,xp dev,xp-dev,xpdev,gateway search) — is answered entirely fromXPDEV_PROFILEinmain.js, with no network call and no Wikipedia round-trip: Quick Wiki shows the profile answer, the context line, the repository count, source links (website, GitHub, DoorsOS, ExamOS, Genesis-AI) and the project logohttps://xpdevs.github.io/logo/XPDevs.pngwith anXPDevssource badge (never a false "Wikipedia" badge/caption). - The same query pins 62 rows above every ranked result: the official site, the GitHub organisation, Gateway, and all 59 public repositories (
github.com/XPDevs/<name>, each with language, stars, last update and afork of an upstream projectflag where applicable). An XPDevs query renders the whole set in one page instead of 20-row pages, so no repository is hidden behind "Show more results". - Facts are sourced from the official site (
xpdevs.github.io: operating systems, United Kingdom, Genesis-AI focus) and the GitHub API; every other query path is untouched.
- Searching
- Favicons on every web URL
- Each result row and each Quick Wiki source link shows a favicon: Google S2 primary (
s2/favicons?domain=…&sz=32), DuckDuckGoicons.duckduckgo.com/ip3/…fallback, letter-avatar final fallback — so no row is icon-less. - Images are
loading="lazy" decoding="async".
- Each result row and each Quick Wiki source link shows a favicon: Google S2 primary (
- Unique descriptions
- All 50,000
index.jsondescriptions are unique (verified: 50,000 / 50,000 unique, and 50,000 / 50,000 unique URLs). - All 50,000 entries are real websites, one per domain, sourced from Wikidata and confirmed to answer over HTTP at build time.
- All 50,000
- Search history (local, reversible)
- Every search is recorded to
localStorageundergw-historyas{q, t, n, ms}— query, time, result count, duration — newest first, capped at 200 entries. Recorded once inperformSearch()(main.js) after the fast local stage paints, so the stored count and duration match what the user actually saw; the Stage 2 enrichment pass corrects the count in place rather than logging a second entry. /history/(history/index.html) is a standalone static page — it cannot loadmain.js, so it implements the same reader against the same key. Entries are grouped by day, filterable, and each row replays the search via../?query=…(already honoured on load bymain.js:1108).- Controls: delete a single search, delete all, export the list as JSON, and a switch to stop recording new searches (
gw-history-optout). Recording is on by default. - Nothing is uploaded, never attached to a search, never included in a custom or exported index, and never used for ranking or profiling. Storage failures (quota, private mode) degrade silently and never break a search.
⚠️ Deliberately not namedhistory:performSearch()useswindow.history.replaceState(History API), and a global of that name would clobber it. The API surface iswindow.gatewaySearchHistory.
- Every search is recorded to
- BM25 hybrid ranking (see Ranking below) — IDF with additive smoothing,
IDF^1.5term weighting, Okapi BM25, and a static authority prior. - Plus: 25-language UI, dark mode, autocomplete (
getSuggestions),Did you mean?spell-check,I'm Feeling Lucky, uploadable custom index, safe-domain filtering, tracking-param stripping.
gateway-main/
├── index.html # Home + results UI, settings modal
├── ui.css # Theme, results, Quick Wiki, favicon, responsive styles
├── main.js # UI, staged search orchestration, rendering (favicons, Quick Wiki image)
├── crawl.js # Index engine, Wikipedia/Wikidata fetchers, ranking, caches
├── index/
│ ├── index.json # Bundled local index: [{t, u, d, s}] — 50,000 unique-description entries
│ └── index-meta.json # Index version + entry count (cache-busting)
├── about/ advertising/ business/ how-search-works/ history/ privacy/ terms/ # Static pages
├── favicon.ico
└── 404.html
No build, no dependencies. Any static server works:
cd gateway-main
python3 -m http.server 8000
# open http://localhost:8000/Or npx serve ., GitHub Pages, Nginx, etc. Use http(s)://, not file:// (fetch + IndexedDB require it).
Custom index (optional): Settings → Upload Index → select a JSON array of {t, u, d, s}. It is validated, persisted to IndexedDB, and versioned (old caches invalidated).
Settings (gear icon): language, dark mode, Quick Wiki on/off (persisted as gw-quickwiki in localStorage, default on — when off, no Quick Wiki box is fetched or rendered and the Try "release date…" example re-enables it), and index upload.
User query
├─ Stage 1 (sync-fast): gatewaySearchLocal() → prepared normalised index → score → _boostXpdevs → paint (rAF) + timing
├─ Stage 2a (async): gatewayCrawl() → local + Wikipedia search + official URLs + extlinks → merge new URLs only, re-sort, update count
├─ Stage 2b (async, lazy): gatewayQuickWiki() → parse intent → wiki search → best-page match → Wikidata claims → image → renderQuickWiki()
└─ Stage 2c (async): gatewaySpellCheck() → renderDidYouMean()
Key modules:
crawl.js:_normalize,_prepareIndex(term frequencies, document frequency,avgdl, authority),searchIndex,gatewaySearchLocal,gatewayCrawl,_searchWikipedia(opensearch → extracts+pageimages+pageprops+info, extlinks for best page),_wikidataClaims/_labelsForClaims/_formatWikidataTime,quickWiki(+_dedupeAnswerContext,_subjectMatchRatiogating),getSuggestions,spellCheck,_dedupeAndRank,isSafeResult,cleanTracking, tiered cache (memory → localStoragegw_v6_→ IndexedDBGatewayIndex). Loads the bundled index fromindex/index.json+index/index-meta.jsonviaINDEX_DIR. Introspection:gatewayRankDebug(query),gatewayAuthorityScore(domain).main.js:performSearch(staged),_gwRender(stats with seconds +renderDidYouMean+renderQuickWiki+ paginatedrenderItem),_faviconHtml(S2 → DDG → letter),_answerLinkHtml, translations (25 locales), dark mode, autocomplete wiring.
Every result — bundled index, live Wikipedia, official sites, cited web links — is scored with one hybrid function, so all sources compete on identical terms.
FinalScore = (BM25_Score * 0.8) + (Authority_Score * 0.2)
- IDF with additive smoothing —
IDF(q_i) = ln(1 + (N - n(q_i) + 0.5) / (n(q_i) + 0.5))N = 20,000,000,000(web-corpus scale,RANK_CORPUS_SIZE; override withwindow.GATEWAY_RANK_CORPUSfor self-hosted corpora).n(q_i)= document frequency, counted at index load.
- Dynamic query term weighting —
W(q_i) = IDF(q_i) ^ 1.5, so rare entities are boosted and common terms are damped. - Okapi BM25 —
BM25(D, Q) = Σ W(q_i) · (f(q_i,D) · (k1 + 1)) / (f(q_i,D) + k1 · (1 - b + b · (|D| / avgdl))), withk1 = 1.2,b = 0.75.- Documents are
title + description;|D|is the word count;avgdlis the mean across the whole index (12.73 words for the bundled set: 548,586 tokens over 50,000 documents, 79,765 distinct terms).
- Documents are
- Authority — a pre-calculated static float in
[0.0, 10.0], PageRank-style, resolved per domain: exact table → parent-domain walk (en.wikipedia.org→wikipedia.org) → public-sector/academic host shapes (.gov,gov.uk,gob.ve,.edu,ac.uk, …) → deterministic 2.0–6.0 hash band for everything else. It never varies with the query. An index entry may ship its own value ina, which always wins.- Since authority contributes at most 2.0 points, relevance dominates and authority only breaks ties — the intended 80/20 split.
Tokenisation. Query terms are stopword-filtered; single digits are kept (PlayStation 5, World War 2) while lone letters are dropped. Document text keeps every token, so stopwords still count toward f and |D|. Matching is whole-token, not substring.
Removed in this revision: the old hand-tuned constants (exact-title +700, title-prefix +380, per-field +55/+32/+24/+9, coverage bonus, and the fixed Wikipedia/official/web boosts of 2000/1800/930/280/360/260). Also fixed a latent STOPWORDS bug where .split(' ') bound only to the last string literal, so the set held 20 stray characters and no stopword was ever filtered.
Verified by re-deriving IDF, W, BM25 and FinalScore from the raw index for 150+ results across 6 queries: max drift 0.0. Corpus stats (avgdl = 12.7286, vocabulary = 79,765) also match an independent count. Warm search averages ~3 ms over 50,000 documents (median 2.6 ms, p95 21.1 ms across 600 timed gatewaySearchLocal() calls); one-time index preparation costs ~1.3 s and is cached in memory.
- First paint never awaits network; network failures cannot blank results.
MAX_INDEX_RESULTS=120,MAX_RESULTS=100,PER_PAGE=20; each search walks only the documents its query terms actually match in a prebuilt inverted index — ~2.6 ms warm median (measured over the bundled 50,000-entry index), plus a one-timedf/avgdl/authority precompute of ~1.3 s that is cached in memory.- To verify: search anything and read
result-stats, e.g.(0.04 seconds). Compare against Google's typical 0.3–0.6 s SERP time.
index/index.jsonformat:ttitle,uURL (tracking params stripped),dunique description,snormalised domain,aoptional pre-calculated authority (0.0–10.0; falls back to the domain table when absent).- Regeneration check:
python3 -c "import json; d=json.load(open('index/index.json')); print(len(d), len(set(x['d'] for x in d)))"→50000 50000. - Local keys written by the browser (all removable via site data):
gw-lang,gw-dark,gw-quickwiki(preferences);gw_v6_*(10-minute search cache, max 30 keys);gw-history+gw-history-optout(search history, max 200 entries). Nothing else is persisted, and none of it is ever transmitted.
- Dangerous domains, private-IP hostnames, non-http(s) URLs, and malware-keyword hits are filtered (
isSafeDomain/isSafeResult/_safeHref). - Tracking params (
utm_*,fbclid,gclid, …) and hashes stripped; no third-party analytics; favicon/image loads are the only external requests besides Wikipedia/Wikidata APIs.
Clean, minimal interface with instant local search.
Results for "Costa Coffee", featuring the Quick Wiki box, result stats, and favicons.