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.
- nymric-0.1.0/.gitattributes +1 -0
- nymric-0.1.0/.github/workflows/ci.yml +25 -0
- nymric-0.1.0/.github/workflows/publish.yml +36 -0
- nymric-0.1.0/.gitignore +21 -0
- nymric-0.1.0/.pre-commit-config.yaml +17 -0
- nymric-0.1.0/CHANGELOG.md +8 -0
- nymric-0.1.0/CONTRIBUTING.md +42 -0
- nymric-0.1.0/DESIGN.md +83 -0
- nymric-0.1.0/ETHICS.md +32 -0
- nymric-0.1.0/LICENSE +21 -0
- nymric-0.1.0/PKG-INFO +176 -0
- nymric-0.1.0/README.md +145 -0
- nymric-0.1.0/SECURITY.md +21 -0
- nymric-0.1.0/docs/demo.svg +240 -0
- nymric-0.1.0/nymric/__init__.py +1 -0
- nymric-0.1.0/nymric/__main__.py +82 -0
- nymric-0.1.0/nymric/data/sites.json +23 -0
- nymric-0.1.0/nymric/faces.py +40 -0
- nymric-0.1.0/nymric/links.py +58 -0
- nymric-0.1.0/nymric/mailto.py +36 -0
- nymric-0.1.0/nymric/probe.py +69 -0
- nymric-0.1.0/nymric/py.typed +0 -0
- nymric-0.1.0/nymric/show.py +62 -0
- nymric-0.1.0/nymric/sites.py +10 -0
- nymric-0.1.0/nymric/stitch.py +68 -0
- nymric-0.1.0/pyproject.toml +49 -0
- nymric-0.1.0/tests/test_nymric.py +223 -0
|
@@ -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
|
nymric-0.1.0/.gitignore
ADDED
|
@@ -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
|
+

|
|
35
|
+

|
|
36
|
+

|
|
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
|
+

|
|
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
|
+

|
|
4
|
+

|
|
5
|
+

|
|
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
|
+

|
|
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.
|
nymric-0.1.0/SECURITY.md
ADDED
|
@@ -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.
|