Keeping an OSINT Evidence Log — Fields, Capture, Retractions

A practical guide to the evidence log behind an OSINT finding: the minimum fields per entry, how to capture and hash a source before you analyse it, how to log a false lead and retract it, and how the log becomes the report. With a worked example and a downloadable template.

An evidence log is an append-only table in which every row ties one observation to a preserved, hashed copy of its source and to the inference you drew from it. It exists so that three things stay possible after the investigation is over: someone else can verify each step, someone else can reproduce it, and you can retract a step without the rest of the finding collapsing with it.

The minimum per entry: an ID, the capture time in UTC, the source URL, an archived copy, the hash of the file you saved, what you observed, what you inferred, how confident you are, what would falsify it, and a status. That’s the whole format. The rest of this post is how to fill those fields without fooling yourself, a worked example with a false lead that gets logged, tested and retracted, and how the log turns into a report.

Template: evidence-log-template.csv for a spreadsheet, evidence-log-template.md as a printable worksheet with the field explanations. Both carry three rows from the example below.

What the log is for

A finding is a chain. “The photo was taken in Vessenby on 3 March” rests on a shop sign, a council notice, a river gauge and a shadow, and each of those rests on a web page that may change or vanish next week. Without a log, the chain lives in your head and in forty browser tabs. With one, every link is written down with the copy it came from.

Three jobs, in order of how often they save you:

  • Verification. A reader, editor or reviewer can check any single claim against the preserved source, without trusting your summary of it. The hash shows the file they’re looking at is the file you looked at.
  • Reproducibility. Queries, tools and settings are in the entry, so someone can run the step again and see whether they get the same result. If they don’t, the entry tells them where the paths split.
  • Retraction. When a step turns out wrong, you can see exactly which conclusions depended on it. Without a log you have to reconsider everything. With one you follow the references.

The third one is the reason to keep the log while you work rather than reconstructing it afterwards. A reconstructed log only contains the steps that worked. The steps that didn’t work are the ones that tell a reader which alternatives you already ruled out.

The minimum fields

FieldWhat goes in itWhy it’s there
idSequential, never reused: E-001, E-002 …Everything else refers to entries by ID: files, the report, other entries
captured_utc2026-03-03T09:31:12ZPages change. The time says which version you saw. UTC, so it can’t be misread across time zones
source_urlThe exact URL, not the site’s front pageThe pointer to the live source, if it still exists
archive_urlWayback Machine and/or archive.today snapshot, or “failed: reason”An independent third-party copy. Yours can be doubted, theirs has its own timestamp
local_fileE-001_photo.jpgYour own copy, named by entry ID
sha256Hash of the local fileProves the file hasn’t changed since capture
observationWhat is literally in the source. No interpretationSeparates the evidence from you
inferenceWhat you conclude from it, and whyThis is the part a reader has to be able to follow
confidencelow / medium / high, with a one-line reasonTells the report how hard it can lean on this entry
falsifierThe concrete observation that would prove the inference wrongTurns the entry into something you can test
statuscaptured / open / supported / dead-end / retractedWhere the entry stands now
next_actionThe query, tool or check you’ll run nextCloses the loop from observation to action
refsIDs of entries this depends on or supersedesThe dependency graph you need for retractions

The template adds local_file, next_action and refs to the ten fields named above. None of them is strictly required, but without refs a retraction means rereading the whole log.

Two fields do most of the work. Observation and inference in separate columns is the rule that catches most errors, because it forces you to notice when you wrote down a conclusion and called it a fact. “Photo shows the Old Mill footbridge” is an inference. “Photo shows a three-span steel footbridge with green railings” is an observation. The falsifier is the other one. If you can’t name what would prove an inference wrong, you can’t test it, and an untested inference shouldn’t go in the report.

For confidence I use three levels and write the reason next to them:

  • high: two independent sources agree, or one primary source leaves no alternative;
  • medium: one source, or several that could share an origin;
  • low: plausible, consistent with the material, not tested yet.

No percentages. A number like 80 % suggests a precision the method doesn’t have.

Capture before you analyse

