handle-correlation
Decides which observed usernames belong to the same actor, with a probability you can defend.
Scores handles. Does not discover them. Input is observations you already collected.
pip install handle-correlation
handlecorr score --observations case.csv --corpus usernames.txt
Quickstart
case.csv:
handle,platform,first_seen,last_seen,source_url,link_email,link_pgp,case_ref
kr4ken,github,2019-03-01,2024-11-02,https://...,,ABCD1234...,CASE-1
kraken_x,telegram,2021-06-14,2026-01-20,https://...,k@example.com,ABCD1234...,CASE-1
Only handle and platform are required. The link_* columns are what turn
leads into findings.
handlecorr score --observations case.csv --corpus top-usernames.txt --out ./out
p band grp pair
0.961 STRONG_EVIDENCE 2 handle:github:kr4ken <-> handle:telegram:kraken_x
0.029 WEAK 1 handle:x:kraken1988 <-> handle:npm:krakendev
1 actor cluster(s)
- github:kr4ken, telegram:kraken_x
Runnable: python examples/end_to_end_handles.py
The headline property
Handle reuse across eight platforms cannot produce an attribution.
o = [Observation("kr4ken", p) for p in
("github","telegram","x","forum","gitlab","npm","reddit","keybase")]
assess(all_claims(o), corpus.holders).band # Band.WEAK
Eight platforms is one observation of one naming habit, made once. All same-root claims share a correlation group. Treating them as eight independent confirmations is how a confident false accusation gets built.
Add one cryptographic binding and it resolves:
o = [Observation("kr4ken", "github", linked={"pgp": FPR}),
Observation("kraken_x", "telegram", linked={"pgp": FPR})]
# independent_groups = 2
Confidence policy
Stricter than the generic model, and it governs when they disagree.
| Points | Level | Meaning |
|---|---|---|
| 0–1 | INSUFFICIENT | Not a lead. Consistent with unrelated people. |
| 2–3 | LOW | A lead for further collection. Never an attribution. Do not report it as identifying anyone. |
| 4–5 | MODERATE | Only with a durable identifier; otherwise LOW. |
| 6+ | HIGH | Requires a durable identifier. |
Points count distinct evidence types, not claims. Eight platforms with one handle is one point. Two profiles publishing the same PGP key is one point — one identifier corroborated twice.
hc = correlation_points(all_claims(observations))
hc.points, hc.level.value, hc.caveat # caveat is written to paste into a report
Handle reuse earns the harder floor because it is simultaneously the commonest signal and the weakest: people pick similar names independently, and an adversary can adopt a target's handle at zero cost.
Why no enumeration
Enumeration is solved — maigret and Sherlock do it well. What none of them do is tell you what a match is worth.
Requiring you to supply observations keeps the collection decision, and its legal basis, with the person who has authority to make it. If you need enumeration for an authorized case, run maigret and feed its output in. The interface is a CSV.
Further reading
The method in depth — Signals, Selectivity, Mutation handling, Dark web sources — is in docs/GUIDE.md.
Deploying
DEPLOYMENT.md. Observation files contain real handles from real
cases — keep them outside the repo. .gitignore, scripts/pre-commit and an
artifact-guard CI job enforce this.
ln -sf ../../scripts/pre-commit .git/hooks/pre-commit
Known limitations
METHODOLOGY_AUDIT.md is an adversarial read of this toolkit, written as if by
a reviewer with no stake in it. Read it before relying on a number.
The governing limitation: calibration is unvalidated. Every probability is a defensible ordering, not a measured frequency. And of nine identified failure modes, four bias toward overconfidence and none bias low — an asymmetry that is a direct consequence of having nothing fitted to catch it.
Status
See CHANGELOG.md. Pre-release; API unstable. Calibration not validated against ground truth — bands
are principled, not fitted. Stylometry is deliberately stubbed: the literature
does not support the accuracy commercial tools claim at short text lengths.
Author
Tushar Karumudi — github.com/tusharkarumudi
License
Copyright 2026 Tushar Karumudi.
Licensed under the Apache License, Version 2.0. See LICENSE.
Cite via CITATION.cff.
Metadata
Release files for handle-correlation 2.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| handle_correlation-2.0.1.tar.gz | 40.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| handle_correlation-2.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 61.9 kB
Release files / handle_correlation-2.0.1.tar.gz
| Download URL | handle_correlation-2.0.1.tar.gz |
|---|---|
| Size | 40.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
42c53495cd194a06ddc879861db7c323fdcdde6baf5af31fcdb8b4c7136981b7
|
|
BLAKE2b-256 checksum How to use checksums |
7e0db6695c6c205fea38fe53c08b25460f23e4ce13651f9349bbd5bf9bdefd0f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.
Transparency logRelease files / handle_correlation-2.0.1-py3-none-any.whl
| Download URL | handle_correlation-2.0.1-py3-none-any.whl |
|---|---|
| Size | 21.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
077d8335ccfd8be9255b64156cf6eff3e44af01b2509231a052744beca914b97
|
|
BLAKE2b-256 checksum How to use checksums |
eb7fd113d7a618433130a9ebcd8ef667f713033229d958fc837fa3c596fd9e57
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.
Transparency log