osint-foundations.md (41011B)
1 --- 2 title: "OSINT Foundations & Tradecraft" 3 description: "How to run an open-source investigation without burning yourself or your case: research accounts, collection hygiene, provenance, and knowing when to stop." 4 category: osint 5 subcategory: "Foundations" 6 tags: [osint, methodology, opsec, verification] 7 tools: [hunchly, obsidian, logseq, multipass, lima, docker, curl, wget, dig, shasum] 8 difficulty: beginner 9 updated: 2026-10-04 10 references: 11 - name: "Bellingcat's Online Investigation Toolkit" 12 url: "https://bellingcat.gitbook.io/toolkit" 13 author: "Bellingcat" 14 license: none 15 relation: derived 16 note: "Tool catalogue: names, descriptions, cost flags and links for this area." 17 - name: "OSINT Newsletter Tools Library" 18 url: "https://tools.osintnewsletter.com" 19 author: "The OSINT Newsletter" 20 license: none 21 relation: derived 22 note: "Second tool catalogue, cross-checked against the above." 23 - name: "Bellingcat — Stay Safe" 24 url: "https://bellingcat.gitbook.io/toolkit/resources/stay-safe" 25 author: "Bellingcat" 26 license: none 27 relation: inspired 28 note: "Operational-safety guidance for investigators." 29 --- 30 31 ## What this covers 32 33 The habits that decide whether an investigation holds up: how you look at a target without telling 34 them, how you record what you found so it survives the page being deleted, and how you avoid 35 deciding the answer before you have it. Most of this sheet is discipline rather than tooling, 36 because most of it has no command — and where a tool genuinely exists, the commands below are the 37 ones that keep you from leaking. 38 39 ## The rule that matters most 40 41 **Every request you make is a signal to the other side.** Viewing a LinkedIn profile notifies its 42 owner. Loading a Telegram channel with your real account puts your username in the member list. 43 Hitting a small target's website leaves your IP in their logs. A reverse image search on a photo 44 that only three people have seen tells those three people someone is looking. 45 46 Decide before you start: is this target likely to notice, and does it matter if they do? 47 48 ## Method 49 50 1. **Write the question down before you collect anything.** One sentence, answerable, with a 51 stated standard of proof. "Is this company linked to that one" is not a question; "does any 52 public filing show a shared officer, address or beneficial owner" is. 53 2. **Decide your exposure budget first.** Which identity touches the target, from what address, 54 and what happens if that address is logged. Changing this mid-investigation is how an account 55 gets burned. 56 3. **Capture as you go, never afterwards.** Anything interesting is archived the moment you see it. 57 Pages disappear, get edited or go private within hours of someone noticing attention. See 58 [Archiving & Evidence Preservation](/sheets/osint/archiving-and-evidence). 59 4. **Record the route, not just the result.** URL, UTC timestamp, the query that surfaced it, and 60 what you clicked to get there. A screenshot with no source and no date is worth nothing, and the 61 path to a finding is the part you forget first. 62 5. **Keep raw separate from conclusions, in different files.** One place for what you collected, 63 another for what you think it means. Conflating them is how an assumption becomes a fact three 64 notes later and a published claim three weeks later. 65 6. **Log what you looked for and did not find.** Negative results stop you re-running the same 66 dead end next week, and they are what an honest report needs in order to describe its own limits. 67 7. **Name the thing that would prove you wrong, then go look for it.** If you cannot state what 68 evidence would change your conclusion, you are not investigating, you are assembling. 69 8. **Stop at the question you wrote down.** The collection will always offer you more. More about a 70 bystander, more about a family member, more that is interesting and none of your business. 71 72 ## Key tools 73 74 ### Hunchly 75 76 The one tool that removes the discipline problem, because it captures continuously rather than when 77 you remember to. It is a browser extension that records every page you visit while a case is open: 78 the full HTML and resources, the URL, a UTC timestamp and a hash of the capture, into a local case 79 file that is full-text searchable. That hash-at-capture-time is the evidentiary point — it is the 80 same argument as the manifest pattern in 81 [Archiving & Evidence Preservation](/sheets/osint/archiving-and-evidence), applied automatically to 82 everything you looked at including the pages that turned out not to matter. 83 84 Commercial, subscription, now owned by Maltego Technologies, with a 30-day trial that needs no card. 85 There is no CLI and no API: this is a GUI tool and the workflow is the product. 86 87 ```text 88 1. Install the extension, open the desktop app, and create a case BEFORE you 89 start looking. Captures outside an open case are not recorded. 90 2. Set the case name to your case ID, not to the target's name. The case file 91 leaks the target's name to anyone who sees your screen or your backups. 92 3. Toggle capture ON. The extension icon state is the only indication; check it 93 after every browser restart, because a silent-off session is unrecoverable. 94 4. Add "selectors" — the names, handles, domains and phone numbers you are 95 tracking. Hunchly then highlights them on every page you visit and logs 96 which page each one appeared on. This is the feature people underuse: it 97 catches a name in a footer you would never have read. 98 5. Tag pages as you go. An untagged case file of 4,000 pages is a search index, 99 not a narrative. 100 6. Export: the case export carries the captured pages, the hashes, the timestamps 101 and the selector hit log. Export at milestones, not just at the end — the 102 case file is a single local database and a single local database can corrupt. 103 7. Storage choice matters. Local keeps everything on your machine; the hosted 104 option puts your captures and therefore your whole research pattern on 105 someone else's infrastructure. Pick deliberately and write down which. 106 ``` 107 108 What it does not do: a Hunchly capture is a record that *you* saw that content at that time, with no 109 third-party attestation. It is excellent provenance for your own process and weak evidence against 110 a determined challenge, which is why anything load-bearing still gets a public-archive capture and 111 an independent timestamp. It also captures everything, including pages you visited by accident and 112 pages containing other people's personal data — treat the case file as sensitive material in its own 113 right. 114 115 ### Obsidian or Logseq: the case vault 116 117 Local, plain-text, file-per-entity notes with links between them. The reason this beats a document 118 is that an investigation is a graph of entities, not a narrative, and the thing you need six weeks 119 in is "every note that mentions this phone number". Obsidian suits entity-per-note work where the 120 links between people are the finding; Logseq's daily journal and outliner suit chronology-heavy 121 work. Both store Markdown on disk, so the vault is greppable, diffable and `git`-able without the 122 application. 123 124 Structure the vault so that provenance cannot be separated from content: 125 126 ```text 127 case-2026-014/ 128 00-question.md the question, the standard of proof, the stop condition 129 10-entities/ 130 person-a-khan.md one file per entity, named by role not by guesswork 131 company-northgate.md 132 account-acct_f.md 133 20-raw/ collected artefacts, never edited 134 2026-10-04T0407Z-post123.warc.gz 135 2026-10-04T0407Z-post123.info.json 136 MANIFEST.sha256 137 30-findings/ 138 f01-post-edited-after-upload.md 139 40-negative/ 140 not-found.md every search that returned nothing, with the query 141 90-log.md append-only: what you did, when, from which identity 142 ``` 143 144 Every entity note carries its own provenance header, so a claim can never be read without its 145 source: 146 147 ```markdown 148 --- 149 entity: company-northgate 150 type: company 151 aliases: ["Northgate Ltd", "Northgate Limited", "northgate ltd"] 152 confidence: medium 153 first_seen: 2026-10-03 154 last_checked: 2026-10-04 155 --- 156 157 ## Established 158 159 - Registered number 09876543, UK. Source: Companies House API, retrieved 160 2026-10-04T04:07Z. Raw: `20-raw/ch-09876543.json` (sha256 4b1c8e...). 161 162 ## Reported but unverified 163 164 - Described as "the trading arm" in the filing at para 17. Single source, 165 uncorroborated, author has an interest. Do not repeat as fact. 166 167 ## Ruled out 168 169 - Not the same as Northgate Mining Ltd (number 07654321). Different officers, 170 different address, name similarity only. Checked 2026-10-04. 171 172 ## Open 173 174 - Beneficial owner behind the BVI parent. UK PSC filing names the parent only. 175 ``` 176 177 The `Ruled out` section is the one people skip and the one that saves the investigation. An 178 explicitly rejected hypothesis stays rejected; an implicitly rejected one resurfaces in a month as 179 a half-remembered lead and sometimes as a published error. 180 181 Three cautions. Do not use wiki-style double-bracket links if the notes might ever be published or 182 converted — they do not resolve outside the application, and a dead link in a published finding 183 reads as sloppiness. Sync services put your entire research graph on third-party infrastructure, so 184 if the vault syncs, know where to. And a vault is not an archive: the notes reference the artefacts, 185 and the artefacts need their own hashes and timestamps. 186 187 ### The research account 188 189 A separate identity that touches targets, which you expect to lose. Platforms ban research accounts 190 eventually — for scraping, for viewing too many profiles, for geographic inconsistency, for nothing 191 at all — so build nothing on it you cannot walk away from, and never let it share anything with an 192 account you care about. 193 194 There is no tool for this. There is a checklist, and the order matters: 195 196 ```text 197 BEFORE the account exists 198 [ ] Decide the persona's purpose. A plausible-but-empty account is more 199 suspicious than an obviously new one with a stated interest. 200 [ ] Email first, from a provider that does not require a phone number, and 201 never from the provider your real accounts use. 202 [ ] Phone number, if the platform demands one. A number you control and can 203 lose. Never your own; a reused number links the research account to you 204 permanently and silently, because platforms match on it across services. 205 [ ] Password manager entry with the case ID in the title, so the account is 206 findable and disposable as a unit. 207 208 WHEN the account exists 209 [ ] Separate browser profile at minimum, separate VM if the target is 210 competent. Never the same profile as anything personal — shared cookies, 211 shared localStorage and shared autofill all link them. 212 [ ] Consistent timezone, language and locale between the account's stated 213 location and the browser's. A profile claiming Lisbon from an en-GB 214 browser on UTC+0 is a detectable mismatch. 215 [ ] No contact import. Ever. One accidental contact sync hands the platform 216 your real address book and the platform hands the target "people you 217 may know". 218 [ ] Age the account before using it. A day-old account viewing 200 profiles 219 is a rate-limit and a ban; a two-month-old one is a user. 220 221 ONGOING 222 [ ] One account per investigation where the targets could plausibly compare 223 notes. Cross-contamination between cases is how one burn becomes three. 224 [ ] Log every target the account touched, so you can assess the damage when 225 it is eventually identified. 226 [ ] Expect it to be lost. Export anything you need from it as you go. 227 ``` 228 229 The legal and ethical line is not a technical question and the checklist does not answer it. 230 Creating an account usually breaches a platform's terms of service; in some jurisdictions and some 231 employment contexts it does more than that, and a persona that actively deceives a person — rather 232 than merely observing public content — is a different act from a passive research account. Know 233 which one you are doing, get it authorised in writing if you are doing it for anyone but yourself, 234 and note that nothing here makes impersonating a real person or organisation acceptable. 235 236 ### Isolation: a disposable VM 237 238 A compromise or a deanonymisation should cost you a throwaway machine rather than your real one and 239 the identity attached to it. Containers and virtual machines are not interchangeable here: 240 a container shares the host kernel and, with default settings, the host network identity, which 241 makes it fine for running CLI tooling and wrong for browsing a hostile target. Browse from a VM. 242 243 ```bash 244 # full VM, Ubuntu, disposable: multipass on macOS and Windows 245 multipass find # 26.04 is the current LTS alias 246 multipass launch lts --name research-014 --cpus 2 --memory 4G --disk 20G 247 multipass shell research-014 248 multipass stop research-014 && multipass delete research-014 --purge # gone 249 250 # or Lima, the same idea with a declarative YAML template. 251 # Note the locator form: `template:` — the older `template://` is deprecated as of Lima 2.0 252 limactl create --name=research-014 template:ubuntu-lts 253 limactl start research-014 254 limactl shell research-014 255 limactl delete --force research-014 256 257 # snapshot before you touch the target, so you can roll back to clean. 258 # multipass only snapshots a STOPPED instance, so stop it first 259 multipass stop research-014 260 multipass snapshot research-014 --name pre-target 261 multipass start research-014 262 # ...after the session, roll back 263 multipass stop research-014 264 multipass restore research-014.pre-target --destructive 265 ``` 266 267 ```bash 268 # containers: correct for CLI tooling, with the network identity made explicit 269 docker run --rm -it \ 270 --dns 1.1.1.1 \ 271 --cap-drop ALL --security-opt no-new-privileges \ 272 -v "$PWD/out:/out" \ 273 python:3.13-slim bash 274 275 # route a container's traffic through a proxy you control, and nothing else 276 docker run --rm -it \ 277 -e ALL_PROXY=socks5h://host.docker.internal:1080 \ 278 -e HTTPS_PROXY=socks5h://host.docker.internal:1080 \ 279 -v "$PWD/out:/out" python:3.13-slim bash 280 281 # a container that cannot reach the network at all, for handling a hostile file 282 docker run --rm -it --network none -v "$PWD/sample:/sample:ro" python:3.13-slim bash 283 284 # check the VM's egress before you use it, not after 285 multipass exec research-014 -- curl -s https://am.i.mullvad.net/json 286 ``` 287 288 For network-level rather than machine-level isolation, Whonix routes an entire VM's traffic through 289 Tor at the gateway, so a misconfigured application inside it cannot leak around the proxy, and 290 Tails gives you an amnesic live system that forgets everything on shutdown. Both are the right 291 answer when the consequence of being identified is serious; both are heavy enough that people skip 292 them for routine work, which is a defensible trade as long as it is a decision rather than a 293 default. 294 295 What isolation does not buy you: a clean VM behind a VPN is still identified by the account you log 296 into, the browser fingerprint you present and the timing of your activity. Isolation limits the 297 blast radius of a mistake. It does not make you anonymous. 298 299 ### Touching a target without leaking 300 301 When you need the bytes from a target's own server rather than from an archive, go via the command 302 line rather than a browser, because a browser sends dozens of headers you did not choose and runs 303 code the target wrote. Here the honest version matters: **neither `curl` nor `wget` has a 304 `--no-referer` flag, because neither sends a `Referer` header unless you ask it to.** Verified, a 305 default `curl` request arrives at the server carrying only `Host`, `User-Agent` and `Accept`. 306 307 ```bash 308 # exactly what a default curl sends — check this yourself rather than trusting it 309 curl -s https://postman-echo.com/headers | jq . 310 # {"headers":{"host":"postman-echo.com","user-agent":"curl/8.7.1","accept":"*/*", ...}} 311 ``` 312 313 ```bash 314 # headers only, no body: cheapest possible look, and it reveals the stack 315 curl -sI 'https://target.example/page' 316 317 # response headers AND body, with the body discarded — some servers lie on HEAD 318 curl -s -o /dev/null -D - 'https://target.example/page' 319 320 # present a plausible browser UA. A default "curl/8.7.1" in a small site's log 321 # is a flag that says "someone is scripting against us" 322 curl -s -A 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0 Safari/537.36' \ 323 'https://target.example/page' -o page.html 324 325 # send no User-Agent at all, which is a different and sometimes better choice 326 curl -s -H 'User-Agent:' 'https://target.example/page' -o page.html 327 328 # a Referer only when you deliberately want the log to show a plausible path 329 curl -s -A 'Mozilla/5.0 ...' -e 'https://www.google.com/' 'https://target.example/page' 330 331 # show the redirect chain without following it into something you did not expect 332 curl -sIL -w '%{http_code} %{url_effective}\n' -o /dev/null 'https://target.example/short' 333 334 # through a SOCKS proxy, with DNS resolved AT the proxy — socks5h, not socks5 335 curl -s --proxy socks5h://127.0.0.1:9050 'https://target.example/page' -o page.html 336 337 # wget equivalents, for a recursive pull you want to keep polite 338 wget -U 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0 Safari/537.36' \ 339 --wait=2 --random-wait --limit-rate=200k \ 340 --page-requisites --adjust-extension 'https://target.example/page' 341 ``` 342 343 `socks5h` versus `socks5` is the one that bites people: with `socks5`, `curl` resolves the hostname 344 locally and only the TCP connection goes through the proxy, so your resolver — and therefore your 345 ISP and often your employer — sees exactly which host you looked up. With `socks5h` the proxy does 346 the resolution. The same distinction applies to `ALL_PROXY` in the container examples above. 347 348 Two things no flag fixes. The TLS handshake carries the hostname in SNI unless the server supports 349 Encrypted Client Hello, so a passive observer on your network learns which site you contacted 350 regardless of these options. And a target that fronts its site with a CDN sees your request through 351 that CDN's logging, which is a second party you did not choose. 352 353 ### Leak checks: IP, DNS and WebRTC 354 355 Run these before you touch a target, after every network change, and after every VPN reconnect. 356 The failure you are looking for is not "am I behind a VPN" — it is "does something on this machine 357 resolve or connect outside the tunnel", which is invisible until you measure it. 358 359 ```bash 360 # the address a web server sees 361 curl -s https://am.i.mullvad.net/json | jq '{ip, country, city, mullvad_exit_ip, organization}' 362 curl -s https://ifconfig.co/json | jq '{ip, country, asn_org}' 363 364 # the address your DNS RESOLVER egresses from — this is the leak that matters. 365 # If this is your ISP while the line above is a VPN exit, DNS is outside the tunnel. 366 dig +short TXT o-o.myaddr.l.google.com @ns1.google.com 367 368 # resolver IP, your apparent client IP, and the EDNS Client Subnet your resolver 369 # is handing to authoritative servers. An "ecs" line means your /24 is being 370 # disclosed to every nameserver you query. 371 dig +short TXT whoami.ds.akahelp.net 372 373 # which resolvers this machine is actually using, whatever the VPN client claims 374 scutil --dns | grep nameserver # macOS 375 resolvectl status | grep -A2 'DNS Serv' # systemd-resolved 376 377 # does a hostname resolve the same inside and outside the isolated VM 378 dig +short target.example 379 multipass exec research-014 -- dig +short target.example 380 381 # mullvad's own CLI, if that is your provider: state, and whether DNS is leaking 382 mullvad status 383 mullvad dns get 384 ``` 385 386 WebRTC and browser fingerprinting have no CLI equivalent, because the leak is the browser's and 387 only a browser reproduces it: 388 389 ```text 390 https://browserleaks.com/webrtc does the browser disclose your real local 391 and public IP via ICE candidates, around 392 the proxy. This is the classic VPN leak and 393 it is a browser setting, not a network one. 394 https://browserleaks.com/dns which resolvers the browser actually used 395 https://www.dnsleaktest.com/ extended test; run it, not the standard one 396 https://coveryourtracks.eff.org/ how distinctive your fingerprint is. Read 397 the "one in N browsers" number, not the 398 pass/fail badge. 399 https://browserleaks.com/geo whether the page can get a precise location 400 from the OS rather than from the IP 401 ``` 402 403 Read the fingerprint result carefully, because the intuition is backwards: hardening a browser with 404 unusual settings and a long extension list makes it *more* identifiable, not less. A stock browser 405 in a stock VM is often the better disguise than a heavily customised one, and the only reliable 406 counter to fingerprinting is to look like a large crowd. 407 408 What these checks cannot tell you: whether the target correlated your visit with something else. 409 Timing, a reused screen resolution, the same unusual font set, a session that starts every weekday 410 at 09:10 UTC — none of that shows up in a leak test, and all of it is linkable across identities. 411 412 ### Chain of custody 413 414 The record that lets you say, later, that the file you are producing is the file you received, and 415 that nothing happened to it in between that you have not written down. It costs a minute per 416 artefact and it is the first thing attacked when a finding matters. 417 418 ```bash 419 # the moment an artefact arrives, before you open it 420 IN=~/cases/2026-014/20-raw 421 mkdir -p "$IN" 422 cp /Volumes/USB/clip.mp4 "$IN/" # copy, never move 423 shasum -a 256 "$IN/clip.mp4" | tee -a "$IN/MANIFEST.sha256" 424 chmod 444 "$IN/clip.mp4" # read-only: work on copies 425 426 # the custody note, as a sibling file, written now and never edited 427 cat > "$IN/clip.mp4.custody" <<'EOF' 428 artefact: clip.mp4 429 sha256: 4b1c8e... # from MANIFEST.sha256 430 received_at: 2026-10-04T04:07:56Z # date -u +%FT%TZ 431 received_from: source S-3 (see 10-entities/source-s3.md), in person, USB 432 provided_as: claimed original off a phone; no chain before this point 433 handled_by: J. Investigator 434 first_action: hashed, set read-only, copied to 50-work/ for analysis 435 onward: none 436 EOF 437 438 # every derived file records what it came from, so no copy is ever orphaned 439 shasum -a 256 50-work/clip-frame-0137.png >> 50-work/DERIVED.sha256 440 printf '%s\tderived from\t%s\n' 'clip-frame-0137.png' 'clip.mp4 (4b1c8e...)' \ 441 >> 50-work/DERIVED.index 442 443 # append-only activity log, one line per action, in UTC 444 printf '%s\t%s\n' "$(date -u +%FT%TZ)" 'extracted frame 00:01:37.5 with ffmpeg 8.0' \ 445 >> ~/cases/2026-014/90-log.md 446 447 # verify the whole raw tree before you hand anything over 448 shasum -a 256 -c "$IN/MANIFEST.sha256" | grep -v ': OK$' 449 ``` 450 451 Then timestamp the manifest, which is what converts your own record into a third party's assertion 452 about time — `ots stamp` and the RFC 3161 route are in 453 [Archiving & Evidence Preservation](/sheets/osint/archiving-and-evidence). 454 455 Four rules that are not negotiable. UTC everywhere, because a local timestamp in a report with 456 international sources is ambiguous and ambiguity is attackable. Copy rather than move, so the 457 original stays where it was. Write the note at the time, because a custody note reconstructed from 458 memory is exactly as reliable as it sounds. And record the gap honestly: if you do not know where 459 the file was before your source handed it to you, the note says "no chain before this point" rather 460 than nothing, because an unstated gap reads as a concealed one. 461 462 ### The negative-results log 463 464 The cheapest high-value habit on this page, and the one almost nobody keeps. A searchable record of 465 every query that returned nothing stops you repeating dead ends, tells you where your coverage 466 actually ends, and is the only honest basis for a sentence like "no public record of X exists". 467 468 ```text 469 # 40-negative/not-found.md — append-only, one block per attempt 470 471 ## 2026-10-04T04:12Z — Companies House officer search 472 query: "Khan" + date of birth 1979-03 473 scope: all UK registered companies, active and dissolved 474 result: 0 matches with that DOB month 475 means: not registered as a UK officer under that spelling. Does NOT mean 476 not an officer: Companies House shows DOB month and year only, and 477 transliteration variants were not tested. 478 next: re-run via Bellingcat Name Variant Search for Cyrillic variants 479 480 ## 2026-10-04T04:31Z — Sherlock, handle "acct_f" 481 query: sherlock acct_f --print-found 482 result: 3 hits, all confirmed unrelated third parties 483 means: handle is reused; it is not a usable pivot for this person 484 next: pivot on the profile photo instead, not the handle 485 486 ## 2026-10-04T05:02Z — Wayback, target.example/about 487 query: CDX, matchType=prefix, from=20240101 488 result: no captures 489 means: nothing about absence of the page. The site serves a robots 490 directive that Wayback honoured at crawl time. 491 next: check archive.today and Ghostarchive before concluding anything 492 ``` 493 494 The `means:` line is the whole point and it is the line that gets dropped. "Zero results" is a fact 495 about a tool's coverage, and turning it into a fact about the world is the single most common 496 overreach in open-source work. A people-search service returning nothing means that service has 497 nothing. A registry returning nothing means that registry, under that spelling, on that date. 498 499 Write the `next:` line too. A negative result with a stated next step is a lead; a negative result 500 without one is just a gap you will rediscover. 501 502 ## Verification 503 504 Treat every claim as unverified until it is pinned to something independent: 505 506 - **Where** — does the imagery match satellite, street view, terrain? Are the shadows consistent 507 with the claimed time? See [Geolocation](/sheets/osint/geolocation). 508 - **When** — is the content older than the event it supposedly shows? Reverse image search first, 509 every time. See [Reverse Image Search](/sheets/osint/reverse-image-search). 510 - **Who** — does the account have history, or was it created last week? Do the same photos appear 511 on other profiles under other names? 512 - **What** — is the file original, or a re-encode of a re-encode? See 513 [Image & Video Forensics](/sheets/osint/image-video-forensics). 514 515 Two sources that both trace back to the same original post are one source. 516 517 ## Note-taking and case management 518 519 | Tool | What it does | Cost | 520 | --- | --- | --- | 521 | [Hunchly](https://www.hunch.ly/) | Browser extension that silently captures every page you visit during a case, with hashes and timestamps, and builds a searchable case file. | paid | 522 | [Obsidian](https://obsidian.md/) | Local markdown notes with backlinks. Good for entity-per-note investigations where the links between people matter as much as the people. | free | 523 | [Logseq](https://logseq.com/) | Outliner-style local notes with daily journals. Suits chronology-heavy work. | free | 524 | [Atlos](https://www.atlos.org/) | Purpose-built collaborative platform for visual investigations, with source tracking and multi-investigator review. | free | 525 526 ## Pitfalls 527 528 - **Confirmation lock-in.** Writing the conclusion first and collecting support for it. Ask what 529 evidence would prove you wrong, then go look for that. 530 - **Machine translation as fact.** A mistranslated verb changes a claim. Check anything load-bearing 531 with a speaker or a second engine. 532 - **Automated tools as authority.** Username enumerators, facial recognition and breach lookups all 533 produce false positives. They generate leads, not findings. 534 - **Losing the chain.** If you cannot say where a file came from and when you got it, you cannot 535 use it. 536 - **Forgetting the human cost.** Publishing that someone can be located has consequences for them. 537 Minimise what you expose beyond what the finding requires. 538 - **A VPN indicator is not a leak test.** The client says "connected" while the system resolver 539 still egresses from your ISP, and a browser can hand out your real address over WebRTC around any 540 tunnel. Measure the resolver and the browser separately, after every reconnect. 541 - **`socks5` where you meant `socks5h`.** The proxy carries the connection but your own resolver 542 does the lookup, so the hostname is logged locally even though the traffic was not. 543 - **Hardening a browser makes it more identifiable.** An unusual configuration and a long extension 544 list are a fingerprint. A stock browser in a disposable VM hides in a larger crowd. 545 - **Zero results is a fact about a tool, not about the world.** Record what the gap means and what 546 it does not, in the same note, at the time. 547 548 ## Worked example 549 550 One datum: a tip-off email naming a company, `Northgate Minerals Trading`, and asserting it is a 551 front. Nothing else. The question is not "is it a front" — that is a conclusion looking for support. 552 This is the first hour, before any collection, and what it buys you. 553 554 ```text 555 # 1. write the question down. 00-question.md 556 question: Does any public record link Northgate Minerals Trading to 557 [named entity] through a shared officer, address or owner? 558 standard: two independent primary sources per link, or it is "reported, unverified" 559 out of scope: the tipster's motive; family members of any officer 560 stop when: the question is answered either way, or three named registries 561 have been checked and returned nothing 562 exposure: registry APIs and archives only. NO requests to any Northgate- 563 controlled domain from an attributable address until step 6. 564 ``` 565 566 The exposure line is the decision that constrains everything after it. Having written it down, the 567 first four steps are forced: only sources that do not touch the target. 568 569 ```bash 570 # 2. establish the egress before anything leaves the machine 571 curl -s https://am.i.mullvad.net/json | jq '{ip, country, organization, mullvad_exit_ip}' 572 dig +short TXT o-o.myaddr.l.google.com @ns1.google.com 573 # web-visible IP: a VPN exit, country NL 574 # resolver egress: 203.0.113.x — the SAME ISP range as the host, not the VPN 575 ``` 576 577 DNS is outside the tunnel. Every hostname you look up is visible to the ISP, and will be whether or 578 not the HTTP request goes through the VPN. Fix that before step 3, not after: the resolver leak is 579 the one that persists, because it is a system setting and the VPN client reported "connected". 580 581 ```bash 582 # 3. isolation, and verify it from inside rather than trusting the launch 583 multipass launch lts --name research-014 --cpus 2 --memory 4G --disk 20G 584 multipass exec research-014 -- curl -s https://am.i.mullvad.net/json | jq -r .ip 585 multipass exec research-014 -- dig +short TXT o-o.myaddr.l.google.com @ns1.google.com 586 # both now report the VPN exit. Snapshot clean before use: 587 multipass stop research-014 && multipass snapshot research-014 --name pre-target 588 multipass start research-014 589 ``` 590 591 ```text 592 # 4. open the Hunchly case BEFORE the first search, not after 593 case name: 2026-014 (the case ID, never "Northgate") 594 selectors: Northgate Minerals Trading 595 Northgate Ltd 596 09876543 597 [officer surname, once known] 598 capture: ON — confirmed by the extension icon after the browser restart 599 ``` 600 601 Every page from here on is captured, hashed and timestamped whether or not you thought it mattered 602 at the time. That is the whole reason the case opens before the searching: the page you will need is 603 the one you skimmed on the way to something else. 604 605 ```bash 606 # 5. collect from sources that do not touch the target, and log the negatives 607 # (the registry and sanctions commands themselves live on the companies sheet) 608 printf '## %s — Companies House name search\nquery: "Northgate Minerals Trading"\nresult: 2 hits, gb/09876543 active + vg/1654321 (source dated 2019)\nmeans: a UK and a BVI company share the name. Not yet evidence they are related.\nnext: PSC filing on 09876543\n\n' \ 609 "$(date -u +%FT%TZ)" >> ~/cases/2026-014/40-negative/not-found.md 610 ``` 611 612 ```bash 613 # 6. the only step that touches the target, and only after deciding to 614 # headers first: it answers the hosting question without fetching the page 615 multipass exec research-014 -- \ 616 curl -sI -A 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0 Safari/537.36' \ 617 'https://northgate-minerals.example/' | head -12 618 # Server: cloudflare → the origin IP is not in this response, and a CDN 619 # operator now has a log line for this request. Noted in 90-log.md. 620 ``` 621 622 A `HEAD` from a VM behind a VPN, with a browser UA, after an explicit decision — rather than a 623 browser tab opened reflexively in hour one from your own address. The difference costs ninety 624 seconds and is the difference between the target knowing and not knowing. 625 626 ```bash 627 # 7. the artefacts, hashed and custody-noted at the moment they arrive 628 IN=~/cases/2026-014/20-raw; mkdir -p "$IN" 629 multipass transfer research-014:/home/ubuntu/ch-09876543.json "$IN/" 630 shasum -a 256 "$IN/ch-09876543.json" | tee -a "$IN/MANIFEST.sha256" 631 chmod 444 "$IN/ch-09876543.json" 632 printf '%s\t%s\n' "$(date -u +%FT%TZ)" 'retrieved CH filing 09876543 via API from research-014' \ 633 >> ~/cases/2026-014/90-log.md 634 ``` 635 636 ```text 637 # 8. the entity note, written as three separate claims. 10-entities/company-northgate.md 638 Established: number 09876543, UK, active. Source: CH API, 2026-10-04T05:12Z, 639 raw ch-09876543.json (sha256 4b1c8e...). 640 Reported/unverified: "a front". Single source, the tipster, who has an interest. 641 Not repeated as fact anywhere else in the case. 642 Ruled out: not Northgate Mining Ltd (07654321) — different officers, 643 different address, name similarity only. Checked 2026-10-04. 644 Open: beneficial owner behind the BVI parent. 645 ``` 646 647 After an hour the case holds: a written question with a stop condition, a fixed and verified 648 exposure posture, a continuous capture log, two registry records with hashes and custody notes, one 649 explicitly rejected lookalike company, and a negative-results entry that says what "two hits" does 650 and does not mean. 651 652 What you can assert: which sources were queried, from which exposure posture, what they returned, 653 and that the preserved registry artefacts still match the hashes recorded at acquisition. This is 654 an auditable collection record, not a finding that the tipster's allegation is true. 655 656 What none of that establishes: whether Northgate is a front. The tipster's claim is still exactly 657 one uncorroborated assertion, recorded as such, in a section of the note that cannot be mistaken 658 for a finding. The substantive work now moves to 659 [Company & Financial Records](/sheets/osint/companies-and-finance) — but it moves there on top of a 660 record that will survive someone attacking it, which is the only thing this sheet is for. 661 662 What would falsify it: an exposure check showing traffic left by the host rather than the research 663 VM, a gap in the action log, or a hash mismatch on either source artefact. Those failures do not 664 prove the allegation false; they make the collection record too weak to support later claims. 665 666 ## Broader catalogues 667 668 - [Foundational OSINT Tools](https://tools.osintnewsletter.com/tool-categories/foundational-osint-tools) 669 - [Bellingcat — Guides & Handbooks](https://bellingcat.gitbook.io/toolkit/resources/guides-and-handbooks) 670 671 672 ## More tools 673 674 Further tools for this area from the OSINT Newsletter Tools Library ([Foundational OSINT Tools](https://tools.osintnewsletter.com/tool-categories/foundational-osint-tools), [Language Translation OSINT](https://tools.osintnewsletter.com/tool-categories/language-translation-osint)), excluding those already listed above. 675 676 | Tool | What it does | 677 | --- | --- | 678 | [100 Search Engines](https://www.100searchengines.com/) | A simple search hub that brings multiple search engines and specialist search services into one place. | 679 | [2lingual Search](https://www.2lingual.com/) | A specialist search engine that lets investigators search the web in one language and discover results in another. | 680 | [527 Explorer](https://projects.propublica.org/527-explorer/) | A ProPublica database that lets you track political “527” organisations in the US i.e. groups that raise and spend money to… | 681 | [Apertium](https://en.wikipedia.org/wiki/Apertium) | An open-source rule-based machine translation platform. | 682 | [Authentic8 Silo Workspace](https://authentic8.com/) | A secure, cloud-native browser designed for conducting online investigations safely, anonymously, and at scale. | 683 | [Babylon](https://www.babylon-software.com/dictionary/) | Multilingual dictionary and translation tool. | 684 | [Copyleaks](https://copyleaks.com/) | AI-powered plagiarism and AI-content detection platform that compares text against web pages, academic sources and proprietary… | 685 | [Cover Your Tracks](https://coveryourtracks.eff.org/) | A privacy-testing tool from the Electronic Frontier Foundation that checks how identifiable your browser and device are online. | 686 | [DeepL Translator](https://www.deepl.com/en/translator) | Online translation tool that converts text from one language to another. | 687 | [DuckDuckGo](https://duckduckgo.com/) | A privacy-focused internet search engine and web browser. | 688 | [EarthPoint Convert](https://www.earthpoint.us/Convert.aspx) | A web-based conversion tool for transforming geographic coordinate data between formats. | 689 | [Excite Web Search](https://www.excite.com/) | A general-purpose internet search service to search for publicly available webpages and online information using keywords, names… | 690 | [Fagan Finder](https://www.faganfinder.com/) | A comprehensive search portal that brings together hundreds of search engines, specialist databases, and online resources in one… | 691 | [FindTheScam](https://findthescam.net/) | Checks whether a website looks legitimate or risky by reviewing WHOIS age, HTTPS/SSL, DNS, reputation, and scam-warning signals. | 692 | [Finger Printer](https://gonzosint.github.io/fingerprinter/) | An interactive browser fingerprinting dashboard that generates and displays a range of unique identifiers based on the behaviour… | 693 | [Forensic OSINT](https://www.forensicosint.com/) | A digital evidence capture and preservation platform to collect, document, and preserve online content. | 694 | [FreeSubtitles AI](https://subtitles.app/) | AI-powered transcription and subtitle generation tool that converts audio and video files into text and subtitles, with… | 695 | [GetProofAnchor](https://getproofanchor.com/) | A web-based tool that captures and preserves online content as verifiable digital evidence. | 696 | [Human or AI](https://humanizeai.com/human-or-ai/) | A game-style OSINT training tool that challenges you to spot the difference between real human profile photos and AI-generated… | 697 | [Known Agents](https://knownagents.com/) | A directory of AI bots and the associated information it has on each bot. | 698 | [Maltego](https://www.maltego.com/) | A visual link analysis and OSINT platform to discover, map, & understand relationships between people, organisations, domains… | 699 | [OpenGraph Intel](https://ogi.khas.app/) | A self-hosted OSINT and link analysis platform that enables investigators to collect, enrich, visualise, and analyse… | 700 | [OSINTracker](https://www.osintracker.com/) | A browser-based OSINT investigation management platform that helps organise entities, map relationships, visualise connections… | 701 | [Palette by OSINT Industries](https://app.osint.industries/palette) | A powerful graph visualisation platform with integrated native search for mapping and analysing OSINT data relationships. | 702 | [SIERRA](https://phantomhelix.com/download) | A local-first desktop investigation workspace for organizing evidence, entities, notes, timelines, and tool output in one case… | 703 | [Ubikron](https://www.ubikron.com/) | An OSINT investigation workbench that transforms your browser into a structured intelligence collection and analysis platform. | 704 | [Your social media fingerprint](https://robinlinus.github.io/socialmedia-leak/) | An OSINT awareness tool showing how much personal and behavioural data can be “leaked” from social media activity to understand… | 705 706 ## Sources 707 708 Both catalogues below are maintained by other people and are considerably larger than 709 this page. Use them as the canonical index; this sheet is a working route through them. 710 711 - [Bellingcat's Online Investigation Toolkit](https://bellingcat.gitbook.io/toolkit) — ~340 tools, each with its own 712 review page covering cost, difficulty, requirements and limitations. 713 - [OSINT Newsletter Tools Library](https://tools.osintnewsletter.com) — ~280 tools, organised by investigative goal. 714 715 Neither publishes a licence, so nothing here is copied from them: tool names, one-line 716 descriptions, cost flags and links are catalogue facts, and the method and commentary are 717 this site's own. See [credits](/credits).