BagIt: a self-verifying case file

A digital case file that can only be verified with the software that created it is not a case file: it is a hostage. BagIt, standardised as RFC 8493, solves the problem in the oldest and soundest way: by putting everything in the clear, in text files, with hashes anyone can recompute.

The idea: a folder that checks itself

A bag is an ordinary folder — or a ZIP — containing a strict convention. The real data lives in a data/ subfolder, the payload. Around it sit a few text files declaring what should be there, and with which digest.

No binary format, no database, no proprietary index. A bag opens in a text editor, and its digests can be recomputed with the command-line tools of any operating system. In twenty years it will still work, because there is nothing in it that can stop working.

acquisition-2026/ data/ exhibit.e01 photos/IMG_0431.jpg the payload manifest-sha256.txt hash of every file in data/ tagmanifest-sha256.txt hash of the other text files bagit.txt bag-info.txt version and encoding · metadata and Payload-Oxum inside manifest-sha256.txt a72c479e…2378 data/exhibit.e01 7f82fc11…f434 data/photos/IMG_0431.jpg Two columns, plain text. The very format that sha256sum -c reads on any Linux. inside bag-info.txt Payload-Oxum: 4218301.2 Bagging-Date: 2026-07-11 4,218,301 bytes across 2 files. The cheapest check there is: two numbers, zero hashing.
Everything is text. The payload lives in data/; every other file exists to declare what should be inside data/, and with which digest.

The four checks

1. Payload-Oxum — the one-second check

In bag-info.txt there is a line like Payload-Oxum: 4218301.2: the total number of bytes in the payload, a dot, the number of files. Checking it takes a scan of file sizes, without reading a single byte of content.

It is no cryptographic guarantee — anyone can swap a file for another of the same length. It is a filter: if the Oxum does not match, the bag is broken and there is no point spending twenty minutes rehashing an entire disk.

2. Completeness

Does every file listed in the manifest actually exist? And, just as importantly, does any file inside data/ exist that is not in the manifest? The second case is the treacherous one: a foreign file slipped into the payload alters no declared digest, and would go unnoticed if only hashes were checked.

3. Payload checksums

Every file in data/ is rehashed and compared against the manifest. Probatio supports twelve manifest algorithms: MD5, SHA-1, SHA-224, SHA-256, SHA-384, SHA-512, all four SHA-3 variants, BLAKE2b and BLAKE2s. A bag may carry more than one, and then all of them must match.

4. Tagmanifest

And who checks the checker? manifest-sha256.txt is a text file like any other: if someone modifies an exhibit and updates the corresponding line in the manifest, every checksum matches.

Hence the tagmanifest: a second manifest holding the digests of the tag files, manifest included. Modifying an exhibit now requires altering three files in cascade, and the last one — the tagmanifest — is covered by nobody.

The outer seal, and transitivity

The tagmanifest is the last link. Seal it, and you have sealed everything.

data/ the exhibits covered by manifest hashes of the exhibits covered by tagmanifest hash of the manifest RFC 3161 timestamp + Ed25519 signature One timestamp, on the tagmanifest alone, dates the entire case file. Changing an exhibit forces a change to the manifest, which forces a change to the tagmanifest, which is timestamped. Timestamping fifty thousand files would cost fifty thousand tokens. Timestamping one is enough.
The transitivity of hashes is why a case file of a thousand exhibits is sealed with a single token. It is the same principle as a Merkle tree.

The CERTO/LOCUS layer

When a manifest.json file appears inside data/, Probatio recognises a bundle produced by CERTO or LOCUS and runs an additional validation on top of the BagIt one:

  • Ed25519 signature over the manifest, binding the case file to the tool that created it.
  • RFC 3161 timestamps as .tsr sidecars, including — when present — the one on tagmanifest-sha256.txt, which is the outer seal described above.
  • CASE/UCO graph in JSON-LD: a formal description, in an international standard, of what was acquired, when, with which tool, and in what relation to the other exhibits.

This layer is additive. A CERTO bundle remains a valid BagIt bag, openable and verifiable by anyone who has never heard of CERTO.

Why a 2008 standard is still the right choice

BagIt was born at the Library of Congress to move digital archives between institutions sharing no software. The requirement was precisely ours: the receiver must be able to verify without trusting the sender.

It is the requirement of adversarial proceedings. The opposing party should not have to install your software to check your case file. They should be able to open a folder, read a text file, and recompute hashes with tools they already have.

A proprietary format, however secure, merely moves the problem: you must trust the verifier. A bag does not. The verifier is sha256sum.

In practice

  1. Never touch a bag «just to look». Opening a file can change its dates; some formats rewrite themselves on open. Work on a copy.
  2. If the Oxum does not match, stop. The bag is incomplete or corrupt, and rehashing is wasted time.
  3. A foreign file in data/ invalidates the bag even if every checksum matches. That is not pedantry: it is the point.
  4. Prefer two manifest algorithms, say SHA-256 and SHA3-256. It costs a single read.
  5. Timestamp the tagmanifest, not the individual exhibits. One timestamp, one case file.

A bag is not a clever container. It is an honest one: it declares what it holds, and hands you the means to contradict it. In forensics, nothing else is needed.