# How to verify this evidence package

Capture ID: `4f868e92-f12f-4fb7-bb5a-cb096b82eac8`
Captured URL: https://permavault.xyz
Captured at (UTC): 2026-07-23T08:33:19.462Z

This package is designed to be verifiable **without Permavault**. Every check below uses
standard, widely available tools: `sha256sum` (or `shasum`), `openssl` 3 or later, and
optionally `curl`. If Permavault disappeared tomorrow, everything in this package would
still verify, and the content itself would still be retrievable from the Arweave network.

## Quick start

On macOS or Linux, open a terminal in this folder and run:

```
bash verify.sh
```

On Windows, open PowerShell in this folder and run:

```
powershell -ExecutionPolicy Bypass -File verify.ps1
```

Add `--offline` (or `-Offline` for PowerShell) to skip the Arweave network check.

## One tool requirement: OpenSSL 3

The signature check needs OpenSSL 3, because the signature uses Ed25519, which older
builds cannot verify. macOS is the common snag: the `openssl` that ships with a Mac is
LibreSSL, which cannot run this check. Install OpenSSL 3 with Homebrew:

```
brew install openssl
```

Then run verify.sh again; it finds the Homebrew copy automatically. On Linux, the
`openssl` from your package manager is almost always OpenSSL 3 already. On Windows,
install OpenSSL 3 (for example `winget install ShiningLight.OpenSSL.Light`) or use the
copy bundled with Git for Windows.

If no suitable OpenSSL is available, the scripts do NOT fail the package: they skip just
the signature check, print a warning explaining how to complete it later, and still
report the remaining checks honestly. A missing tool is never reported as tampering.

## What is in this package

