nymric 0.1.0__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.
@@ -0,0 +1 @@
1
+ * text=auto eol=lf
@@ -0,0 +1,25 @@
1
+ name: ci
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ test:
13
+ runs-on: ubuntu-latest
14
+ strategy:
15
+ matrix:
16
+ python: ["3.11", "3.12", "3.13", "3.14"]
17
+ steps:
18
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
19
+ - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
20
+ with:
21
+ python-version: ${{ matrix.python }}
22
+ - run: pip install -e ".[dev,avatars]"
23
+ - run: ruff check .
24
+ - run: mypy nymric
25
+ - run: pytest -q --cov-fail-under=80
@@ -0,0 +1,36 @@
1
+ name: publish
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ build:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
15
+ - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
16
+ with:
17
+ python-version: "3.12"
18
+ - run: pip install build
19
+ - run: python -m build
20
+ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
21
+ with:
22
+ name: dist
23
+ path: dist/
24
+
25
+ publish:
26
+ needs: build
27
+ runs-on: ubuntu-latest
28
+ environment: pypi
29
+ permissions:
30
+ id-token: write
31
+ steps:
32
+ - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
33
+ with:
34
+ name: dist
35
+ path: dist/
36
+ - uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1
@@ -0,0 +1,21 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ venv/
5
+ dist/
6
+ build/
7
+ *.egg-info/
8
+ .pytest_cache/
9
+ .mypy_cache/
10
+ .coverage
11
+ htmlcov/
12
+ *.md.out
13
+ mosaic-*.md
14
+
15
+ # secrets / local only
16
+ .env
17
+ .env.*
18
+ *.pem
19
+ *.key
20
+ *.p12
21
+ secrets/
@@ -0,0 +1,17 @@
1
+ repos:
2
+ - repo: https://github.com/gitleaks/gitleaks
3
+ rev: v8.30.1
4
+ hooks:
5
+ - id: gitleaks
6
+ - repo: https://github.com/pre-commit/pre-commit-hooks
7
+ rev: v6.0.0
8
+ hooks:
9
+ - id: detect-private-key
10
+ - id: check-added-large-files
11
+ - id: check-merge-conflict
12
+ - id: end-of-file-fixer
13
+ - id: trailing-whitespace
14
+ - repo: https://github.com/astral-sh/ruff-pre-commit
15
+ rev: v0.15.20
16
+ hooks:
17
+ - id: ruff
@@ -0,0 +1,8 @@
1
+ # changelog
2
+
3
+ ## 0.1.0
4
+
5
+ first cut. async existence checks across 21 hand-verified sites, bio-link following to
6
+ find associated accounts, optional avatar perceptual-hash matching, an email seed that
7
+ pivots through gravatar's public profile, and the correlation mosaic (confirmed, likely,
8
+ possible) with terminal, json, markdown and svg output.
@@ -0,0 +1,42 @@
1
+ # contributing
2
+
3
+ prs welcome. keep them small. the whole point is that the tool stays readable in one
4
+ sitting.
5
+
6
+ ## adding a site
7
+
8
+ sites are data, not code. drop an object into [nymric/data/sites.json](nymric/data/sites.json):
9
+
10
+ ```json
11
+ {"name": "example", "url": "https://example.com/{}", "method": "status", "links": true}
12
+ ```
13
+
14
+ `method: status` is judged on 200 vs 404. `method: text` needs a `found` marker (a string
15
+ only real profiles contain) or an `absent` marker (the not-found string, for sites that
16
+ 200 either way). a `{}` in a marker gets the handle. set `links: true` if the profile is
17
+ worth scraping for outbound handles.
18
+
19
+ rules for a new site:
20
+
21
+ - public data only. no logins, no auth, nothing meant to be private.
22
+ - respect the site's tos and rate limits. conservative by default.
23
+ - prefer a clean 200/404 split. only reach for a text marker when you have to, and say why.
24
+ - 403, 429 and 5xx are inconclusive, never "missing".
25
+ - check it by hand before you open the pr: one real account, one random string, and paste
26
+ what you saw.
27
+
28
+ ## what won't get merged
29
+
30
+ anything that turns this into a target-package generator. block or anti-bot evasion,
31
+ private-data access, scraping behind auth, or bulk "find everyone" features. see
32
+ [ETHICS.md](ETHICS.md), it isn't decoration.
33
+
34
+ ## dev
35
+
36
+ ```
37
+ pip install -e ".[dev,avatars]"
38
+ pre-commit install
39
+ ruff check .
40
+ mypy nymric
41
+ pytest
42
+ ```
nymric-0.1.0/DESIGN.md ADDED
@@ -0,0 +1,83 @@
1
+ # design notes
2
+
3
+ how nymric works, and why it's built this way. the decisions matter more than the code,
4
+ so they're written down.
5
+
6
+ ## the pipeline
7
+
8
+ ```
9
+ seed -> probe every site -> pull bio links -> stitch the mosaic -> show it
10
+ ^
11
+ email seed -> gravatar (resolves to a username first)
12
+ ```
13
+
14
+ one small file per step. `probe` (does the handle exist?), `links` (what other handles
15
+ does the profile point at?), `faces` (do the avatars match?), `stitch` (put it together
16
+ with a confidence), `show` (render), `mailto` (email to gravatar to username). sites live
17
+ as data in `sites.json`, so coverage is a config change, never a code change.
18
+
19
+ ## detecting existence
20
+
21
+ two methods, both unauthenticated.
22
+
23
+ status sites are judged on 200 vs 404, which is clean when a site bothers to 404. text
24
+ sites return 200 for everything, so we match a marker instead: `found` is a string only
25
+ real profiles contain (hacker news has `karma:`), and `absent` is the not-found string, so
26
+ the profile exists when that string is missing (steam's "could not be found").
27
+
28
+ the rule that matters: 403, 429 and 5xx are inconclusive, never "missing". an anti-bot
29
+ block is not evidence the account is absent. this is the single biggest source of junk in
30
+ existence tools that don't draw the distinction, and it's why reddit got cut. its edge
31
+ returns byte-identical 403s for real and fake users when you aren't logged in, so there is
32
+ no honest way to check it unauthenticated.
33
+
34
+ ## three ways it correlates
35
+
36
+ existence is the boring part. the point is deciding which accounts are the same person.
37
+
38
+ bio links come first. fetch a found profile, pull the outbound handles it advertises, and
39
+ if two accounts point at each other (or both at a third we already found) that's a link.
40
+ the extractor is strict on purpose: it drops same-platform self links, reserved and brand
41
+ words, asset files, and anything that isn't a bare profile path, because a github page
42
+ linking to `github.com/features` is chrome, not a person.
43
+
44
+ avatars come second. perceptual-hash (a 64-bit dhash) each profile picture and compare
45
+ across platforms. a near-identical match (hamming distance 6 or less) is a same-person
46
+ signal even when no bio connects them. comparing only across different sites, plus a tight
47
+ threshold, plus dropping near-flat images, keeps a platform's default avatar from forging a
48
+ match.
49
+
50
+ email is the third door in. an email seed is md5-hashed and looked up against gravatar's
51
+ public profile json, and its `preferredUsername` becomes the handle everything else runs
52
+ on. gravatar, not holehe-style password-reset probing, because reset-abuse would
53
+ contradict the project's own ethics.
54
+
55
+ ## confidence
56
+
57
+ confirmed means a bio cross-link or an avatar match ties it to another found account.
58
+ likely means an associated account two or more bios point at. possible means it exists but
59
+ nothing corroborates it, and it's called out as a maybe-namesake rather than sold as a sure
60
+ thing, because usernames aren't unique.
61
+
62
+ every edge carries the reason it exists ("cross-linked from devto, keybase", "same avatar
63
+ as mastodon"), so the score is auditable instead of a black box. no ai in the core path.
64
+
65
+ ## why curated, not 3000 sites
66
+
67
+ big lists rot. they quietly pile up broken detectors and false positives. the 21 sites
68
+ here were each probed with a real handle and a fake one before shipping, and the ones that
69
+ couldn't cleanly tell the two apart were cut. quality over coverage is the feature, and the
70
+ data-driven design means the list can grow the same careful way.
71
+
72
+ ## concurrency
73
+
74
+ one shared httpx client, a bounded semaphore (default 10) so it stays polite, and honest
75
+ handling of redirects and timeouts. avatar fetches ride the same client. it's fast because
76
+ it queries a curated set, not because it hammers anyone.
77
+
78
+ ## limits and what's next
79
+
80
+ markers break when a site redesigns, but they're data so the fix is one line. avatar
81
+ matching needs the optional pillow extra and falls back to bio-links-only without it. next
82
+ on the list: per-detector freshness hints, an avatar cache, a graphviz export of the
83
+ mosaic, and a config file for custom site sets.
nymric-0.1.0/ETHICS.md ADDED
@@ -0,0 +1,32 @@
1
+ # ethics and scope
2
+
3
+ nymric builds a map of someone's public footprint. that's genuinely useful for checking
4
+ your own exposure, brand and impersonation monitoring, or authorized research. it's also
5
+ the exact shape of thing that helps a stalker. so, a few ground rules, and they're not
6
+ decoration.
7
+
8
+ public data only. it reads pages and public apis anyone can open in a browser. no logins,
9
+ no auth, nothing behind an access control, no password-reset probing. if a site needs you
10
+ signed in to see something, nymric doesn't touch it. email seeds go through gravatar's
11
+ public profile api and nothing else.
12
+
13
+ a match is a lead, not a verdict. the same handle on two sites is not proof of the same
14
+ human. results carry a confidence and a reason on purpose. don't publish or act on a "same
15
+ person" guess you haven't verified, because being wrong hurts an innocent bystander.
16
+
17
+ be polite. concurrency is low by default, and 403 / 429 / 5xx are treated as inconclusive,
18
+ never as "this account doesn't exist". don't crank the knobs to hammer a service. slow is
19
+ sustainable.
20
+
21
+ no harassment, stalking, doxxing, or intimidation. don't run this on a private person
22
+ without a lawful, consented, or authorized basis, and don't use it to build a target
23
+ package for someone else to do the same.
24
+
25
+ keep little. it stores nothing by default. if you export a report, treat it as sensitive
26
+ personal data. encrypt it, scope who can read it, delete it when you're done.
27
+
28
+ the law still applies to public data. gdpr, ccpa, and computer-misuse, anti-stalking, and
29
+ harassment statutes don't stop mattering just because the data was public. when in doubt,
30
+ get authorization in writing or don't run the query.
31
+
32
+ provided for lawful research only, with no warranty. you're responsible for how you use it.
nymric-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 m1h
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.
nymric-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,176 @@
1
+ Metadata-Version: 2.4
2
+ Name: nymric
3
+ Version: 0.1.0
4
+ Summary: trace a username or email across public sites and correlate the accounts
5
+ Project-URL: Homepage, https://github.com/m1h/nymric
6
+ Project-URL: Issues, https://github.com/m1h/nymric/issues
7
+ Author: m1h
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: correlation,footprint,osint,recon,username
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Information Technology
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Topic :: Security
17
+ Requires-Python: >=3.11
18
+ Requires-Dist: httpx>=0.27
19
+ Requires-Dist: rich>=13
20
+ Provides-Extra: avatars
21
+ Requires-Dist: pillow>=10; extra == 'avatars'
22
+ Provides-Extra: dev
23
+ Requires-Dist: mypy>=1.11; extra == 'dev'
24
+ Requires-Dist: pillow>=10; extra == 'dev'
25
+ Requires-Dist: pre-commit>=3; extra == 'dev'
26
+ Requires-Dist: pytest-cov>=5; extra == 'dev'
27
+ Requires-Dist: pytest>=8; extra == 'dev'
28
+ Requires-Dist: respx>=0.21; extra == 'dev'
29
+ Requires-Dist: ruff>=0.6; extra == 'dev'
30
+ Description-Content-Type: text/markdown
31
+
32
+ # nymric
33
+
34
+ ![ci](https://github.com/m1h/nymric/actions/workflows/ci.yml/badge.svg)
35
+ ![python](https://img.shields.io/badge/python-3.11%2B-blue)
36
+ ![license](https://img.shields.io/badge/license-MIT-green)
37
+
38
+ check a username (or an email) across a pile of public sites, follow the links on
39
+ whatever profiles show up, and stitch it all into one picture of a single person with a
40
+ confidence on each piece.
41
+
42
+ > tiles, not a photograph. a match is a lead, not proof.
43
+
44
+ ![demo](docs/demo.svg)
45
+
46
+ ## the idea
47
+
48
+ most tools here (sherlock, whatsmyname, enola) answer one question: does this handle
49
+ exist on N sites. useful, but you get a flat wall of hits and you're the one left working
50
+ out which are the same person and which are some stranger with the same name. nymric
51
+ spends its effort on that second part.
52
+
53
+ ## what it does
54
+
55
+ - checks a username across a curated set of sites, all at once
56
+ - reads the outbound links on the profiles that expose them and pulls the person's other
57
+ accounts out of the bio (their twitter, twitch, a linked github, whatever)
58
+ - ties it together. if dev.to and keybase both point at a github we found, that github is
59
+ `confirmed`. a lone hit with nothing backing it is `possible` (might be a stranger),
60
+ not dressed up as a sure thing
61
+ - matches profile pictures too. two accounts with the same photo (perceptual hash, run on
62
+ your machine) is a `confirmed` link even when no bio connects them
63
+ - takes an email as well. it hashes the email, finds the public gravatar profile, and
64
+ pivots on the username that comes back. no password-reset probing, just what gravatar
65
+ already hands out
66
+ - prints a short mosaic, or `--json` / `--md` / `--svg` if you want it elsewhere
67
+
68
+ ## what it's for, and what it won't do
69
+
70
+ it's for checking your own exposure, brand and impersonation monitoring, and authorized
71
+ research. it won't do logins, auth, anything behind a wall, bulk "find everyone", or
72
+ dodging anti-bot. public data only. the [ethics](ETHICS.md) file is short and it's the
73
+ actual point.
74
+
75
+ ## install
76
+
77
+ the easy way (once it's on pypi):
78
+
79
+ ```
80
+ pipx install nymric
81
+ ```
82
+
83
+ or from source:
84
+
85
+ ```
86
+ git clone https://github.com/m1h/nymric
87
+ cd nymric
88
+ pip install .
89
+ ```
90
+
91
+ python 3.11+, two deps (httpx, rich). want avatar matching too? `pip install "nymric[avatars]"`
92
+ pulls in pillow. everything else runs without it.
93
+
94
+ ## quickstart
95
+
96
+ point it at a handle:
97
+
98
+ ```
99
+ nymric torvalds
100
+ ```
101
+
102
+ you get back the accounts that handle shows up on, grouped by how sure it is they're the
103
+ same person, each with the reason why. add `--json` or `--md report.md` to save it. the
104
+ picture at the top of this readme is a real run.
105
+
106
+ ## usage
107
+
108
+ ```
109
+ nymric <username|email> [options]
110
+
111
+ -s, --sites NAME... only these sites (e.g. -s github lichess)
112
+ -c, --concurrency N parallel requests (default 10)
113
+ -t, --timeout SECS per-request timeout (default 10)
114
+ --no-follow don't follow bio links, existence only
115
+ --no-avatars don't hash profile pictures
116
+ --ua STRING user-agent override
117
+ --json json to stdout
118
+ --md FILE also write a markdown report
119
+ --svg FILE also save an svg screenshot
120
+ -V, --version
121
+ ```
122
+
123
+ give it an email instead and it goes through gravatar first:
124
+
125
+ ```
126
+ nymric someone@example.com
127
+ ```
128
+
129
+ ## how it compares
130
+
131
+ | tool | what you get back |
132
+ |---|---|
133
+ | sherlock / enola | whether the handle exists on ~400 sites (flat list, verify it yourself) |
134
+ | whatsmyname | a curated existence dataset and a browser app |
135
+ | maigret | existence plus a recursive search of the same handle, big dump |
136
+ | nymric | follows bio links to the person's other accounts and ties them into one mosaic, each piece with a confidence and the reason it's there |
137
+
138
+ ## how it works
139
+
140
+ every site is a row of data, not code, in [nymric/data/sites.json](nymric/data/sites.json):
141
+
142
+ ```json
143
+ {"name": "github", "url": "https://github.com/{}", "method": "status", "links": true}
144
+ ```
145
+
146
+ `status` sites are judged on 200 vs 404. `text` sites carry a `found` marker (a string
147
+ only real profiles contain) or an `absent` marker (the "not found" string, for sites like
148
+ steam that return 200 either way). a `{}` in a marker gets the handle.
149
+
150
+ the rule that matters most: 403 / 429 / 5xx never count as "missing", they count as
151
+ inconclusive. a bot-block is not proof the account isn't there. that is the biggest source
152
+ of junk results in tools that skip it, and it's why reddit isn't on the list (its edge
153
+ serves identical 403s for real and fake users when you aren't logged in).
154
+
155
+ adding a site is a json edit. the rest is a handful of small files: `probe`, `links`,
156
+ `stitch`, `faces`, `mailto`. there's a longer writeup in [DESIGN.md](DESIGN.md).
157
+
158
+ ## honest about the limits
159
+
160
+ - usernames aren't unique. a `possible` hit really might be someone else. that's why it's
161
+ flagged instead of hidden.
162
+ - the list is small on purpose (21 sites). big lists rot. these are ones that behave
163
+ without an anti-bot wall. reddit, instagram, tiktok, x need auth or block you, so they're
164
+ left out instead of faked.
165
+ - markers break when a site redesigns. it's data, so the fix is one line.
166
+
167
+ ## prior art
168
+
169
+ built on the shoulders of [sherlock](https://github.com/sherlock-project/sherlock),
170
+ [maigret](https://github.com/soxoj/maigret), and the
171
+ [whatsmyname](https://github.com/WebBreacher/WhatsMyName) dataset. the angle here is the
172
+ correlation layer they leave to you.
173
+
174
+ ## license
175
+
176
+ MIT. lawful research only, no warranty.
nymric-0.1.0/README.md ADDED
@@ -0,0 +1,145 @@
1
+ # nymric
2
+
3
+ ![ci](https://github.com/m1h/nymric/actions/workflows/ci.yml/badge.svg)
4
+ ![python](https://img.shields.io/badge/python-3.11%2B-blue)
5
+ ![license](https://img.shields.io/badge/license-MIT-green)
6
+
7
+ check a username (or an email) across a pile of public sites, follow the links on
8
+ whatever profiles show up, and stitch it all into one picture of a single person with a
9
+ confidence on each piece.
10
+
11
+ > tiles, not a photograph. a match is a lead, not proof.
12
+
13
+ ![demo](docs/demo.svg)
14
+
15
+ ## the idea
16
+
17
+ most tools here (sherlock, whatsmyname, enola) answer one question: does this handle
18
+ exist on N sites. useful, but you get a flat wall of hits and you're the one left working
19
+ out which are the same person and which are some stranger with the same name. nymric
20
+ spends its effort on that second part.
21
+
22
+ ## what it does
23
+
24
+ - checks a username across a curated set of sites, all at once
25
+ - reads the outbound links on the profiles that expose them and pulls the person's other
26
+ accounts out of the bio (their twitter, twitch, a linked github, whatever)
27
+ - ties it together. if dev.to and keybase both point at a github we found, that github is
28
+ `confirmed`. a lone hit with nothing backing it is `possible` (might be a stranger),
29
+ not dressed up as a sure thing
30
+ - matches profile pictures too. two accounts with the same photo (perceptual hash, run on
31
+ your machine) is a `confirmed` link even when no bio connects them
32
+ - takes an email as well. it hashes the email, finds the public gravatar profile, and
33
+ pivots on the username that comes back. no password-reset probing, just what gravatar
34
+ already hands out
35
+ - prints a short mosaic, or `--json` / `--md` / `--svg` if you want it elsewhere
36
+
37
+ ## what it's for, and what it won't do
38
+
39
+ it's for checking your own exposure, brand and impersonation monitoring, and authorized
40
+ research. it won't do logins, auth, anything behind a wall, bulk "find everyone", or
41
+ dodging anti-bot. public data only. the [ethics](ETHICS.md) file is short and it's the
42
+ actual point.
43
+
44
+ ## install
45
+
46
+ the easy way (once it's on pypi):
47
+
48
+ ```
49
+ pipx install nymric
50
+ ```
51
+
52
+ or from source:
53
+
54
+ ```
55
+ git clone https://github.com/m1h/nymric
56
+ cd nymric
57
+ pip install .
58
+ ```
59
+
60
+ python 3.11+, two deps (httpx, rich). want avatar matching too? `pip install "nymric[avatars]"`
61
+ pulls in pillow. everything else runs without it.
62
+
63
+ ## quickstart
64
+
65
+ point it at a handle:
66
+
67
+ ```
68
+ nymric torvalds
69
+ ```
70
+
71
+ you get back the accounts that handle shows up on, grouped by how sure it is they're the
72
+ same person, each with the reason why. add `--json` or `--md report.md` to save it. the
73
+ picture at the top of this readme is a real run.
74
+
75
+ ## usage
76
+
77
+ ```
78
+ nymric <username|email> [options]
79
+
80
+ -s, --sites NAME... only these sites (e.g. -s github lichess)
81
+ -c, --concurrency N parallel requests (default 10)
82
+ -t, --timeout SECS per-request timeout (default 10)
83
+ --no-follow don't follow bio links, existence only
84
+ --no-avatars don't hash profile pictures
85
+ --ua STRING user-agent override
86
+ --json json to stdout
87
+ --md FILE also write a markdown report
88
+ --svg FILE also save an svg screenshot
89
+ -V, --version
90
+ ```
91
+
92
+ give it an email instead and it goes through gravatar first:
93
+
94
+ ```
95
+ nymric someone@example.com
96
+ ```
97
+
98
+ ## how it compares
99
+
100
+ | tool | what you get back |
101
+ |---|---|
102
+ | sherlock / enola | whether the handle exists on ~400 sites (flat list, verify it yourself) |
103
+ | whatsmyname | a curated existence dataset and a browser app |
104
+ | maigret | existence plus a recursive search of the same handle, big dump |
105
+ | nymric | follows bio links to the person's other accounts and ties them into one mosaic, each piece with a confidence and the reason it's there |
106
+
107
+ ## how it works
108
+
109
+ every site is a row of data, not code, in [nymric/data/sites.json](nymric/data/sites.json):
110
+
111
+ ```json
112
+ {"name": "github", "url": "https://github.com/{}", "method": "status", "links": true}
113
+ ```
114
+
115
+ `status` sites are judged on 200 vs 404. `text` sites carry a `found` marker (a string
116
+ only real profiles contain) or an `absent` marker (the "not found" string, for sites like
117
+ steam that return 200 either way). a `{}` in a marker gets the handle.
118
+
119
+ the rule that matters most: 403 / 429 / 5xx never count as "missing", they count as
120
+ inconclusive. a bot-block is not proof the account isn't there. that is the biggest source
121
+ of junk results in tools that skip it, and it's why reddit isn't on the list (its edge
122
+ serves identical 403s for real and fake users when you aren't logged in).
123
+
124
+ adding a site is a json edit. the rest is a handful of small files: `probe`, `links`,
125
+ `stitch`, `faces`, `mailto`. there's a longer writeup in [DESIGN.md](DESIGN.md).
126
+
127
+ ## honest about the limits
128
+
129
+ - usernames aren't unique. a `possible` hit really might be someone else. that's why it's
130
+ flagged instead of hidden.
131
+ - the list is small on purpose (21 sites). big lists rot. these are ones that behave
132
+ without an anti-bot wall. reddit, instagram, tiktok, x need auth or block you, so they're
133
+ left out instead of faked.
134
+ - markers break when a site redesigns. it's data, so the fix is one line.
135
+
136
+ ## prior art
137
+
138
+ built on the shoulders of [sherlock](https://github.com/sherlock-project/sherlock),
139
+ [maigret](https://github.com/soxoj/maigret), and the
140
+ [whatsmyname](https://github.com/WebBreacher/WhatsMyName) dataset. the angle here is the
141
+ correlation layer they leave to you.
142
+
143
+ ## license
144
+
145
+ MIT. lawful research only, no warranty.
@@ -0,0 +1,21 @@
1
+ # security
2
+
3
+ ## reporting
4
+
5
+ if you find a security issue in nymric itself (not in a site it queries), please don't
6
+ open a public issue. use github's private "report a vulnerability" flow, and give it a few
7
+ days before disclosing. i'll credit you unless you'd rather stay anonymous.
8
+
9
+ ## in scope
10
+
11
+ nymric only reads public data. no auth, nothing behind an access control, no
12
+ password-reset probing (see [ETHICS.md](ETHICS.md)). worth flagging:
13
+
14
+ - an ssrf-style bug where a crafted bio link makes the tool fetch somewhere it shouldn't
15
+ - a site module that reaches a private or authenticated endpoint
16
+ - anything that lets it be pointed at a target harder than the polite defaults allow
17
+
18
+ ## not in scope
19
+
20
+ - a site changing its markup so a detector goes stale. that's a normal bug, open a pr.
21
+ - a queried site rate-limiting or blocking you. expected, don't work around it.