darkwatch 0.5.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. darkwatch-0.5.1/.dockerignore +18 -0
  2. darkwatch-0.5.1/.env.example +24 -0
  3. darkwatch-0.5.1/.github/workflows/docker.yml +45 -0
  4. darkwatch-0.5.1/.github/workflows/pages.yml +36 -0
  5. darkwatch-0.5.1/.github/workflows/publish-vscode.yml +46 -0
  6. darkwatch-0.5.1/.github/workflows/release.yml +67 -0
  7. darkwatch-0.5.1/.gitignore +9 -0
  8. darkwatch-0.5.1/.vscode/settings.json +5 -0
  9. darkwatch-0.5.1/Dockerfile +31 -0
  10. darkwatch-0.5.1/LICENSE +21 -0
  11. darkwatch-0.5.1/PKG-INFO +351 -0
  12. darkwatch-0.5.1/README.md +330 -0
  13. darkwatch-0.5.1/docs/ARCHITECTURE.md +208 -0
  14. darkwatch-0.5.1/docs/PROJECT_STATE.md +432 -0
  15. darkwatch-0.5.1/docs/ROADMAP.md +73 -0
  16. darkwatch-0.5.1/docs/release-notes/v0.5.0.md +31 -0
  17. darkwatch-0.5.1/docs/release-notes/v0.5.1.md +11 -0
  18. darkwatch-0.5.1/install.ps1 +78 -0
  19. darkwatch-0.5.1/install.sh +44 -0
  20. darkwatch-0.5.1/pyproject.toml +42 -0
  21. darkwatch-0.5.1/scripts/build_telegram_channels.py +49 -0
  22. darkwatch-0.5.1/site/.nojekyll +0 -0
  23. darkwatch-0.5.1/site/favicon.svg +5 -0
  24. darkwatch-0.5.1/site/index.html +300 -0
  25. darkwatch-0.5.1/src/darkwatch/__init__.py +3 -0
  26. darkwatch-0.5.1/src/darkwatch/__main__.py +5 -0
  27. darkwatch-0.5.1/src/darkwatch/cache.py +100 -0
  28. darkwatch-0.5.1/src/darkwatch/cli.py +896 -0
  29. darkwatch-0.5.1/src/darkwatch/config.py +558 -0
  30. darkwatch-0.5.1/src/darkwatch/data/__init__.py +0 -0
  31. darkwatch-0.5.1/src/darkwatch/data/telegram_channels.json +5400 -0
  32. darkwatch-0.5.1/src/darkwatch/desktop.py +125 -0
  33. darkwatch-0.5.1/src/darkwatch/fetch.py +196 -0
  34. darkwatch-0.5.1/src/darkwatch/matcher.py +347 -0
  35. darkwatch-0.5.1/src/darkwatch/notify.py +246 -0
  36. darkwatch-0.5.1/src/darkwatch/report.py +425 -0
  37. darkwatch-0.5.1/src/darkwatch/scanner.py +222 -0
  38. darkwatch-0.5.1/src/darkwatch/schedule.py +232 -0
  39. darkwatch-0.5.1/src/darkwatch/search.py +359 -0
  40. darkwatch-0.5.1/src/darkwatch/sources/__init__.py +161 -0
  41. darkwatch-0.5.1/src/darkwatch/sources/ahmia.py +351 -0
  42. darkwatch-0.5.1/src/darkwatch/sources/hibp.py +140 -0
  43. darkwatch-0.5.1/src/darkwatch/sources/leakcheck.py +110 -0
  44. darkwatch-0.5.1/src/darkwatch/sources/leaksites.py +196 -0
  45. darkwatch-0.5.1/src/darkwatch/sources/recentattacks.py +88 -0
  46. darkwatch-0.5.1/src/darkwatch/sources/seeds.py +92 -0
  47. darkwatch-0.5.1/src/darkwatch/sources/sites.py +225 -0
  48. darkwatch-0.5.1/src/darkwatch/sources/stealers.py +151 -0
  49. darkwatch-0.5.1/src/darkwatch/sources/telegram.py +145 -0
  50. darkwatch-0.5.1/src/darkwatch/sources/xposedornot.py +195 -0
  51. darkwatch-0.5.1/src/darkwatch/storage.py +382 -0
  52. darkwatch-0.5.1/src/darkwatch/tor.py +227 -0
  53. darkwatch-0.5.1/src/darkwatch/web/__init__.py +7 -0
  54. darkwatch-0.5.1/src/darkwatch/web/jobs.py +93 -0
  55. darkwatch-0.5.1/src/darkwatch/web/server.py +375 -0
  56. darkwatch-0.5.1/src/darkwatch/web/static/app.css +323 -0
  57. darkwatch-0.5.1/src/darkwatch/web/static/app.js +730 -0
  58. darkwatch-0.5.1/src/darkwatch/web/static/favicon.svg +6 -0
  59. darkwatch-0.5.1/src/darkwatch/web/static/index.html +176 -0
  60. darkwatch-0.5.1/src/darkwatch/web/static/shader.js +205 -0
  61. darkwatch-0.5.1/src/darkwatch/web/static/vendor/DrawSVGPlugin.min.js +11 -0
  62. darkwatch-0.5.1/src/darkwatch/web/static/vendor/Flip.min.js +11 -0
  63. darkwatch-0.5.1/src/darkwatch/web/static/vendor/ScrambleTextPlugin.min.js +11 -0
  64. darkwatch-0.5.1/src/darkwatch/web/static/vendor/ScrollTrigger.min.js +11 -0
  65. darkwatch-0.5.1/src/darkwatch/web/static/vendor/gsap.min.js +11 -0
  66. darkwatch-0.5.1/tests/conftest.py +107 -0
  67. darkwatch-0.5.1/tests/test_cli.py +408 -0
  68. darkwatch-0.5.1/tests/test_config.py +239 -0
  69. darkwatch-0.5.1/tests/test_desktop.py +40 -0
  70. darkwatch-0.5.1/tests/test_fetch.py +125 -0
  71. darkwatch-0.5.1/tests/test_matcher.py +174 -0
  72. darkwatch-0.5.1/tests/test_notify_and_schedule.py +190 -0
  73. darkwatch-0.5.1/tests/test_recall.py +190 -0
  74. darkwatch-0.5.1/tests/test_search.py +189 -0
  75. darkwatch-0.5.1/tests/test_sources.py +1037 -0
  76. darkwatch-0.5.1/tests/test_storage_and_report.py +270 -0
  77. darkwatch-0.5.1/tests/test_tor_and_cache.py +171 -0
  78. darkwatch-0.5.1/tests/test_web.py +295 -0
  79. darkwatch-0.5.1/uv.lock +701 -0
  80. darkwatch-0.5.1/vscode-extension/.eslintrc.json +11 -0
  81. darkwatch-0.5.1/vscode-extension/.gitignore +4 -0
  82. darkwatch-0.5.1/vscode-extension/.vscodeignore +12 -0
  83. darkwatch-0.5.1/vscode-extension/CHANGELOG.md +36 -0
  84. darkwatch-0.5.1/vscode-extension/LICENSE +21 -0
  85. darkwatch-0.5.1/vscode-extension/PUBLISHING.md +81 -0
  86. darkwatch-0.5.1/vscode-extension/README.md +47 -0
  87. darkwatch-0.5.1/vscode-extension/media/icon.png +0 -0
  88. darkwatch-0.5.1/vscode-extension/media/sidebar.svg +5 -0
  89. darkwatch-0.5.1/vscode-extension/package-lock.json +5956 -0
  90. darkwatch-0.5.1/vscode-extension/package.json +374 -0
  91. darkwatch-0.5.1/vscode-extension/src/extension.ts +862 -0
  92. darkwatch-0.5.1/vscode-extension/src/test/runTest.ts +28 -0
  93. darkwatch-0.5.1/vscode-extension/src/test/suite.ts +102 -0
  94. darkwatch-0.5.1/vscode-extension/tsconfig.json +18 -0
