DarkNyx is a free, open-source dark-web search engine and threat-intelligence platform: a Tor-crawling search core, a public read-only API, a set of free OSINT/safety tools, and a single-page web frontend — all runnable on your own infrastructure.
This is the Community codebase. It is a genuine, complete, self-hostable product on its own — not a crippled demo. A separate, closed-source Pro tier (bulk AI summarization, an investigation copilot, risk-score narratives) is built as an add-on gateway that this codebase never needs and never imports.
- Search onion sites and pages crawled over Tor — full-text and semantic (embedding) search, 14 content categories (marketplace, forum, leak-dump, ransomware, carding, and more).
- Entity search — search by Bitcoin/Ethereum/Monero address, IP, CVE, API key, IBAN, PGP key, .onion link, and 15 other structured entity types extracted from crawled pages.
- Safety ratings — every domain gets a Verified / Unrated / Suspicious / Dangerous rating from automatic content classification, with a reveal-before-you-click gate on anything rated Suspicious or worse.
- Ransomware activity tracking — a live victim feed sourced from RansomLook, per-group "known mirrors" info (live up/down status, safety- reviewed mirror screenshots), and per-victim detail.
- Marketplace price trend tracking.
- Clone clusters — the biggest groups of pages whose opening text is
identical on many onion sites: a hosting placeholder, a copied scam or
phishing kit, a mirrored market. Each cluster shows how many pages and
sites carry it, a sample title, its categories and the safety mix of the
sites; search
clone:<id>to list every copy. Host addresses are never shown in the list. - Free OSINT safety tools — see below for what each one actually does.
- Watchlist alerts — a webhook fires the moment a keyword or regex is matched on a new ransomware victim, plus an immediate retro-hunt against the existing corpus.
- A local, on-demand AI page summarizer (llama.cpp + Qwen3.5-4B) — zero external API calls, zero per-query cost.
┌───────────┐
│ web/ │ static single-page frontend
└─────┬─────┘
│ HTTPS, /api/*
┌─────▼─────┐
│ api/ │ FastAPI public API
└─────┬─────┘
┌────────────┼─────────────┐
▼ ▼ ▼
┌─────────┐ ┌───────────┐ ┌─────────┐
│Postgres │ │OpenSearch │ │ Valkey │ rate limiting
└────┬────┘ └─────┬─────┘ └─────────┘
▲ ▲
│ writes │ indexes
┌────┴────────────┴──┐ ┌───────────────┐
│ crawler/ │───────▶│ Tor + Privoxy │ outbound .onion access
│ (Scrapy) │ └───────────────┘
└────────────────────┘
The crawler stores every page in Postgres and indexes it into OpenSearch as it
goes. The API reads both, and uses Valkey for per-IP rate limits. The optional
on-demand summarizer is a separate llama.cpp server that the API calls over HTTP
(SUMMARIZER_URL); it is not part of docker-compose.yml.
See docs/ARCHITECTURE.md for the full component breakdown, docs/API.md for the API reference, and docs/DEPLOYMENT.md for a from-scratch production deployment guide (systemd units, nginx, TLS via DNS-01).
A page goes through six stages between being discovered and showing up in a search result:
- Discovery. A crawled page's outbound links become discovery
candidates.
DiscoveryPipelinerecords each one against its source host and drops it immediately — a discovered-but-not-yet-crawled link has no meaning to any of the downstream stages, so it never reaches them. - Crawl. Scrapy fetches the page through the
tor-privoxy-0container — the crawler never touches the open internet directly for.onionfetches. - Enrichment. Each crawled page runs through
classification.py(14 content categories),pii.py(flags personal data without exposing it),entity_extract.py(Bitcoin/Ethereum/Monero addresses, IPs, CVEs, API keys, IBANs, PGP keys, and 15 other structured types),email_extract.py,price_extract.py(marketplace listings), andclone_detect.py(checks near-duplicate pages against known-legitimate mirrors — how the platform tells a real onion service apart from a phishing clone of one). - Safety rating.
safety_rating.pyrolls a domain's crawled pages up into one Verified / Unrated / Suspicious / Dangerous rating. Most categories are ranked by severity (extremism-violence and leak-dump sit above the rest), but two signals override that ranking outright regardless of category: a page linking to malware, or a near-duplicate of a known-legitimate page from a different domain (both mean the domain is dangerous no matter what else is on it). CSAM is checked before every other signal, including those overrides — a single CSAM-classified page makes the whole domain CSAM-confirmed unconditionally. A CSAM-confirmed domain isn't just rated Dangerous like everything else here;api/labels.pydrops it from search results, entity search, and domain lookups entirely. The URL checker tool (below) won't even live-fetch or explain why it's refusing to check such a host — a flat refusal, never a hint. - Storage. Structured data (domains, pages, entities, prices) goes to
Postgres. The same page content is embedded with
all-MiniLM-L6-v2(384 dimensions — the same modelclassification.pyalready loads for content classification, reused rather than loading a second model) and indexed into OpenSearch alongside the raw text, so one query serves both full-text and semantic (k-NN vector) search. - Serving. The
api/FastAPI app only reads the search index, and it has no crawling code, so it can be deployed and scaled independently of the crawler. Its writes to Postgres are limited to the small user and moderation tables (watchlists, site reports, domain-verification claims, digest subscriptions) and a moderator's safety-rating override.
Seven tools, all under /tools/*, all free, all usable without running your
own crawl — each is a thin API wrapper (or, for the Tor checker, pure
client-side JavaScript) around the same engines the platform already runs
internally.
- Breach & credential exposure checker — checks whether an email appears in PII the crawler has already flagged on real leak-dump pages. The email is hashed before checking and never stored or logged; a match tells you it exists and how many times it's been seen, never which domain or page it came from.
- Image metadata scrubber — strips EXIF and other embedded metadata from an uploaded image and returns a clean copy, re-encoded rather than merely stripped-in-place. The protective mirror of the same forensic work this platform does to find that kind of metadata in crawled images.
- URL/site safety checker — checks the platform's own crawled index first, so checking a known host never requires connecting to it live; only falls back to a live fetch for a host that hasn't been crawled yet. Also backs the fake-shop checker on the frontend — same engine, different framing.
- Phishing email analyzer — runs pasted email content through the same scam-classification model as the message checker below, and separately checks up to 3 embedded links against the URL checker. Doesn't verify SPF/DKIM/DMARC yet — that needs a live DNS lookup against the sender's domain, a distinct capability not yet built.
- Scam message classifier — one shared model behind both a general scam-message checker and a job/investment-scam checker; most scam-pattern detection is the same problem regardless of which framing a user picks.
- QR code checker — decodes an uploaded QR code and safety-checks whatever URL it points to through the same engine as the URL checker, before you ever open it. Built specifically against "quishing" (malicious QR codes) — a destination you can't eyeball before scanning.
- Tor Browser configuration checker — runs entirely client-side in your browser, no server round-trip, so nothing about your setup is ever sent anywhere. Checks real anti-fingerprinting hygiene: whether any plugins are reporting, whether more than one language is exposed, and whether your reported hardware concurrency is high enough to help fingerprint you. Not exhaustive — a quick read on your current setup, not a full audit.
By the end of this guide you will have your own copy of the search engine running on your computer, and you will be able to search it in your browser at http://127.0.0.1:8080. Most of the time is spent waiting for downloads. You do not need to know how the code works.
The engine is a handful of small programs that work together. Four of them run inside Docker (one command starts all four), and three are ordinary programs you start yourself.
Four services that run in Docker. You do not install these separately.
| Service | What it does, in plain words |
|---|---|
| Postgres | The database. It remembers every site and page the crawler has read, the details pulled out of them (Bitcoin addresses, IP addresses, prices) and each site's safety rating. |
| OpenSearch | The search index. It is what makes searching fast, and lets you search by words and by meaning. |
| Valkey | A small, very fast memory store (it works like Redis). The API uses it to count requests, so one visitor cannot flood the service. |
| Tor + Privoxy | The door to the dark web. Tor is the network that .onion addresses live on. Privoxy turns Tor's connection into an ordinary web proxy that the crawler can use. |
Three programs you start yourself.
| Program | What it does | Folder |
|---|---|---|
| Crawler | Visits .onion sites through Tor, reads each page, sorts it into a category, rates the site's safety, and saves the result in Postgres and OpenSearch. |
crawler/ |
| API | Answers searches. It only reads what the crawler has saved in Postgres and OpenSearch; it does not crawl anything. | api/ |
| Web page | The search page you see in your browser. It asks the API for everything it shows. | web/ |
So the flow is: the crawler fills the database and the search index, the API reads them, and the web page asks the API. Until the crawler has saved some pages there is nothing to search.
Some features are optional and you can ignore them for now: the AI page summarizer (needs a separate
language-model server), the weekly email digest, the /admin/* moderation endpoints, and crawling
ordinary (non-onion) websites.
| You need | Why | Check that you have it | Where to get it |
|---|---|---|---|
| Git | To download the code | git --version |
https://git-scm.com/downloads |
| Docker with Compose | To run the four services | docker compose version |
https://docs.docker.com/get-docker/ (Docker Desktop on Windows and macOS) |
| Python 3.11 or newer | To run the crawler, the API and the web page | python3 --version (on Windows: python --version) |
https://www.python.org/downloads/ |
| A terminal | To type the commands below | macOS and Linux have one. On Windows use Git Bash (installed with Git) or WSL. |
Your computer should have about 8 GB of memory and several GB of free disk space, and it needs an
internet connection. Linux and macOS users also need one small library for the QR-code checker:
sudo apt install libzbar0 on Debian or Ubuntu, or brew install zbar on macOS.
The Python packages (the libraries the programs are built from) are installed for you in steps 4
and 5. Each program gets its own private folder called a virtual environment (.venv), so nothing
is installed system-wide and you can delete it at any time. The main ones:
| Package | Used by | What for |
|---|---|---|
| Scrapy | Crawler | The crawling framework |
| FastAPI and Uvicorn | API | The web service and the server that runs it |
| psycopg | Crawler, API | Talking to Postgres |
| opensearch-py | Crawler, API | Talking to OpenSearch |
| redis | API | Talking to Valkey |
| sentence-transformers | Crawler, API | The small AI model that sorts pages into categories and powers search by meaning. It brings in PyTorch, which is the biggest download. |
| Pillow, pyzbar | Crawler, API | Reading images and QR codes |
The first time a program needs the AI model (all-MiniLM-L6-v2), it downloads it once from Hugging
Face and keeps it.
Type each command exactly as shown. Lines that start with # are explanations, not commands.
1. Get the code and create your settings file
git clone https://github.com/NexvisionLab/Darkweb-search-engine.git
cd Darkweb-search-engine
cp .env.example .env.env is a plain text file that holds your settings. Open it in any text editor and change
POSTGRES_PASSWORD to a password of your own. Leave the rest as it is: the defaults already point at
the services you are about to start. (ADMIN_TOKEN and the summarizer, email and surface-web settings
only matter for the optional features above.)
2. Start the four services
docker compose up -d postgres opensearch redis tor-privoxy-0
docker compose psThe first run downloads and builds the images, which takes a few minutes. Run docker compose ps
again until postgres says healthy. All four should show as running. On Linux, if opensearch
stops straight away, raise the memory-map limit it needs: sudo sysctl -w vm.max_map_count=262144.
3. Create the database tables
Postgres starts empty. This command fills it with the tables the engine needs, and it is safe to run twice:
set -a; . ./.env; set +a
docker exec -i darkweb-postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" < db/schema.sqlYou will see a list of CREATE TABLE lines. The first command copies your settings from .env into
the current terminal. The Python programs do not read .env themselves, so every terminal that
runs step 4, 5 or 6 must have had that line typed in it first. (PowerShell does not understand it; use
this instead: Get-Content .env | Where-Object { $_ -match '^\s*[^#\s].*=' } | ForEach-Object { $k, $v = $_ -split '=', 2; Set-Item "env:$k" $v.Trim('"') }.)
4. Run the crawler
cd crawler
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/scrapy crawl onion -a seeds_file=../onions_list/onions.txt -s DEPTH_LIMIT=1The first two commands create the private environment and install the packages (this is the slow
part, often several minutes). The last starts the crawl. On Windows the programs are in
.venv\Scripts\ instead of .venv/bin/.
The seed list has about 19,000 .onion addresses, and most of them are no longer online, so you will
see many timeouts and errors. That is normal. Let it run for a few minutes, then press Ctrl+C to
stop it. To see how many pages it saved, open a second terminal in the repository folder and run:
set -a; . ./.env; set +a
docker exec -i darkweb-postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "SELECT count(*) FROM pages;"Any number above zero means the crawler works. (The crawler also creates the search index the first time it starts, and indexes every page as it saves it.)
5. Run the API (a terminal in the repository folder, with the set -a line typed in it)
python3 -m venv api/.venv
api/.venv/bin/pip install -r api/requirements.txt
api/.venv/bin/uvicorn api.main:app --reload --port 8000Leave it running. Start it from the repository folder, not from inside api/, or it fails with
No module named 'api'. To check it, open http://127.0.0.1:8000/stats in a browser, or run
curl "http://127.0.0.1:8000/stats" in another terminal. You should get a short block of counts
(sites, pages, categories) back, not an error.
6. Open the web page (another terminal, in the repository folder)
python3 ops/dev_server.pyThen go to http://127.0.0.1:8080 and try a search. The page asks for its data at /api/.... In a
real deployment the nginx web server forwards that to the API, and ops/dev_server.py does the same
job on your computer. Use it instead of a plain python3 -m http.server, which has no /api, so
every search would fail. If your API runs on another port, add --api http://127.0.0.1:PORT.
- Stop a program (crawler, API or web page): click its terminal and press Ctrl+C.
- Stop the four services but keep your data:
docker compose stop. - Start again another day:
docker compose up -d postgres opensearch redis tor-privoxy-0, then steps 5 and 6 (and step 4 to crawl more). Each new terminal needs theset -a; . ./.env; set +aline again. Your pages are still there. You only repeat steps 1 to 3 on a new computer. - Erase everything and start over:
docker compose down -v. This deletes all stored pages and the search index. The next start is empty again.
| What you see | Likely cause and fix |
|---|---|
KeyError: 'POSTGRES_USER' |
The settings were not loaded into this terminal. Type the set -a; . ./.env; set +a line again (step 3). |
| A search gives a 500 error or no results | OpenSearch is not running, or the crawler has not saved a page yet, so the search index does not exist. Run docker compose ps, then run step 4 until it has saved some pages. |
connection refused on port 5432, 9200 or 6379 |
A service is not running. Run docker compose ps, then docker compose logs postgres (or opensearch, redis) to see why. |
No module named 'api' |
The API was started from inside api/. Start it from the repository folder. |
| The page loads but every request fails with 502 | The API is not running on port 8000, or dev_server.py was given a different --api. |
| The crawler finds nothing for a long time | Normal for the first minutes: Tor is slow and most seed addresses are dead. Check the tor service with docker compose logs tor-privoxy-0. |
To rebuild the search index from Postgres (after changing the classifier or the index fields), run
cd crawler && .venv/bin/python scripts/reindex_opensearch.py. It deletes the existing index and
rebuilds it, so searches are empty while it runs. A normal crawl does not need it.
Dark-web sites include scams, adult material and content that is illegal in many countries. The crawler reads the text of whatever it reaches. The engine rates sites, gates results, and treats child-abuse material specially (see "Safety rating" above), but nothing guarantees you will not store something disturbing. Run it on a machine and network you are comfortable using, and check the laws that apply to you.
| Path | What it is |
|---|---|
api/ |
FastAPI app — the public read-only API. No crawling code, deployable independently. |
crawler/ |
Scrapy crawler + enrichment pipeline (classification, entity extraction, PII masking, safety rating) + operational scripts (crawler/scripts/). |
web/ |
The frontend — one static HTML file, no build step. |
db/schema.sql |
Full Postgres schema, idempotent (CREATE TABLE IF NOT EXISTS / ALTER TABLE ... ADD COLUMN IF NOT EXISTS) — safe to re-run. |
tor-proxy/ |
Docker image for the Tor + Privoxy HTTP-proxy bridge the crawler routes through. |
ops/ |
systemd units, nginx config, and TLS/networking fixes for a real production deployment (see docs/DEPLOYMENT.md), plus dev_server.py for local development. |
docker-compose.yml |
Postgres, OpenSearch, Valkey, and the Tor proxy — the four backing services. |
onions_list/onions.txt |
A large (~19,000-address) unverified .onion URL list to bootstrap a first crawl. See onions_list/README.md — real, but not liveness-checked; the crawl pipeline sorts out what's actually reachable. |
All configuration is environment variables, loaded from .env. See
.env.example for the full list with explanations, and
docs/DEPLOYMENT.md for how each one is used in
production.
See CONTRIBUTING.md.
MIT — see LICENSE. DarkNyx is provided for security research and personal-safety purposes. It is not a substitute for legal, security, or law-enforcement advice.