Signature
Signalment signs every JSON export with its Ed25519 key, over a hash of everything in the file. Change, add or remove anything, anywhere in it, an audit entry or a key no hash covers, and the signature no longer matches. The page checks it with the key this site publishes at /api/research/export-key; verify-export.mjs takes a key with --key; a key it finds only at the server the file names shows where the file came from, not that Signalment wrote it, so it says so and fails unless --trust-origin confirms that server is yours. A file with no signature, from before signing began or from a server with no key, is checked every other way, and the check says that what lies outside the chain, and entries deleted from it, cannot be proven.
Records
Every record is first hashed over its study, subject, form and version, visit, observation date, sequence number, who entered it and when, its source and the values as entered, with the hash of the record before it. Change one answer, one date or one name in a first entry, and that record stops matching. Re-hash it to hide the change, and the next record no longer points at it.
Versions and signatures
A correction, a change of date or a signature is a new version of the record, hashed over its values, date and status, who made it, when and why, and the version before it. The record as exported must be its latest version, and every signature is bound to the exact version it signed. Change a current value and the record no longer matches its versions.
Audit trail
Every create, change, signature, export and sign-in is an entry with its actor, before and after values, reason and time, hashed together with the entry before it. Corrections never overwrite: they add an entry, and the record keeps every version. A file without owner identities replaces an owner’s name, and a blinded file takes out an animal’s arm and the block size or sizes (and the kit counts that give them away); those entries are counted and linked, not recomputed, and the identifying or unblinded export checks them in full. An entry that may name an arm or the block size is followed by its seal, a hash of everything in it but what was taken out, so the rest of a redacted entry is still recomputed; entries from before seals began are counted apart and said to be linked, not proven, and a file with no seal at all is said to have none. In a blinded file the whole file is searched: any value under a key that names an arm, an arm label or the allocation, or an arm label anywhere, fails it. A key is read as it looks, so full-width letters and Cyrillic or Greek lookalikes count as the Latin letters they resemble; any key that is not plain ASCII fails a blinded file, since the server writes none; and so does a list that pairs one-letter codes with labels or names, wherever it sits outside the protocol’s own text. An arm written as free text under some other name is not something a file can be held to.
Animals, queries and the allocation
The audit trail is replayed in order. Each animal’s status, site, arm (in a file that carries arms) and end, each query’s question, answer and status, and, in the unblinded export, each allocated arm, committed to by its hash when it was assigned, must match what the trail recorded. Swap two arms or rewrite a withdrawal, and the line that checks it fails.
The lock
Locking a study signs one fingerprint over the current version of every record, and every consent and signature made before it. A record added, removed or re-hashed after the lock changes the fingerprint, and the export says so. A lock followed by an unlock is history: its signature and audit entries are still checked, and the fingerprint is recomputed only for a study exported while locked.
Not checked: subjects’ signalment (breed, age, weight), signers’ email addresses, form definitions, data checks and coding, which the audit trail does not record in full. The check names them on a line of its own.
Where the export comes from: a study’s Settings, then Exports, then JSON bundle. Owner names and contact details are left out unless an identifying export is asked for, with a reason, and that request is on the audit trail too.