The order matters: capture, hash, archive, then look closely. A source you analysed but didn’t preserve is a claim, not evidence. The Berkeley Protocol on Digital Open Source Investigations names the target URL, the HTML source and a full-page capture as the minimum standard for collected web content, and lists embedded media, metadata, context, collection data and a hash value as the full set (chapter VI.C, paragraph 155). For a solo analyst that translates to:

ID=E-001
URL='https://social.example/p/88412'

# 1. Time of capture, UTC, RFC 3339 style. Check the clock is NTP-synced first
timedatectl | grep 'synchronized'
date -u +%Y-%m-%dT%H:%M:%SZ

# 2. The page and its response headers (the server's Date: header is a second timestamp)
curl -sS -D "${ID}_headers.txt" -o "${ID}_page.html" "$URL"

# 3. Embedded media as served, not as a screenshot
curl -sS -o "${ID}_photo.jpg" 'https://social.example/media/88412.jpg'

# 4. Hash everything the entry points to, append to one hash list
sha256sum "${ID}"_* >> hashes.sha256

# 5. Is there already a snapshot?
curl -s "https://archive.org/wayback/available?url=$URL"

Then submit the URL to the Wayback Machine’s Save Page Now (web.archive.org/save) and to archive.today, and paste the resulting snapshot URLs into archive_url. Open each snapshot and check it shows the content, not a login wall or a cookie banner. If a capture fails, log that too: “archive.today: failed, login wall” is information. It tells the next person not to count on that copy.

Practical notes, each one learned the hard way:

  • Save the file as served. A screenshot of a photo is a new image with new pixels and no metadata. Download the media file itself and take a screenshot of the page in addition, with the system clock visible, for context.
  • If curl gets a login wall, save from the browser (Save Page As, complete) and write in the entry that the method changed. Don’t silently mix methods.
  • Archives have gaps. archive.today’s own FAQ says it doesn’t save video or sound. The Wayback Machine’s Save Page Now saves one page with its images and CSS, not the pages it links to. Video needs its own download and its own hash.
  • Name files by entry ID. E-004_2019-article.html finds its row. download (3).html doesn’t.
  • Never edit a saved file. Crops, enhancements and annotated copies are new files with new hashes, logged as derived from the original in refs.
  • Check fixity before you publish. sha256sum -c hashes.sha256 confirms nothing changed on disk. A mismatch means either the file was touched or the hash list was, and both need a note.

Dead ends and retractions

The log is append-only. Once an entry is written, its observation, inference and confidence are frozen. Two things may change afterwards: status and refs. Every change of status gets its own entry that says why. This sounds bureaucratic until the first time you need to know what you believed on Tuesday and why you stopped believing it.

  • A dead end is a step that produced nothing usable: no EXIF, no search hits, a deleted account. Log it with status dead-end and one sentence on what you tried. It’s short, and it stops you (and anyone reproducing the work) from running it a second time.
  • A retraction is an inference that was tested and failed. The original entry stays, status retracted, with a new entry that states the reason and which evidence broke it. Then follow refs: every entry that depended on the retracted one is now open again.

A retraction is not a failure of the log, it’s the log doing what it’s for. The correction at the top of my Holehe writeup is the public end of the same process: one inference (key date equals account date) fell, the flag didn’t, and the log made it possible to say exactly which sentence changed.

Worked example: the Old Mill footbridge

Everything in this example is invented: the town of Vessenby, its river, the shops, the council, the accounts and every URL. The .example domains are reserved for documentation and don’t resolve. Hashes are placeholders, shortened here and full in the CSV. There is no photo. The material is described in text, which is also how a log describes it.

The task. On 3 March 2026 at 09:02 UTC, an account I’ll call Account A posts a photo with the caption “Old Mill footbridge in Vessenby going under right now”. It spreads. The question: does the photo show Vessenby, and was it taken on 3 March 2026?

The material. One JPEG, 1600×1200, as served by the platform. Brown water up to the deck of a three-span steel footbridge with green railings. On the left bank, a row of shops, one fascia reading PELLOW & DAUGHTERS · HARDWARE. Behind the bridge, a church with a square tower. Trees bare. Sun low and in front of the camera. Shadows of the railings fall toward the lower left of the frame.