@@ -0,0 +1,18 @@
1
+ .git
2
+ .github
3
+ .venv
4
+ node_modules
5
+ vscode-extension
6
+ site
7
+ docs
8
+ tests
9
+ data
10
+ reports
11
+ watchlist.yaml
12
+ .env
13
+ *.vsix
14
+ __pycache__
15
+ *.pyc
16
+ .ruff_cache
17
+ .pytest_cache
18
+ dist
@@ -0,0 +1,24 @@
1
+ # Darkwatch secrets. Copy to `.env` next to the watchlist and fill in what you use.
2
+ # Never commit `.env`. Every value here is optional; Darkwatch runs without any of them.
3
+
4
+ # Have I Been Pwned API key: adds per-email breach and paste lookups. https://haveibeenpwned.com/API/Key
5
+ DARKWATCH_HIBP_KEY=
6
+
7
+ # ntfy phone push: a long, random topic name (anyone who knows it can read the pushes).
8
+ # Subscribe to the same topic in the ntfy app. https://ntfy.sh
9
+ DARKWATCH_NTFY_TOPIC=
10
+
11
+ # Webhook for a Discord / Slack style channel (receives the full summary).
12
+ DARKWATCH_WEBHOOK_URL=
13
+
14
+ # Email alerts (receives the full summary). Port 465 uses implicit TLS; otherwise STARTTLS.
15
+ DARKWATCH_SMTP_HOST=
16
+ DARKWATCH_SMTP_PORT=587
17
+ DARKWATCH_SMTP_STARTTLS=true
18
+ DARKWATCH_SMTP_USER=
19
+ DARKWATCH_SMTP_PASSWORD=
20
+ DARKWATCH_SMTP_FROM=
21
+ DARKWATCH_SMTP_TO=
22
+
23
+ # Path to tor.exe, if it is not auto-detected (PATH, tools/tor-*/tor/tor.exe, or Tor Browser).
24
+ DARKWATCH_TOR_EXE=
@@ -0,0 +1,45 @@
1
+ name: Publish Docker image
2
+
3
+ # Builds the CLI image and pushes it to the GitHub Container Registry on a version tag:
4
+ # ghcr.io/daemon-vi/darkwatch:<version> and :latest
5
+ # Uses the built-in GITHUB_TOKEN — no extra secret. Run by hand for a dry-run build (no push).
6
+
7
+ on:
8
+ push:
9
+ tags:
10
+ - "v[0-9]*"
11
+ workflow_dispatch:
12
+
13
+ permissions:
14
+ contents: read
15
+ packages: write
16
+
17
+ jobs:
18
+ image:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ - uses: docker/setup-buildx-action@v3
23
+ - name: Log in to GHCR
24
+ if: ${{ github.event_name != 'workflow_dispatch' }}
25
+ uses: docker/login-action@v3
26
+ with:
27
+ registry: ghcr.io
28
+ username: ${{ github.actor }}
29
+ password: ${{ secrets.GITHUB_TOKEN }}
30
+ - name: Tags
31
+ id: meta
32
+ uses: docker/metadata-action@v5
33
+ with:
34
+ images: ghcr.io/daemon-vi/darkwatch
35
+ tags: |
36
+ type=semver,pattern={{version}}
37
+ type=raw,value=latest,enable=${{ github.event_name != 'workflow_dispatch' }}
38
+ - uses: docker/build-push-action@v6
39
+ with:
40
+ context: .
41
+ push: ${{ github.event_name != 'workflow_dispatch' }}
42
+ tags: ${{ steps.meta.outputs.tags }}
43
+ labels: ${{ steps.meta.outputs.labels }}
44
+ cache-from: type=gha
45
+ cache-to: type=gha,mode=max
@@ -0,0 +1,36 @@
1
+ name: Deploy website
2
+
3
+ # Publishes site/ to GitHub Pages (https://daemon-vi.github.io/darkwatch/).
4
+ # Runs on a push to main that touches the site, or by hand from the Actions tab.
5
+
6
+ on:
7
+ push:
8
+ branches: [main]
9
+ paths:
10
+ - "site/**"
11
+ - ".github/workflows/pages.yml"
12
+ workflow_dispatch:
13
+
14
+ permissions:
15
+ contents: read
16
+ pages: write
17
+ id-token: write
18
+
19
+ concurrency:
20
+ group: pages
21
+ cancel-in-progress: true
22
+
23
+ jobs:
24
+ deploy:
25
+ runs-on: ubuntu-latest
26
+ environment:
27
+ name: github-pages
28
+ url: ${{ steps.deployment.outputs.page_url }}
29
+ steps:
30
+ - uses: actions/checkout@v4
31
+ - uses: actions/configure-pages@v5
32
+ - uses: actions/upload-pages-artifact@v3
33
+ with:
34
+ path: site
35
+ - id: deployment
36
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,46 @@
1
+ name: Publish VS Code extension
2
+
3
+ # Publishes the extension in vscode-extension/ to the VS Code Marketplace and Open VSX.
4
+ # Trigger by pushing a tag like `vscode-v0.1.0`, or run it by hand from the Actions tab.
5
+ #
6
+ # Set two repository secrets first (Settings -> Secrets and variables -> Actions):
7
+ # VSCE_PAT - a Marketplace personal access token for the `daemon-vi` publisher
8
+ # OVSX_PAT - an Open VSX access token for the same namespace
9
+ # Each publish step is skipped when its token is absent, so a run with no secrets just
10
+ # packages and uploads the .vsix as a build artifact.
11
+
12
+ on:
13
+ push:
14
+ tags:
15
+ - "vscode-v*"
16
+ workflow_dispatch:
17
+
18
+ jobs:
19
+ publish:
20
+ runs-on: ubuntu-latest
21
+ env:
22
+ VSCE_PAT: ${{ secrets.VSCE_PAT }}
23
+ OVSX_PAT: ${{ secrets.OVSX_PAT }}
24
+ defaults:
25
+ run:
26
+ working-directory: vscode-extension
27
+ steps:
28
+ - uses: actions/checkout@v4
29
+ - uses: actions/setup-node@v4
30
+ with:
31
+ node-version: "20"
32
+ - run: npm ci
33
+ - run: npm run compile
34
+ - run: npm run lint
35
+ - run: npx vsce package
36
+ - name: Upload the .vsix as a build artifact
37
+ uses: actions/upload-artifact@v4
38
+ with:
39
+ name: darkwatch-vsix
40
+ path: vscode-extension/*.vsix
41
+ - name: Publish to the VS Code Marketplace
42
+ if: ${{ env.VSCE_PAT != '' }}
43
+ run: npx vsce publish --pat "$VSCE_PAT"
44
+ - name: Publish to Open VSX
45
+ if: ${{ env.OVSX_PAT != '' }}
46
+ run: npx ovsx publish -p "$OVSX_PAT"
@@ -0,0 +1,67 @@
1
+ name: Release
2
+
3
+ # On a version tag like `v0.5.0`:
4
+ # - `release` builds and tests, then publishes a GitHub Release carrying the wheel, sdist, the
5
+ # VS Code extension .vsix and the install scripts (install.ps1 / install.sh install from it).
6
+ # - `pypi` publishes the wheel and sdist to PyPI via trusted publishing (no token). This needs a
7
+ # one-time PyPI trusted publisher for project `darkwatch` -> Daemon-VI/darkwatch, workflow
8
+ # `release.yml`. Until that exists the step fails safely and the GitHub Release still ships.
9
+ # The `vscode-v*` tags are handled by publish-vscode.yml.
10
+
11
+ on:
12
+ push:
13
+ tags:
14
+ - "v[0-9]*"
15
+
16
+ jobs:
17
+ release:
18
+ runs-on: windows-latest
19
+ permissions:
20
+ contents: write
21
+ defaults:
22
+ run:
23
+ shell: bash
24
+ steps:
25
+ - uses: actions/checkout@v4
26
+ - uses: astral-sh/setup-uv@v5
27
+ - uses: actions/setup-node@v4
28
+ with:
29
+ node-version: "20"
30
+
31
+ - name: Test the CLI
32
+ run: |
33
+ uv sync --all-extras
34
+ uv run ruff check src tests scripts
35
+ uv run pytest -q
36
+ - name: Check the tag matches the package version
37
+ run: |
38
+ version="$(uv run python -c 'import darkwatch; print(darkwatch.__version__)')"
39
+ test "v$version" = "$GITHUB_REF_NAME" || { echo "tag $GITHUB_REF_NAME != version $version"; exit 1; }
40
+ - name: Build the wheel and sdist
41
+ run: uv build
42
+
43
+ - name: Build the VS Code extension
44
+ working-directory: vscode-extension
45
+ run: |
46
+ npm ci
47
+ npm run compile
48
+ npm run lint
49
+ npx vsce package
50
+
51
+ - name: Publish the GitHub Release
52
+ env:
53
+ GH_TOKEN: ${{ github.token }}
54
+ run: |
55
+ gh release create "$GITHUB_REF_NAME" dist/* vscode-extension/*.vsix install.ps1 install.sh \
56
+ --title "Darkwatch $GITHUB_REF_NAME" --notes-file docs/release-notes/$GITHUB_REF_NAME.md
57
+
58
+ pypi:
59
+ runs-on: ubuntu-latest
60
+ permissions:
61
+ id-token: write # trusted publishing to PyPI, no stored token
62
+ steps:
63
+ - uses: actions/checkout@v4
64
+ - uses: astral-sh/setup-uv@v5
65
+ - run: uv build
66
+ - name: Publish to PyPI (trusted publishing)
67
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,9 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ .env
5
+ /data/
6
+ /reports/
7
+ /watchlist.yaml
8
+ .pytest_cache/
9
+ .ruff_cache/
@@ -0,0 +1,5 @@
1
+ {
2
+ "// darkwatch": "Run the CLI from this checkout via uv, so the extension works when this folder is open.",
3
+ "darkwatch.command": ["uv", "run", "darkwatch"],
4
+ "darkwatch.watchlist": "watchlist.yaml"
5
+ }
@@ -0,0 +1,31 @@
1
+ # Darkwatch as a container image: the CLI with Tor already inside, for running scans anywhere.
2
+ #
3
+ # docker run --rm ghcr.io/daemon-vi/darkwatch --help
4
+ # docker run --rm -v "$PWD/dw:/work" ghcr.io/daemon-vi/darkwatch setup --name "Acme Inc" --company
5
+ # docker run --rm -v "$PWD/dw:/work" ghcr.io/daemon-vi/darkwatch run
6
+ #
7
+ # The image is for the read-only scan/CLI. The dashboard (`darkwatch web`) binds to loopback by
8
+ # design and is meant to run on your own machine, not exposed from a container.
9
+
10
+ FROM python:3.12-slim AS build
11
+ WORKDIR /src
12
+ RUN pip install --no-cache-dir build
13
+ COPY pyproject.toml README.md ./
14
+ COPY src ./src
15
+ RUN python -m build --wheel --outdir /dist
16
+
17
+ FROM python:3.12-slim
18
+ LABEL org.opencontainers.image.source="https://github.com/Daemon-VI/darkwatch"
19
+ LABEL org.opencontainers.image.description="Darkwatch — read-only dark web exposure monitor (CLI, with Tor)"
20
+ LABEL org.opencontainers.image.licenses="MIT"
21
+ # tor: the onion sources fetch over it; darkwatch finds `tor` on PATH and manages it per run
22
+ RUN apt-get update \
23
+ && apt-get install -y --no-install-recommends tor ca-certificates \
24
+ && rm -rf /var/lib/apt/lists/*
25
+ COPY --from=build /dist/*.whl /tmp/
26
+ RUN pip install --no-cache-dir /tmp/*.whl && rm -f /tmp/*.whl
27
+ RUN useradd -m -u 10001 watcher
28
+ USER watcher
29
+ WORKDIR /work
30
+ ENTRYPOINT ["darkwatch"]
31
+ CMD ["--help"]
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rithik Krishna
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,351 @@
1
+ Metadata-Version: 2.5
2
+ Name: darkwatch
3
+ Version: 0.5.1
4
+ Summary: Darkwatch: watchlist-driven dark web exposure monitor. Finds a person's or company's identifiers on ransomware leak sites, onion pages (over a managed Tor), breach and paste data; scores, dedupes, reports with next actions and alerts.
5
+ Author-email: Rithik Krishna <317035893+Daemon-VI@users.noreply.github.com>
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.12
8
+ Requires-Dist: beautifulsoup4>=4.12
9
+ Requires-Dist: fastapi>=0.115
10
+ Requires-Dist: pyyaml>=6.0
11
+ Requires-Dist: requests[socks]>=2.32
12
+ Requires-Dist: rich>=13
13
+ Requires-Dist: typer>=0.12
14
+ Requires-Dist: uvicorn>=0.30
15
+ Provides-Extra: dev
16
+ Requires-Dist: aiosmtpd>=1.4; extra == 'dev'
17
+ Requires-Dist: httpx>=0.27; extra == 'dev'
18
+ Requires-Dist: pytest>=8; extra == 'dev'
19
+ Requires-Dist: ruff>=0.5; extra == 'dev'
20
+ Description-Content-Type: text/markdown
21
+
22
+ # Darkwatch
23
+
24
+ A dark web exposure monitor for people and companies you are authorised to protect.
25
+
26
+ **Website: https://rithikkrishnat.me/darkwatch/** · [VS Code extension](https://marketplace.visualstudio.com/items?itemName=daemon-vi.darkwatch) · [Releases](https://github.com/Daemon-VI/darkwatch/releases) · [PyPI](https://pypi.org/project/darkwatch/)
27
+
28
+ You list identifiers in a watchlist: names, emails, phone numbers, domains, usernames. Darkwatch
29
+ checks them against ransomware leak sites, onion pages fetched over Tor, infostealer logs, and
30
+ breach and paste data. It scores each hit by what surrounds it, remembers what it has already
31
+ reported, and writes a report that says what to do about every hit. New hits trigger a Windows
32
+ notification, and optionally a phone push, a webhook or an email. A scheduled task runs it daily.
33
+
34
+ There is a local dashboard for everything that is easier to see than to read: keyword search
35
+ across every finding, filters, charts, triage, live scans, and one-off investigations of a value
36
+ that is not on the watchlist. `darkwatch web`, or the "Darkwatch" shortcut on the Desktop.
37
+
38
+ It only reads. It never logs in, posts, buys, or downloads files, and it keeps no page bodies.
39
+ Per finding it stores the URL, the title, the first and last time it was seen, a short snippet
40
+ around the match, and a hash of the page so a changed page can be recognised — never the page.
41
+
42
+ ## What it searches
43
+
44
+ Ten sources. Nine of them need no key at all.
45
+
46
+ | Source | Needs | What it gives |
47
+ |---|---|---|
48
+ | `leaksites` | nothing | Every post that ransomware gangs have made on their leak sites, as crawled by ransomware.live (the full history since 2013, one bulk file cached for 12 hours) and RansomLook (last 30 days). Claim pages are never fetched. |
49
+ | `recentattacks` | nothing | Recently reported cyber-attacks and breaches — gang-claimed or not — from ransomware.live's incident feed, so an incident a victim discloses or the press covers is caught before any leak-site post exists. |
50
+ | `stealers` | nothing | Machines infected by credential-stealing malware that had a watched email, username or domain saved in the browser, via Hudson Rock's Cavalier OSINT API. This is the one source that says *your own machine*, rather than a service you used. The free tier masks the stolen values (`P********3`), which is all Darkwatch wants: enough to recognise the machine, never the credential. |
51
+ | `leakcheck` | nothing | Which breaches hold each email **or username**, and which data classes they exposed (`ssn`, `dob`, `password`, `address`). The only source that answers for a handle, so a person whose strongest identifier is a username is not invisible. |
52
+ | `xposedornot` | nothing | Breaches and pastes that included each email, plus breaches of the service at each watched domain, with the data classes exposed. The free tier allows 25 lookups per hour. |
53
+ | `hibp` | nothing / `DARKWATCH_HIBP_KEY` | Whether a watched domain's own service was breached (free). With a paid key, per-email breaches and pastes too. |
54
+ | `telegram` | nothing | Public Telegram threat-actor and infostealer channels (from the community-maintained deepdarkCTI index, ~940 channels), searched through Telegram's no-login web preview. This is where much leak and stealer-log trading now happens. |
55
+ | `sites` | nothing | Which of 24 public sites carry each watched username — GitHub, X, YouTube, Mastodon, npm, Docker Hub, Hugging Face, Substack, Tumblr and more. A found profile is then read for the person's other identifiers, so a handle leads to the real name or email printed on that page. The checks run in parallel, since each site is a different host. |
56
+ | `ahmia` | Tor for pages | Ahmia's onion search index. With Tor verified, the search itself goes over Tor to Ahmia's onion service, and never over the clearnet (`ahmia_route`). Every listing is checked locally for the exact term, then matching pages are fetched over Tor as text, plus the top 5 per query. |
57
+ | `seeds` | Tor for onion URLs | Pages you choose, re-read every run, with same-host links followed one level deep. |
58
+
59
+ Every site in `sites` was verified against live responses with a handle known to exist and one
60
+ known to be free; a site that answers 200 for a free handle without a marker that proves absence
61
+ is left out, because it would report every handle as a profile. Add your own with `person_sites`.
62
+
63
+ ## Deep scan
64
+
65
+ ```powershell
66
+ darkwatch run --deep --open
67
+ ```
68
+
69
+ The ordinary run is tuned to finish in a few minutes. `--deep` trades time for reach and can run
70
+ an hour or more:
71
+
72
+ - **every source**, whatever the watchlist enables;
73
+ - **all ~940 Telegram channels** instead of the first 40;
74
+ - **onion depth raised** — the blind top-N sweep goes 5 → 15 pages per query, on a 2,000-page budget
75
+ instead of 150;
76
+ - **links followed one level** out of any onion page that already mentions a watched identifier,
77
+ so Darkwatch reads the actual dark-web site, not just the search listing.
78
+
79
+ Two things `--deep` still will not do, by design:
80
+
81
+ - **It never logs in, joins, pays, or solves a CAPTCHA**, so forums and markets that wall their
82
+ content behind an account (BreachForums successors, XSS, carding markets) are not reached —
83
+ there is no read-only way in, and creating an account is out of scope.
84
+ - **It never blind-crawls.** Links are followed only *out of pages that already match a watched
85
+ identifier*, and onion discovery still goes through Ahmia's abuse filter. Darkwatch does not
86
+ wander an unfiltered index, which is what keeps illegal material off the machine.
87
+
88
+ ## The dashboard
89
+
90
+ ```powershell
91
+ darkwatch web # opens http://127.0.0.1:8787/?t=<token>
92
+ ```
93
+
94
+ - **Search** every finding by keyword, across the term, title, URL, snippet, signals, target and
95
+ source. Quote a phrase. Filters for severity, source, status, target and identifier type, with
96
+ live counts.
97
+ - **Charts**: severity over the last 30 days, the mix by source, and what each run found.
98
+ - **Triage** in place — acknowledge, resolve, mark a false positive — with the recommended
99
+ actions for each finding.
100
+ - **Run a scan** from the page and watch it happen: progress streams live over server-sent events.
101
+ - **Investigate** any value without adding it to the watchlist. Nothing is stored.
102
+ - **Export** the open findings as CSV or JSON.
103
+
104
+ It is locked down rather than merely convenient, because it serves personal data over HTTP:
105
+ it binds to loopback only, every API route requires a token minted at start-up (the link the
106
+ command prints carries it once), a non-loopback `Host` header is refused so DNS-rebinding fails,
107
+ no CORS header is ever sent, and changes are POST-only. Untrusted values — a leak-site title, an
108
+ onion page's text — are inserted as text nodes, never as HTML, and evidence URLs are shown but
109
+ never made clickable.
110
+
111
+ ## Install
112
+
113
+ **Windows** — paste into PowerShell (no administrator rights needed):
114
+
115
+ ```powershell
116
+ irm https://raw.githubusercontent.com/Daemon-VI/darkwatch/main/install.ps1 | iex
117
+ ```
118
+
119
+ **macOS / Linux:**
120
+
121
+ ```sh
122
+ curl -LsSf https://raw.githubusercontent.com/Daemon-VI/darkwatch/main/install.sh | sh
123
+ ```
124
+
125
+ The installer sets up [uv](https://docs.astral.sh/uv/) if you don't have it (uv fetches a
126
+ suitable Python itself), installs the latest release as the `darkwatch` command, asks who to
127
+ watch, offers Desktop shortcuts, and installs the VS Code extension if VS Code is present. Run it
128
+ again at any time to upgrade; your watchlist and findings are kept.
129
+
130
+ Already have uv? `uv tool install <wheel URL from the latest release>` does the same install.
131
+
132
+ **From PyPI:**
133
+
134
+ ```sh
135
+ uv tool install darkwatch # or: pipx install darkwatch
136
+ ```
137
+
138
+ **Docker** — the CLI with Tor already inside, for running scans anywhere (the dashboard stays
139
+ local by design):
140
+
141
+ ```sh
142
+ docker run --rm -v "$PWD/dw:/work" ghcr.io/daemon-vi/darkwatch setup --name "Acme Inc" --company
143
+ docker run --rm -v "$PWD/dw:/work" ghcr.io/daemon-vi/darkwatch run
144
+ ```
145
+
146
+ **Setup.** `darkwatch setup` creates your Darkwatch folder (`~/Darkwatch`, or `DARKWATCH_HOME`)
147
+ with a watchlist for one person or company, asking for their name, emails, domains, usernames
148
+ and phone numbers. It can also run with no prompts:
149
+ `darkwatch setup --name "Asha Rao" --email asha@example.com --username asharao --no-prompt`.
150
+ Every command then finds that watchlist from any folder. A `watchlist.yaml` in the current folder,
151
+ or `--watchlist <file>`, takes precedence. Add more targets by editing the file.
152
+
153
+ `darkwatch doctor` checks the install and says what is missing.
154
+
155
+ **From a source checkout** (development):
156
+
157
+ ```powershell
158
+ git clone https://github.com/Daemon-VI/darkwatch.git
159
+ cd darkwatch
160
+ uv sync --all-extras
161
+ uv run darkwatch setup
162
+ uv run pytest -q
163
+ ```
164
+
165
+ **Tor.** Darkwatch starts `tor.exe` for each run when nothing is already listening on the
166
+ proxy port, and stops it afterwards. It finds `tor.exe` in these places, in order:
167
+
168
+ 1. The `tor_exe` setting.
169
+ 2. The `DARKWATCH_TOR_EXE` environment variable.
170
+ 3. `PATH`.
171
+ 4. `tools\tor-*\tor\tor.exe` beside the watchlist (or up to two folders above it), or in the
172
+ Darkwatch folder — where an unpacked [Tor Expert Bundle](https://www.torproject.org/download/tor/) goes.
173
+ 5. A Tor Browser install (Desktop, `%LOCALAPPDATA%` or Program Files), e.g. after
174
+ `winget install TorProject.TorBrowser`.
175
+
176
+ Without Tor, every source except the onion page fetches still runs. If Tor Browser is already
177
+ running, point `tor_proxy` at `socks5h://127.0.0.1:9150` and Darkwatch will use it without
178
+ starting its own. Verify a downloaded Tor bundle's signature against the Tor Browser Developers
179
+ key `EF6E 286D DA85 EA2A 4BA7 DE68 4E2C 6E87 9329 8290`.
180
+
181
+ ```powershell
182
+ darkwatch check-tor # starts Tor if needed, then asks check.torproject.org
183
+ darkwatch sources # what each source searches and what limits it
184
+ ```
185
+
186
+ **Secrets** live in `.env` beside the watchlist and never in the YAML. See `.env.example`:
187
+ `DARKWATCH_HIBP_KEY`, `DARKWATCH_NTFY_TOPIC`, `DARKWATCH_WEBHOOK_URL`, `DARKWATCH_SMTP_*`.
188
+
189
+ ## Use
190
+
191
+ ```powershell
192
+ darkwatch setup # first run: who to watch
193
+ darkwatch doctor # check the install
194
+ darkwatch web # the dashboard: search, charts, triage, live scans
195
+ darkwatch run --open # everything; opens the HTML report at the end
196
+ darkwatch run --sources leaksites # one source
197
+ darkwatch run --no-tor # no onion page fetches
198
+ darkwatch search "for sale" # keyword search over stored findings
199
+ darkwatch hits --json # stored hits as JSON (for tooling)
200
+ darkwatch search acme --json # search results + facets as JSON
201
+ darkwatch search --severity CRITICAL,HIGH --source leaksite
202
+ darkwatch search --facets # counts per severity, source, status, target, type
203
+ darkwatch investigate jane@mail.com # one value, live sources, nothing stored
204
+ darkwatch hits # open hits (new and acknowledged)
205
+ darkwatch hits --min-severity HIGH --snippets
206
+ darkwatch show 12 # one hit with its recommended actions
207
+ darkwatch ack 12 --note "rotated password"
208
+ darkwatch resolve 12
209
+ darkwatch false-positive 7 --note "different person"
210
+ darkwatch reopen 7
211
+ darkwatch report --open # rebuild the report from stored hits
212
+ darkwatch runs # history: duration, documents, hits, Tor
213
+ darkwatch scan-text dump.txt --save # check a file you already hold
214
+ darkwatch notify-test # synthetic alert through every configured channel
215
+ darkwatch shortcut # put the Desktop shortcuts in place
216
+ ```
217
+
218
+ From a source checkout, prefix each command with `uv run`.
219
+
220
+ ## Desktop shortcuts
221
+
222
+ `darkwatch shortcut` puts three shortcuts on your Desktop, so you never need the command line:
223
+
224
+ - **Darkwatch** starts the dashboard and opens it in your browser. This is the one to use.
225
+ - **Darkwatch - Scan now** runs a scan in a console window and opens the report when it finishes.
226
+ - **Darkwatch - Report** opens the latest report with no console window.
227
+
228
+ Remove them with `darkwatch shortcut --remove`. They point at the project's own interpreter, so
229
+ nothing has to be on `PATH`.
230
+
231
+ Reports go to `reports/` as Markdown, HTML and JSON. `latest.*` always holds the newest set,
232
+ and older sets beyond `keep_reports` (60) are deleted. The HTML report follows the system
233
+ light or dark theme and can be filtered by severity.
234
+
235
+ A hit is one identifier on one piece of evidence: a target, a term, a URL and a source. It is
236
+ reported as new once. Later runs only update its last-seen time. If a later run finds stronger
237
+ evidence at the same URL, the stored evidence is replaced and the hit keeps its triage status.
238
+ If the severity also rises, the hit is escalated: it alerts again, and a resolved hit reopens.
239
+ Reports show only targets that are still in the watchlist.
240
+
241
+ ## Daily runs
242
+
243
+ ```powershell
244
+ darkwatch schedule install --at 09:00 # Task Scheduler: "Darkwatch daily scan"
245
+ darkwatch schedule status
246
+ darkwatch schedule run-now
247
+ darkwatch schedule remove
248
+ ```
249
+
250
+ The task runs the project's `pythonw.exe`, so no console window appears. It runs as you, only
251
+ while you are logged on, which is what lets the notification show. It keeps running on battery
252
+ and starts late if the laptop was off at the scheduled time. Output goes to
253
+ `data/logs/darkwatch.log`. A lock file stops a manual run and the scheduled run from
254
+ overlapping. It is an OS lock, so a killed run never leaves it stuck.
255
+
256
+ ## Alerts
257
+
258
+ | Channel | Configure with | Receives |
259
+ |---|---|---|
260
+ | Windows notification | `notify.desktop: true` (default) | Counts and target names. Clicking it opens `latest.html`. |
261
+ | ntfy phone push | `notify.ntfy_topic` or `DARKWATCH_NTFY_TOPIC` | Severity counts only, with no names. ntfy topics are readable by anyone who knows the name. Use a long random topic, then subscribe to it in the ntfy app. |
262
+ | Webhook (Discord/Slack) | `DARKWATCH_WEBHOOK_URL` | The full summary, with identifiers and URLs. |
263
+ | Email | `DARKWATCH_SMTP_HOST`, `_PORT`, `_USER`, `_PASSWORD`, `_FROM`, `_TO`, `_STARTTLS` | The full summary. Port 465 uses implicit TLS. Certificates are verified, and Darkwatch never logs in without TLS. |
264
+
265
+ Only new or escalated hits at or above `notify.min_severity` (default MEDIUM) are sent.
266
+
267
+ ## Severity
268
+
269
+ A hit's score adds up four parts:
270
+
271
+ 1. **The identifier's weight:** phone 3, email 2, domain 2, name 1, username 1, keyword 1.
272
+ 2. **One point per signal group.** There are six: credentials, financial, government ID, sale,
273
+ doxxing, and access (which covers RDP, VPN, initial access and ransomware wording).
274
+ 3. **The evidence's weight:** leak site 4, infostealer infection 4, reported attack 3, onion
275
+ page 2, seed page 2, breach 2, paste 2, Telegram channel 2, Ahmia listing 1, public profile 0.
276
+ 4. **Minus one point if the evidence is at least 3 years old.** The hit is then marked `dated`.
277
+
278
+ Two things are scored down rather than up, because they were the false positives that mattered:
279
+ a bare run of digits with nothing around it to say it is a phone number, and a single dictionary
280
+ word repeated as a username with no `@handle` context. Both are marked `uncorroborated` and
281
+ credited at most one point, so they surface without shouting.
282
+
283
+ | Score | Severity |
284
+ |---|---|
285
+ | Under 3 | LOW |
286
+ | 3 to 4 | MEDIUM |
287
+ | 5 to 6 | HIGH |
288
+ | 7 or more | CRITICAL |
289
+
290
+ Where signals come from depends on the evidence:
291
+
292
+ - **Onion and seed pages:** the page title plus 160 characters on either side of the match.
293
+ - **Leak-site posts:** the tracker's whole record for that victim — the description, website, sector and country — not the sentence Darkwatch writes around it.
294
+ - **Breaches:** the data classes the breach exposed, such as "Passwords" or "Dates of birth", rather than the prose describing it.
295
+ - **Infostealer infections:** the fact of the infection. A stealer takes saved passwords and session cookies by definition, so that is not something to look for in prose; corporate services on the same machine add the access signal.
296
+ - **Public profiles:** none. A profile a person put up is expected, so it scores LOW; the page's own wording never manufactures a breach signal. It rises only if another source finds the same identifier somewhere worse.
297
+
298
+ A plaintext combolist is also recognised structurally: two or more `user:pass` lines count as
299
+ credentials even when the page never uses the word.
300
+
301
+ Every hit lists its signals, so each score can be explained.
302
+
303
+ ## VS Code extension
304
+
305
+ Install **Darkwatch** (`daemon-vi.darkwatch`) from the VS Code Marketplace or Open VSX — the
306
+ installer above does it for you. It is a thin, safe front end over this CLI, and it walks you
307
+ through the rest: if the CLI is missing, the Darkwatch view offers **Install Darkwatch**; if there
308
+ is no watchlist, **Set Up Darkwatch** asks who to watch; then **Run First Scan**.
309
+
310
+ - a **Findings** view in the Activity Bar, grouped by severity, with a status-bar count;
311
+ - **Run Scan**, **Deep Scan**, **Open Dashboard**, **Investigate** in a terminal that shows
312
+ progress live, refreshing the view when the scan ends;
313
+ - **Search** stored findings; **triage** (acknowledge / resolve / false positive) from a
314
+ finding's menu or the Command Palette;
315
+ - **Open Watchlist**, **Open Latest Report**, **Check Tor**, **Check the Install**.
316
+
317
+ It finds the CLI by itself (on `PATH`, in `~/.local/bin`, or `uv run` in an open checkout); set
318
+ `darkwatch.command` only to override that. Like the dashboard, it never opens an evidence URL —
319
+ those point at leak sites and onion services — it shows the URL as text and copies it on request.
320
+ Every CLI call is a direct process spawn with no shell. It reads data through
321
+ `darkwatch doctor --json`, `darkwatch hits --json` and `darkwatch search --json`.
322
+
323
+ Build, test and install it from source:
324
+
325
+ ```powershell
326
+ cd vscode-extension
327
+ npm install
328
+ npm test # launches VS Code and drives every view state against the real CLI
329
+ npm run package # produces darkwatch-<version>.vsix
330
+ code --install-extension darkwatch-0.2.0.vsix
331
+ ```
332
+
333
+ Releases: `.github/workflows/release.yml` builds the wheel and the `.vsix` and makes a GitHub
334
+ Release on a `v*` tag; `.github/workflows/publish-vscode.yml` publishes the extension to the
335
+ Marketplace and Open VSX on a `vscode-v*` tag (see `vscode-extension/PUBLISHING.md`).
336
+
337
+ ## Responsible use
338
+
339
+ - Monitor only identifiers you own or have written permission to monitor.
340
+ - The report tells you what to do, but it does not do it for you. Actions include changing
341
+ passwords, turning on MFA, a SIM-swap lock, contacting the bank, reporting at
342
+ cybercrime.gov.in or on helpline 1930, and CERT-In's 6-hour incident reporting for
343
+ organisations in India.
344
+ - Preserve evidence with a timestamped Tor Browser screenshot before a page changes. Do not
345
+ follow links out of a hit or download anything from an onion site. If you come across
346
+ illegal content, stop and report it.
347
+
348
+ ## Project docs
349
+
350
+ `docs/PROJECT_STATE.md` records what has been verified, with numbers.
351
+ `docs/ARCHITECTURE.md` explains how the pieces fit and why. `docs/ROADMAP.md` lists what is next.