Decentralized search aggregator. Nostr first, web when needed. No backend required.
Live: https://0xSearchstr.shakespeare.wtf Β· nsite mirror: https://0xsearchstr.shakespeare.to
Nostr: npub1z2k4ttglmwgc75c5e856tngnt05mw3hxams4lkr3muf354nh6xvskk2ew6
Sister app: 0xPresearchstr β same engine, shared index (see Federation)
User Search
β
βΌ
ββββββββββββββββ All providers run in parallel βββββββββββββββ
β β
β Nostr (NIP-50) SearXNG Wikipedia Hacker News Tor β
β β β β β β β
β Web Index DuckDuckGo Stack Overflow Community β
β Cache Index β β Index β
β βΌ βΌ βΌ βΌ β
β SearchResult[] from each provider β
β β
ββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ
β
Instant Answer? (calculator Β· npub Β· Wikipedia)
β
Merge + Deduplicate + Rank
β
βΌ
Display Results
β
Still nothing?
β
βΌ
Trending cached queries + Browser Fallback Links
Instead of building another centralized search engine, 0xSearchstr is a search aggregator with a plugin-based provider architecture:
- Every source is a provider β each returns a universal
SearchResult[] - All providers run in parallel β results stream in as each completes
- Nostr scores highest β decentralized results are prioritized
- Auto-indexing β every search publishes discovered pages back to Nostr as document observations (never the query)
- Community-curated β any Nostr user can submit links to the shared index
- Never leaves you empty β trending cached queries + fallback links to privacy-respecting search engines
Everything runs in the browser. No backend, no crawler, no tracking.
Every provider is classified by who can see your query, and a traffic-light indicator sits next to the search bar at all times:
| Light | Tier | Providers | Who sees the query |
|---|---|---|---|
| π’ | Nostr | Cache Index, Nostr, Community | Relay operators only (query + IP, no account linked) |
| π‘ | Direct | Wikipedia, Hacker News, Stack Overflow | The API operator (query + IP in standard logs) |
| π΄ | Proxied | SearXNG, DuckDuckGo, Ahmia | A CORS proxy and the destination service |
- Privacy Mode (Settings) β one switch to run Nostr-tier providers only. Zero third-party exposure, at the cost of fewer results.
- The full, honest threat model lives on the About page. "No backend" β "no one sees anything" β we document exactly who does.
Direct answers above the result list, no waiting:
- Calculator β
2^10,(3+4)*5,15% of 80β safe recursive-descent parser, computed locally, never sent anywhere - Nostr profiles β paste a bare
npub1β¦/nprofile1β¦β rich profile card (avatar, banner, NIP-05, bio) - Wikipedia summaries β strong title matches render the article's first paragraph + thumbnail (auto-disabled in Privacy Mode)
The index isn't just a bot cache β any logged-in Nostr user can submit links (kind 30078, signed with their own key) via the Submit button. Submissions support https://, magnet:, ipfs://, and .onion links with automatic content-type detection (Torrent, IPFS, Video, Audio, PDF badges).
- URL allowlist blocks
javascript:/data:at parse time; onion submissions render behind a Tor warning interstitial - Independent project, credit where due: 0xSearchstr was built on its own, but the idea of letting every user curate the index β not just bots and crawlers β was adopted after we discovered Nostra Search, a project exploring the same territory. Thanks for the spark. Our implementation is our own, with an improved schema (unique per-URL d-tags fix their one-entry-per-author limitation)
- Nostra read-interop: we also read Nostra Search's own index (
nostra:index), including their AES-GCM obfuscated payloads β their community's curation shows up here, attributed as "Nostra Index"
The Search Index Protocol (kind 39697, canonical spec: SIP-01/SIP-01) is shared with 0xPresearchstr, the UNCAGED Index Relay, the Crawlstr + indexstr crawler network, and open to any fork or third-party engine. Every browser is its own indexer β there is no central signing key:
- Per-device identity β each browser generates its own indexer keypair on first use (Settings β Indexing). Pseudonymous, replaceable, exportable.
- Independent observations β when indexer A and indexer B both see
example.com, they publish separate events with the samedtag. Search nodes count distinct indexers per document β "7 independent indexers saw this page." - Any relay works β plain Nostr filters (
#d,#t) on ordinary relays; NIP-50 is an optional acceleration, never a requirement.
The legacy query cache (kind 30078, 0xsearchstr:cache:*) remains federated with 0xPresearchstr during migration β readers trust both bot pubkeys, and old clients keep a warm cache. Full legacy spec in NIP.md.
/explore turns the index into discoverable content: recently indexed pages (with independent-indexer counts), trending terms, staked keywords, and aggregate stats. Every search becomes content.
Stake your identity on a keyword instead of tokens: sign an addressable kind 30078 event (0xsearchstr:stake:<keyword>) binding a keyword to your link. Anyone searching that keyword β on any compatible client β sees your link as the top "Community Stake" placement. One stake per keyword per npub; re-staking replaces it. No tokens, no auction.
Every result card carries π/π votes and a report flag:
- Votes are NIP-25 reactions (kind 7;
etag for events,rtag with the SIP-01-normalized URL for pages). Anonymous by default β signed by this device's indexing identity, never your npub. Settings β Indexing has an opt-in "vote with my npub" toggle. - Reports are NIP-56 kind 1984 events (
0xsearchstr.abusenamespace), landing in the team's/admininbox.
Trending searches with no plaintext queries anywhere: each device publishes only sha256(normalized query) under its indexing identity (0xsearchstr:term:*). A term's plaintext is revealed by the network only once 3+ independent devices searched it β the reveal is self-verifying (readers re-hash and compare). Rare or sensitive queries (NIP-19 ids, NIP-05 addresses, URLs, math) are never signaled at all.
A hidden, role-gated dashboard (not linked in nav β team members see "Admin console" in their account menu when logged in with a team key):
- Stats β indexed pages, stakes, open reports, relay pool sizes
- Reports β the NIP-56 abuse inbox, one-click "hide from results"
- Moderation β team-signed NIP-32 labels (kind 1985,
0xsearchstr.moderation) hide URLs/event ids from every user's results; un-hiding publishes a NIP-09 deletion. Clients trust labels from the owner + role-list pubkeys only - Roles (owner) β add/remove admins & moderators by npub (owner-signed
0xsearchstr:admin-roles/0xsearchstr:mod-rolesaddressable events) - Filter test β check whether a URL or event id is currently filtered
An AI answer layer sits on top of the federation: the top results become a numbered evidence pack, the model answers only from it, with clickable [n] citations. Ephemeral β never indexed into SIP-01. Tiers: your own key (BYOK β PPQ.ai, OpenRouter, Ollama, any OpenAI-compatible endpoint) β a built-in free tier (shared rate-limited PPQ key, public by design). Privacy boundaries hold: NIP-19/05, URLs, and math keep their deterministic paths; Nostr results are excluded from evidence unless you opt in.
- OpenSearch β add 0xSearchstr as a browser search engine (
/opensearch.xml) - Keyboard shortcuts β
Ctrl+K/Cmd+Kor/focuses the search bar from anywhere - Shareable searches β
?q=query&source=webURL params - Smart snippets β Nostr notes show the relevant window around your query terms (Google-style), not just the note head; hashtag/link-stuffed spam is filtered out
- Graceful degradation β a failing provider shows a muted "skipped" state instead of a red error when others delivered
The killer feature: every search grows the index.
When you search, useful web results are published to Nostr as document observations β one lightweight, addressable event per URL (kind 39697, SIP-01), signed by this browser's own dedicated indexing identity. No account, no login, no central signing key β and your search query is never published, only the pages' public metadata.
Search "best monero wallet"
β
βββ Read the shared web index (kind 39697) + legacy cache (kind 30078)
β βββ HIT? β instant results from Nostr
β
βββ Run all providers in parallel
β βββ Merge + deduplicate + rank
β
βββ Index the useful pages (auto-index)
βββ kind 39697 per URL, signed by your device's indexer key
βββ every compatible client can now find those pages
Indexing identity: each browser generates its own keypair on first use (Settings β Indexing). It is pseudonymous, replaceable, exportable, and never linked to your personal Nostr identity. Key separation is guaranteed; network anonymity is not (relays see IP/timing β be honest about that).
The more people use any compatible client, the smarter every client gets. No crawler. No database. Just Nostr.
Migration note: the legacy queryβresults cache (kind 30078) is frozen legacy data (SIP-01 Β§17) β 0xSearchstr still reads it for backward compatibility with 0xPresearchstr and older clients, but no longer writes it. The only signer in this codebase is the per-device indexing identity.
git clone https://github.com/NostrDanish/0xSearchstr.git
cd 0xSearchstr
npm install
npm run devOpen http://localhost:8080 and search.
src/lib/providers/
βββ types.ts β SearchResult, SearchProvider interface (privacy tiers!)
βββ web-index.ts β Shared web index β SIP-01 kind 39697 observations (reads first)
βββ cached-index.ts β Legacy federated query cache (kind 30078, read for compatibility)
βββ nostr.ts β NIP-50 relay search
βββ stakes.ts β Keyword stakes (Presearch-style top placements)
βββ community.ts β Community-curated index (+ Nostra Search interop + NIP-B0)
βββ git.ts β NIP-34 repos/issues/PRs (ngit/GRASP pool, read-only)
βββ wiki.ts β NIP-54 wiki articles (wikifreedia pool, read-only)
βββ brave.ts β Brave Search API (BYOK, off by default)
βββ searxng.ts β SearXNG meta-search with failover
βββ duckduckgo.ts β DuckDuckGo HTML scraper
βββ wikipedia.ts β MediaWiki API
βββ hacker-news.ts β Algolia HN Search API
βββ stackoverflow.ts β StackExchange API
βββ tor.ts β Ahmia.fi .onion search
βββ registry.ts β Provider catalog (+ per-engine enable/disable)
βββ index.ts β Barrel export
src/lib/
βββ webIndex.ts β SIP-01 protocol: URL normalization, build/parse/verify
βββ indexerIdentity.ts β Per-device anonymous indexer keypair
βββ keywordStakes.ts β Stake schema (0xsearchstr:stake:*)
βββ termSignals.ts β k-anonymity trending (0xsearchstr:term:*)
βββ votes.ts β NIP-25 result votes (device identity by default)
βββ reports.ts β NIP-56 abuse reports (0xsearchstr.abuse)
βββ moderation.ts β NIP-32 team labels (0xsearchstr.moderation) + roles
βββ queryClassify.ts β Query understanding (URL/nip19/nip05/math detection)
βββ resultRank.ts β Coverage-ranked result merging
βββ ai/ β Optional AI answer layer (BYOK + built-in free tier)
- Create
src/lib/providers/my-provider.tsimplementingSearchProvider - Import it in
registry.tsand add toALL_PROVIDERS - Done β the orchestrator picks it up automatically
interface SearchProvider {
id: string;
name: string;
source: SearchSource;
/** Extra tabs this provider also runs under (e.g. community runs under 'tor' too) */
additionalSources?: SearchSource[];
/** Privacy tier β who can observe the query. Drives the traffic-light + Privacy Mode. */
privacy: 'nostr' | 'direct' | 'proxied';
/** Honest one-liner about who sees the query (shown in the privacy popover). */
privacyNote: string;
search(options: SearchOptions): Promise<ProviderSearchResponse>;
}| Provider | Source | API | Privacy | Notes |
|---|---|---|---|---|
| Web Index | Nostr kind 39697 | WebSocket | π’ Nostr | Shared document observations from all indexers (SIP-01), integrity-verified (dβu, xβcontent) |
| Cache Index | Federated Nostr index | WebSocket | π’ Nostr | Legacy query cache, read-only (frozen) β off by default |
| Nostr | NIP-50 relays | WebSocket | π’ Nostr | UNCAGED + community default relays, fully user-editable |
| Keyword Stakes | Nostr kind 30078 | WebSocket | π’ Nostr | Community-staked keyword placements |
| Community | Nostr kind 30078 + 39701 | WebSocket | π’ Nostr | User-submitted links + NIP-B0 bookmarks + Nostra Search index |
| Git Repos | ngit/GRASP relays (NIP-34) | WebSocket, read-only | π’ Nostr | Repos, issues, PRs & patches β pool editable in Settings |
| Nostr Wiki | Wiki relays (NIP-54) | WebSocket, read-only | π’ Nostr | Wikifreedia corpus β pool editable in Settings |
| SearXNG | Dynamic instance pool | CORS proxy | π΄ Proxied | DDG, Brave, Wikipedia, and dozens more |
| DuckDuckGo | HTML scraper | CORS proxy | π΄ Proxied | Direct DDG fallback when SearXNG is slow |
| Brave | Brave Search API | CORS proxy | π΄ Proxied | Off by default β BYOK in Settings β Engines |
| Wikipedia | MediaWiki API | Direct (CORS) | π‘ Direct | Off by default β enable in Settings β Engines |
| Hacker News | Algolia API | Direct (CORS) | π‘ Direct | Stories with points/comments |
| Stack Overflow | StackExchange API | Direct (CORS) | π‘ Direct | Off by default β enable in Settings β Engines |
| Tor (Ahmia) | HTML scraping | CORS proxy | π΄ Proxied | Off by default β policy-compliant .onion search |
Every engine can be toggled off in Settings β Search Engines β off engines never run and never see your query. The tab bar is modular too: Settings β Search Tabs picks visible tabs, order, and the default landing tab.
Instead of a hardcoded instance list, the SearXNG provider uses a self-healing dynamic pool (inspired by searxist):
βββ Tier 1: Custom βββββββ Your self-hosted / trusted instances (always first)
βββ Tier 2: Discovered βββ€ Live from searx.space, privacy-filtered:
β β β’ no analytics β’ clearnet β’ β₯80% search success
βββ Tier 3: Seeds ββββββββ Hardcoded bootstrap fallback
- Auto-discovery β the pool refreshes from searx.space every 24h, client-side
- Quality-aware health tracking β per-instance success/failure/latency and result-count stats (EMA) in localStorage; failing or thin instances sink, fast and complete ones rise
- Self-hosting friendly β add your own instance in Settings and it runs first on every search
- Zero backend β discovery, health, and ranking all happen in the browser
Two layers, both manageable at /settings:
- Your Relays (NIP-65) β your personal relay list with read/write flags; publishes kind 10002 when logged in. Defaults to the 0xSearchstr app relays for new users.
- Search Relays (index pool) β the pool that powers Nostr search, the shared web index, and index publishing. Defaults: the UNCAGED index relay (
relay-na1.metanomalist.com), community NIP-50 relays (ditto, jskitty.cat, nos.today, nostr.band, noswhere), Primal + Hifish for replication, and a Tor index relay (.onion). Every default is removable β removed defaults can be restored, and customs (including your own self-hosted NIP-50 relay) can be added. A built-in latency tester checks reachability and round-trip times. The.onionrelay is only contacted when the app itself runs on a.onionorigin β on clearnet it's skipped silently (a connection could never succeed, and on devices with a local Tor resolver the attempt trips Chrome/Brave's local-network permission prompt).
All providers run in parallel. The UI shows live status:
β Nostr (124ms) β Wikipedia (230ms) β³ SearXNG... β³ HN...
Results appear as each provider finishes β no waiting for the slowest one.
The tab bar is fully modular β Settings β Search Tabs lets every user pick which tabs show, reorder them, and star the tab a fresh visit starts on. Deep links (/?source=tor&q=β¦) keep working even for hidden tabs.
| Tab | Sources |
|---|---|
| Web | Web Index (SIP-01) + Stakes + Community + SearXNG + DuckDuckGo + Brave (BYOK) |
| Index | The community index only β SIP-01 observations + legacy cache |
| All | All providers merged + ranked (stakes on top) |
| Nostr | Profiles, notes, articles, Wikifreedia, files, torrents, code snippets |
| News | Hacker News stories |
| Wiki | Nostr wiki articles (NIP-54 pool); Wikipedia engine off until enabled |
| Code | Git repos/issues/PRs (NIP-34 via ngit/GRASP) + NIP-C0 snippets; Stack Overflow off until enabled |
| Tor | .onion hidden services via Ahmia + index-observed onion pages |
| I2P | Eepsite directory links + index-observed eepsites |
The backend/ directory contains a full self-hosted stack for when you want to run your own search infrastructure:
| Service | Description |
|---|---|
| Meilisearch | Full-text search index engine |
| Nostr Crawler | NIP-01 subscriber indexing kinds 0/1/30023/1063 |
| Clearnet Crawler | Polite web crawler (robots.txt, rate-limited) |
| Tor/I2P Crawler | Hidden service crawler with content policy enforcement |
| NIP-50 Relay | Search relay proxy bridging Meilisearch to Nostr |
| Abuse API | REST search API + abuse report management |
cp .env.example .env # Edit MEILI_API_KEY + ABUSE_ADMIN_TOKEN
docker compose up -dSee the backend README and Content Policy for details.
The interoperable web-document index is specified by SIP-01 β canonical spec at github.com/SIP-01/SIP-01 (kind 39697) β designed so an independent developer can implement a compatible indexer, search node, or search engine without reading this codebase. 0xSearchstr's implementation is byte-compatible with the spec's Β§13 test vectors (see docs/SEARCH_INDEX_PROTOCOL.md).
App-specific legacy schemas β the federated query cache, community submissions, trusted indexer list, and Nostra Search interop β are documented in NIP.md.
The self-hosted backend enforces content policy modeled on Ahmia. Hard-blocked categories: CSAM, human trafficking, weapons sales, drug marketplace listings. See the Policy page for details.
- React 19 + TypeScript + Vite
- TailwindCSS 4 + shadcn/ui
- Nostrify β NIP-50 relay search
- SearXNG β meta-search fallback
- Wikipedia β MediaWiki API
- Hacker News β Algolia search
- TanStack Query β data fetching + caching
MIT
Vibed with Shakespeare