Verify

Verify an export.

Every Kestrel export ZIP contains a signed manifest. Verify the signature yourself — no trust required.

Verify in your browser

Drop an export. Get an answer.

You don't need to install anything or write any code. Drop a Kestrel export ZIP into the box below and your browser will check the Ed25519 signature and recompute the SHA-256 hash of every file inside. The file stays on your device — nothing is uploaded to Kestrel.

Drop an export ZIP here, or .

Verification runs entirely in your browser. The file never leaves your device.

Requires a modern browser with WebCrypto Ed25519 support (Chrome 113+, Firefox 129+, Safari 17+). If yours doesn't, the Node.js instructions further down the page do the same work.

What's in an export

Two files carry the integrity guarantee.

A Kestrel export is a standard ZIP archive. Alongside the case documents, evidence, submissions, and timeline, every archive contains two files at the root that together allow anyone to verify the archive was produced by Kestrel and has not been altered since.

  • /manifest.json — a JSON document listing every file in the archive with its SHA-256 hash, the dispute reference, the tenant, the exporting user, a unique nonce, and an expiry date.
  • /manifest.sig — a versioned Ed25519 signature. The current scheme is v2:<kid>:<base64> where kid identifies the signing key and the signature covers the SHA-256 digest of the manifest bytes. Older exports use the v1: scheme (signature directly over manifest bytes). Both are verified automatically.

To establish end-to-end integrity you recompute the SHA-256 of each file, compare against the manifest, then verify the manifest signature against our published public key. If any step fails, the archive has been tampered with or was not produced by Kestrel.

Public key

The Kestrel signing key.

Save the following as kestrel-public.pem. This is the public half of the Ed25519 key pair used to sign every export manifest. The private half never leaves our servers.

-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAJnntHlRo06pWnrIxKBLIV87/G+xWEZQIucLd2ye1g8I=
-----END PUBLIC KEY-----

Verify it yourself

The same check in Node.js.

If you'd rather not trust a browser — or want an auditor to run the check on their own machine — the following script does exactly what the widget above does, using only Node's built-in node:crypto module. No dependencies, no Kestrel tooling required.

import { readFileSync } from 'node:fs'
import { createHash, createPublicKey, verify } from 'node:crypto'

const publicKey = createPublicKey({
  key: readFileSync('./kestrel-public.pem'),
  format: 'pem',
})

const manifestBytes = readFileSync('./manifest.json')  // raw bytes from the ZIP
const sigFile = readFileSync('./manifest.sig', 'utf8').trim()

let dataToVerify, signature

if (sigFile.startsWith('v2:')) {
  // v2 — signature covers SHA-256(manifest_bytes) as a hex string
  const [, kid, b64] = sigFile.split(':')
  console.log('Key ID:', kid)
  signature = Buffer.from(b64, 'base64')
  const digestHex = createHash('sha256').update(manifestBytes).digest('hex')
  dataToVerify = Buffer.from(digestHex, 'utf8')
} else if (sigFile.startsWith('v1:')) {
  // v1 — signature covers the raw manifest bytes directly
  signature = Buffer.from(sigFile.slice(3), 'base64')
  dataToVerify = manifestBytes
} else {
  throw new Error('Unknown signature version')
}

const ok = verify(null, dataToVerify, publicKey, signature)
console.log(ok ? 'Signature valid' : 'Signature INVALID — do not trust this export')

Critical detail: read the manifest as raw bytes, not as a parsed and re-serialised JSON object. The signature is computed over the exact bytes written to the archive — any difference in whitespace, key order, or encoding would cause verification to fail even on an untampered file.

If verification fails

Do not trust the export.

If any step of verification fails, do not trust the export. Contact us at hello@onkestrel.com with the dispute reference and we will investigate.