What I didn’t have. No EXIF, no GPS, no second photo, no contact with Account A.

Result up front. The photo shows the Old Mill footbridge in Vessenby and was taken on 3 March 2026 between 06:50 and 09:02 UTC. Place: high confidence. Date: medium-high. Getting there took one false lead that looked like a debunk. All times below are UTC, which is also Vessenby’s local time in March.

E-001 · Capture of the post

  • captured_utc: 2026-03-03T09:31:12Z
  • source_url: https://social.example/p/88412
  • archive_url: Wayback snapshot https://web.archive.org/web/20260303093305/https://social.example/p/88412 (checked: shows post and photo). archive.today: failed, login wall.
  • local_file / sha256: E-001_page.html, E-001_photo.jpg (48a9b525…aeec2384)
  • observation: Post time 09:02 UTC, caption as above, one image 1600×1200.
  • inference: None yet. This is the claim under test, preserved.
  • status: captured

E-002 · Metadata

  • observation: exiftool -a -G1 E-001_photo.jpg returns JFIF and ICC profile tags only. No DateTimeOriginal, no GPS, no camera model.
  • inference: The platform strips metadata on upload. The absence is expected and says nothing about the photo.
  • status: dead-end. Nothing further to do with this file’s metadata.

E-003 · False lead: “it’s a 2019 photo”

  • captured_utc: 2026-03-03T09:58:40Z
  • source_url: https://news.example/vessenby-flood-november-2019
  • archive_url: Wayback snapshot, checked.
  • local_file / sha256: E-003_article.html, E-003_photo.jpg (95bf2da4…6a73e3e97)
  • observation: Reverse image search on E-001_photo.jpg returns a regional news article from 14 November 2019, “Vessenby flood”. Its photo shows the same footbridge from nearly the same spot, water at deck level, church tower behind.
  • inference: The viral photo is recycled from the 2019 flood.
  • confidence: medium. The images look near-identical at thumbnail size. I haven’t compared them in detail.
  • falsifier: Any fixed detail that differs between the two photos, or that postdates November 2019.
  • next_action: Compare both at full resolution, side by side, detail by detail.
  • status: open → retracted by E-005

This is the entry most analysts would have turned into a post: “Old photo, recycled from 2019.” It has a source, a date and a matching scene. What it didn’t have yet was a test.

E-004 · Side-by-side comparison

  • refs: E-001, E-003
  • observation: Three fixed differences. (1) Railings are grey in 2019, green in the viral photo. (2) The shop fascia that reads PELLOW & DAUGHTERS · HARDWARE in the viral photo reads VESSENBY CYCLES in 2019. (3) The church tower sits left of a lamp post in 2019 and right of it in the viral photo: the camera stood a few metres further along the bank.
  • inference: Two different photographs of the same scene, taken from a common vantage point (a public riverside path). The reverse search matched the scene, not the file.
  • confidence: high. Three independent details, all fixed structures.
  • falsifier: Evidence that the 2019 image was edited, or that railings and fascia were repainted back and forth.
  • status: supported

E-005 · Retraction of E-003

  • refs: E-003, E-004
  • observation: E-004 shows three fixed differences between the photos.
  • inference: E-003 is wrong. It took “same place” for “same photo”. What survives from it: the 2019 article places the footbridge in Vessenby, which supports the location claim independently of Account A.
  • status: E-003 set to retracted.

The false lead cost twenty minutes and paid for itself: it confirmed the location from a source that had nothing to do with the viral post, and the two details that broke it (railing colour, shop fascia) became the handle for dating.

E-006 · When did the railings turn green?

  • captured_utc: 2026-03-03T10:24:05Z
  • source_url: https://council.example/notices/old-mill-footbridge-refurbishment
  • archive_url: Wayback snapshot, checked.
  • local_file / sha256: E-006_notice.html (de2a223e…405130de0)
  • observation: Council notice: refurbishment of the Old Mill footbridge completed 14 August 2025, “railings repainted in heritage green”.
  • inference: The photo was taken after 14 August 2025.
  • confidence: high. Primary source, specific date, and the green railings in E-001 match.
  • falsifier: A photo of green railings dated before August 2025.
  • next_action: Search dated imagery of the bridge between 2019 and 2025. Result: grey railings in every dated image found, green in none before the notice.
  • status: supported

