Skip to main content

handle-correlation

CI PyPI License

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)

Source distribution for handle-correlation 2.0.1
File Size Uploaded
handle_correlation-2.0.1.tar.gz 40.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for handle-correlation 2.0.1
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

This release

2.0.1 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page