| File | Role | SHA-256 | Arweave transaction |
| --- | --- | --- | --- |
| `permavault.xyz.wacz` | primary | `ad093d193f31d4e6240fc06b6a12a752a9b7105b37d1a7178e0c9b76e268762b` | [7iFluxfrjGu-BN6mdnfxUqQCClB6z9rYNIjDMYokF3I](https://ar-io.dev/7iFluxfrjGu-BN6mdnfxUqQCClB6z9rYNIjDMYokF3I) |
| `screenshot.png` | screenshot | `b08ad419bbeb4ba3a378685546f9dddd558960a0361deffec93b1a721a71b8f1` | [xtS-Q87_q8RqvQ-ZAo-9MdDYqsNTLSzOJorxhd5AvTw](https://ar-io.dev/xtS-Q87_q8RqvQ-ZAo-9MdDYqsNTLSzOJorxhd5AvTw) |
| `pdf-snapshot.pdf` | pdf-snapshot | `9c3a7ce16206b3ecc4a954c2427a9585df95f4a5d1ce992ba3b59ccca0970d91` | [gHonz5j2PGjPWa6f6ceQ7FPYdqDL4nXTeKssqS3FaOA](https://ar-io.dev/gHonz5j2PGjPWa6f6ceQ7FPYdqDL4nXTeKssqS3FaOA) |
| `exhibit.pdf` | exhibit-pdf | `f06a34a4f654d23e5cf3c469c5af58bb9f77552eafd518fa5cb4bf692968981a` | [sgmQXG-5l33jHlxotv8adKEZKT0GfVY_26qTgArOT2c](https://ar-io.dev/sgmQXG-5l33jHlxotv8adKEZKT0GfVY_26qTgArOT2c) |
| `capture-metadata.json` | metadata-sidecar | `8acd3e9ca3f2f72ef2b1fc6c5712cf95da5d34334b50da6fb9924c22e56ced15` | [m03sNQtsQixRF-j5GGn627vbNhd799Ck73ta8hnIvdQ](https://ar-io.dev/m03sNQtsQixRF-j5GGn627vbNhd799Ck73ta8hnIvdQ) |

Plus the verification files themselves: `manifest.json` (the canonical, signed list of
every file above), `manifest.sig` (a detached Ed25519 signature over the exact bytes of
manifest.json), `manifest.sig.json` (signature metadata), `permavault-public-key.pem`
(the signing public key), this guide, and the two scripts.

## The forensics block in capture-metadata.json

The `forensics` block in `capture-metadata.json` records the transport facts
observed while the capture was being made. It is part of the sealed package, so its
hashes are covered by the manifest like every other file. Fields are omitted when they
were not observed. What each field means:

- `redirectChain`: the redirect hops the capture followed for the page itself, in
  order, each with its URL and HTTP status code. The last entry is the final page load.
  This covers HTTP-level redirects only; client-side JavaScript or meta-refresh
  redirects produce no HTTP redirect, so they are not part of the chain.
- `httpStatus`: the HTTP status code of the final page response.
- `responseHeaders`: the response headers of the final page response. Repeated
  headers (for example Set-Cookie) are combined into a single comma-separated value.
- `resolvedIps`: the server IP addresses the capture machine connected to for the
  document page loads observed during capture. These are direct observations from the
  capture sockets.
- `dnsAnswers`: DNS answers (A and AAAA records) for the final page host from the
  capture server's system resolver shortly after the page load. This is a lookup made
  alongside the capture; the browser's own resolution during the capture may have
  differed, so the connected IPs in `resolvedIps` are the stronger evidence.
- `captureServerIp`: the public egress IP of the Permavault capture server itself,
  not the website's.
- `userAgent`: the browser user agent the capture presented to the site.
- `softwareVersions`: versions of the capture software stack (Permavault, Scoop, Node).
- `tlsCertChain`: the TLS certificates observed for the site's hosts, each with its
  SHA-256 fingerprint, subject, issuer, and validity window.


## Legal certificate documents

This package also contains the Legal Certificate tier documents: `declaration.pdf`
(a fill-in FRE 902(13)/(14) declaration template, prepared for review by counsel),
`custody-appendix.pdf` (the capture's chain of custody, rendered from its hash-chained
event log), `custody-log.json` (that event log in raw, machine-readable form), and
`process-description.pdf` (a versioned description of the capture system and process).

These four files are **derivative documents**: they are generated after the capture is
sealed, so they are not listed in `manifest.json` and are not covered by the Ed25519
signature. That does not make them unverifiable. The artifact hashes they cite appear
in `manifest.json` (which IS signed); the manifest's own SHA-256 that they reference is
exactly what the detached signature covers (check 1 above); and `custody-log.json` can
be checked on its own: each entry's `entryHash` is the SHA-256 of the entry's canonical
content plus the previous entry's hash, so the whole chain can be recomputed from the
file alone.

## What each check proves

1. **Signature check.** The Ed25519 signature in `manifest.sig` verifies over
   `manifest.json` using the public key. This proves the manifest, and therefore the
   list of file hashes, has not been altered since Permavault sealed the capture.
2. **File hash check.** Each file's SHA-256 matches its entry in `manifest.json`.
   Combined with the signature check, this proves the files are byte-identical to what
   was sealed at capture time.
3. **Arweave check (online).** Downloading the primary artifact straight from the
   Arweave gateway and comparing hashes proves the content itself, not just the proof,
   survives on the permanent storage network, independently of Permavault.
4. **Key cross-check.** The public key in this package can be compared against two
   independent locations (below), so a tampered package cannot simply swap in its own key.
5. **Bitcoin timestamp (only if `manifest.ots` is present).** An OpenTimestamps proof
   anchoring the manifest in the Bitcoin blockchain. Verify with the open-source
   `ots` client: `ots verify -f manifest.json manifest.ots`.
6. **RFC 3161 timestamp (only if `manifest.tsr` is present).** A timestamp token from
   a time-stamping authority over the manifest's SHA-256, verifiable with OpenSSL
   alone. The accompanying `eidas_validation.json` carries the authority's
   certificate chain and an OCSP response so the check works fully offline:

   ```
   # extract the certificate chain shipped with the token
   jq -r .certificateChainPem eidas_validation.json > tsa-chain.pem
   # verify the token covers the manifest hash
   openssl ts -verify -digest <manifest sha256> -in manifest.tsr -token_in -CAfile tsa-chain.pem
   # inspect the token (issuer, time, serial)
   openssl ts -reply -in manifest.tsr -text
   ```

   (If your OpenSSL rejects `-token_in`, run the same command without it. TSAs differ
   in whether they return a bare token or a full response; verify.sh tries both forms.)

## Verify the signing key independently

Key id: `98ae373420471711` (the first 16 hex characters of the SHA-256 of the key's DER bytes)

The same public key is published at:

- https://app.permavault.xyz/.well-known/permavault-signing-key.json
- https://ar-io.dev/y2rHSglMfqfkkVDT8OI3bv47QkJgUZ5Suw00SAumK_4 (permanent Arweave copy)

Confirm the key id yourself:

```
openssl pkey -pubin -in permavault-public-key.pem -outform DER | sha256sum
```

The first 16 characters of the output must equal the key id above.

### If a key was retired early (validUntil)

The published key document may carry a `validUntil` on a key: the instant Permavault
retired it early, for example after a suspected compromise. The semantics, stated in
the document itself: signatures made by that key after this time must not be trusted,
and a manifest under that key remains valid only with a trusted timestamp attesting a
time before the retirement. The timestamp does not have to come from Permavault: a
confirmed Bitcoin (OpenTimestamps) block time or an RFC 3161 token's genTime both
qualify, and either one suffices. This package's own anchors (`manifest.ots`,
`manifest.tsr`) are exactly such timestamps. When no qualifying timestamp exists,
the honest verdict is that the signature can no longer be trusted: the math may still
verify, but the key lost its standing, so the signature no longer proves who sealed
the manifest or when. A key with no `validUntil` is not retired; nothing changes.

## Manual commands (what the scripts do)

Check the manifest signature:

```
openssl pkeyutl -verify -pubin -inkey permavault-public-key.pem -rawin -in manifest.json -sigfile manifest.sig
```

Check a file hash (compare with its entry in manifest.json):

```
sha256sum "permavault.xyz.wacz"
```

Fetch the primary artifact from Arweave and compare:

```
curl -L https://ar-io.dev/7iFluxfrjGu-BN6mdnfxUqQCClB6z9rYNIjDMYokF3I -o arweave-copy && sha256sum arweave-copy
```

Manifest SHA-256 (what the signature covers): `156c51a5b58a5afc8fc84cb870351f0df49b06440ad2835e169b79ebd5a227b7`

## Scope

This package verifies the technical integrity of the capture: that these exact bytes
were sealed at the recorded time and remain unaltered. It does not assess the truth,
accuracy, or legality of the captured content. The capture process and this proof
structure are designed to support authentication under FRE 902(13)/(14); admissibility
decisions always rest with the court.
