# Artifacts and verification

> The eight files in a package, the manifest, the ed25519 signature over its sha256, and how to check it yourself.

Source: https://retoque.xyz/docs/artifacts
Markdown: https://retoque.xyz/docs/md/artifacts

Every version is one uncompressed tar file with exactly eight files and no executable content. You can check all of it yourself without trusting the registry.

## Package layout

| File | Contents |
| --- | --- |
| `adapter_model.safetensors` | The LoRA weights. Safetensors only, never pickle. |
| `adapter_config.json` | PEFT configuration: base model, rank, alpha, target modules. |
| `retoque.manifest.json` | Canonical JSON describing the version. See [The manifest](#the-manifest). |
| `signature.json` | ed25519 signature over the manifest's sha256, with the publisher public key. |
| `eval-report.json` | The held-out evaluation report the version cites. |
| `checksums.txt` | `sha256sum`-style list of the other seven files. |
| `README.md` | What the patch does, how to load it, and where the evidence is. |
| `LICENSE.txt` | The license terms. Its sha256 is the terms digest. |

The tar is built reproducibly: files in sorted order, PAX format, owner and group 0 with empty names, modification time 0 and mode `0644`. The file is named `<slug>-<version>.tar`. Anything else in an archive, including links, directories and unknown names, is a reason to refuse it.

## The manifest

`retoque.manifest.json` is written as canonical JSON: keys sorted, no spaces, UTF-8. Its sha256 is the manifest digest the signature covers, the registry stores and `retoque.lock` pins.

| Field | Contents |
| --- | --- |
| `schemaVersion` | `"1.0"` |
| `patchId`, `patchVersion` | The slug and semver. The CLI refuses a package whose signed identity differs from the one requested. |
| `publisher` | Publisher name and key algorithm. |
| `baseModel` | Repository, full revision, and the sha256 of the weights, `tokenizer.json` and the chat template, plus the base license. |
| `adapter` | Format `peft-lora-safetensors`, file name, sha256, size, rank, alpha, sorted target modules, precision and PEFT type. |
| `runtimeCompatibility` | Tested runtime and library versions, tested devices and quantization, what is unsupported, and whether special tokens changed. |
| `training` | Run ID, method, training set digest and size, provenance, seed, hyperparameters, hardware, time and final loss. |
| `evaluation` | Run ID, task, held-out and regression suite digests, evaluator source digests, decoding, baseline, patched and prompt-only counts, regressions introduced, whether rollback outputs were identical, report sha256 and the thresholds. |
| `license` | `LICENSE.txt`, its sha256 as `termsDigest`, and the license type. |
| `files` | sha256 and byte size of the five payload files: adapter weights, adapter config, README, license and evaluation report. |
| `knownSideEffects` | The publisher's note on behaviour changes outside the task. |
| `status` | The starting evidence state set by the packager. |

## signature.json

The publisher signs the raw 32-byte sha256 digest of `retoque.manifest.json` with ed25519. Because the manifest lists the sha256 and size of every payload file, one signature covers the whole package.

```json
{
  "algorithm": "ed25519",
  "signedPayload": "sha256(retoque.manifest.json)",
  "manifestSha256": "<64 hex>",
  "publicKey": "<64 hex>",
  "signature": "<128 hex>",
  "role": "publisher"
}
```

## checksums.txt

One line per file in `sha256sum` format, two spaces between the digest and the name, for the five payload files plus `retoque.manifest.json` and `signature.json`. You can check an extracted package with standard tools:

```sh
sha256sum -c checksums.txt
```

## Verify a package

The CLI runs every check in [What install does](https://retoque.xyz/docs/cli#what-install-does):

```sh
retoque verify ./<slug>-<version>.tar    # offline
retoque verify <slug>@<version>          # installed copy, offline
```

Or by hand, from an extracted directory:

```python
import hashlib, json
from nacl.signing import VerifyKey

manifest_bytes = open("retoque.manifest.json", "rb").read()
sig = json.load(open("signature.json"))

digest = hashlib.sha256(manifest_bytes).digest()
assert digest.hex() == sig["manifestSha256"]

# Compare sig["publicKey"] with a publisher key you trust before this step.
VerifyKey(bytes.fromhex(sig["publicKey"])).verify(digest, bytes.fromhex(sig["signature"]))

manifest = json.loads(manifest_bytes)
for name, entry in manifest["files"].items():
    data = open(name, "rb").read()
    assert hashlib.sha256(data).hexdigest() == entry["sha256"], name
    assert len(data) == entry["bytes"], name
```

## Where trust comes from

- The first-party publisher key is pinned in the CLI. Other keys count only if you add them to your own config. The registry can't add trust roots.
- The registry's digests are cross-checked, but the registry alone can't make a package acceptable. Only a trusted signature over the exact bytes can.
- A valid signature proves who published these exact bytes. It says nothing about whether the patch works. The evaluation numbers in the manifest are the publisher's claims for the stated held-out suite, and the evidence state says how far they have been checked.
- Where a version is registered on chain, the registry contract stores its manifest, adapter and terms digests, so you can compare them with your copy. See [Robinhood Chain](https://retoque.xyz/docs/robinhood-chain).
- The base weights' sha256 is recorded but not re-hashed on install. The tokenizer and chat template are checked when the snapshot is cached, and `run` pins the revision.