E-007 · Shadow direction

  • refs: E-001, E-003
  • observation: From the riverside path the camera looks roughly east across the river. Railing shadows fall toward the camera and to the left, i.e. toward the north-west.
  • inference: Sun in the south-east, so morning. With the bridge’s coordinates in SunCalc, early March, the sun stands south-east between about 08:00 and 10:30. An afternoon sun would throw shadows away from the camera.
  • confidence: medium. The camera bearing is estimated from the 2019 article’s map, and shadow angles on a railing are rough.
  • falsifier: A camera bearing off by more than about 60°.
  • status: supported (bounds time of day only)

E-008 · River level

  • captured_utc: 2026-03-03T10:51:37Z
  • source_url: https://water.example/gauges/vessenby-old-mill?from=2025-08-14
  • archive_url: Wayback snapshot failed (dynamic chart). Saved the CSV export instead.
  • local_file / sha256: E-008_gauge.csv (64191870…5e11b4b205)
  • observation: The gauge next to the bridge. Since 14 August 2025 the level has reached deck height (2.40 m, per the refurbishment notice) once: 3 March 2026, 06:50 to 11:20.
  • inference: Water at deck level plus green railings means the photo was taken on 3 March 2026, after 06:50. The post at 09:02 is the upper bound.
  • confidence: medium-high. One gauge, one source, but it’s the primary one. The window agrees with E-007.
  • falsifier: Another deck-level event in the gauge data after August 2025, or a gauge that doesn’t reflect the level at the bridge.
  • status: supported

Verdict

The place rests on E-001, E-004 and E-005: the shop fascia, the bridge and an independent 2019 source. The date rests on E-006 and E-008, with E-007 as a consistent but weaker third check. The moment it was decided was E-008: green railings narrow the date to after August 2025, and the gauge shows exactly one flood since then.

What the log doesn’t establish: who took the photo, and whether Account A is the photographer. Neither was part of the question, and the report says so instead of leaving the reader to assume it.

From log to report

The report is a view on the log, not a separate piece of writing:

  1. Every factual sentence cites entry IDs. “The railings were repainted green in August 2025 (E-006).” A reader who doubts the sentence goes to the entry, then to the archived source, then checks the hash.
  2. A conclusion is never more confident than the weakest entry it depends on. If the date hangs on a medium-high gauge reading, the date is medium-high, however solid the rest looks.
  3. Retracted entries and dead ends go into a short “Ruled out” section. One sentence each: “A 2019 photo of the same scene is a different photograph (E-003, retracted by E-005).” This is what stops the next reader from publishing the same debunk.
  4. Scope limits are stated. What the evidence doesn’t show gets a sentence, as in the verdict above.
  5. The appendix is the log itself: the table, hashes.sha256, and the list of archive URLs.

The report paragraph for the example, written from the log:

The photo shows the Old Mill footbridge in Vessenby (E-004, E-005) and was taken on 3 March 2026 between 06:50 and 09:02 UTC (E-006, E-008; shadow direction consistent, E-007). Confidence: high for place, medium-high for date. Ruled out: a visually similar 2019 photo is a different photograph of the same scene (E-003, retracted in E-005). Not established: who took the photo.

Takeaways

  • Log while you work. A log written afterwards has no dead ends and no retractions, which means it can’t tell a reader what you excluded.
  • Observation and inference in separate fields. Most errors are an inference that got written down as an observation.
  • Write the falsifier before you look for it. It’s the difference between testing a lead and collecting support for it.
  • A near-duplicate from a reverse image search tells you the scene exists elsewhere. It doesn’t tell you it’s the same photograph until the fixed details agree.
  • A retracted entry stays in the log. What it cost and what it revealed are part of the finding.

Sources