# Synthetic reference evidence format

This document specifies `loopgrid.synthetic.v1`. It is an educational, versioned reference protocol, **not the production LoopGrid evidence schema or verifier**. No compatibility with existing LoopGrid SDK exports is claimed.

## Canonicalization

`loopgrid-synthetic-json-v1` is a deliberately restricted JSON subset. Only null, booleans, strings, arrays, ordinary objects, and safe integers in the range ±(2^53−1) are accepted. Floating-point values and non-ASCII object keys are rejected. Object keys are sorted lexicographically (ASCII order). JSON is emitted without insignificant whitespace; strings use JSON escapes with all non-ASCII Unicode code points represented by lowercase `\u` escapes (surrogate pairs for non-BMP values). The canonical representation is encoded as ASCII bytes. The Python parser also rejects duplicate object keys. The browser parser rejects them as well. Use integer cents for currency, never floating-point money. This is not RFC 8785/JCS.

## Event commitments

Each event includes `schema_version`, `workspace_id`, `decision_id`, `signer_fingerprint`, positive contiguous `sequence`, `event_id`, `type`, `recorded_at`, `previous_hash`, and `payload`. The event ID is `evt_001`, `evt_002`, etc. The genesis hash is 64 ASCII zeroes.

1. `content_hash = SHA256(canonical(event without content_hash, chain_hash, signature))`.
2. `chain_hash = SHA256(canonical({"domain":"loopgrid.synthetic.chain.v1","previous_hash":previous_hash,"content_hash":content_hash}))`.
3. `signature = Ed25519.sign(UTF8("loopgrid.synthetic.event.v1\n" + chain_hash))`, encoded as standard base64.
4. The next event's previous hash is the preceding event's chain hash.

The signer fingerprint is `sha256:` followed by the lowercase SHA-256 hexadecimal digest of the DER SubjectPublicKeyInfo (SPKI) public key. The private key is not exported. A real verifier must receive the expected fingerprint through an independent trust channel.

## Manifest and checkpoint

The manifest identifies the format/canonicalization/algorithms, workspace, decision, signer, event count and genesis. The checkpoint payload binds schema version, workspace, decision, signer fingerprint, event count, and final chain hash. Its signature is Ed25519 over UTF-8 `"loopgrid.synthetic.checkpoint.v1\n" + SHA256(canonical(checkpoint.payload))`, where the digest is lowercase hexadecimal.

The verifier reconstructs hashes and sequence from genesis, checks every signature, compares identity fields, matches the manifest count and checkpoint payload to the reconstructed complete history, verifies the checkpoint signature, and compares the public-key fingerprint to the independently supplied pin. Removing a terminal event must be detected by the checkpoint. A fully re-signed history under another key must fail against the original pin.

## Reference ZIP

`bundle.json` is authoritative for this standalone reference verifier. The ZIP also contains `manifest.json`, `events.jsonl`, `checkpoint.json`, `public-key.pem`, and `README.txt`. The split files are inspection conveniences, but the Python verifier checks their consistency with `bundle.json` before verifying the signed history. `README.txt` is not signed evidence. This reference verifier does not claim production bundle-format compatibility. The Python CLI accepts JSON or ZIP containing `bundle.json`. It does not extract ZIP members to disk. It limits input size to 2 MB, ZIP entry count to 12, and event count to 64. The browser imports JSON only.

## Trust and limitations

Verification establishes integrity and provenance of recorded statements under the expected signing key. It does not establish upstream truth, capture completeness, real human identity, legal compliance, or successful business execution. Browser-generated keys and local timestamps are demonstration mechanisms, not production key custody or trusted time. A captured response from a synthetic adapter is not a response from a real external system. For real use, establish trusted capture boundaries, authorization, key management, policy/version provenance, complete event capture, authoritative outcome capture, privacy handling and the product's actual export/verifier compatibility.

## Reproduce

```
python -m pip install cryptography
python verify_synthetic.py fixtures/voice-refund.zip --pin-file fixtures/trusted-pin.txt
```

After installing the dependency, verification needs no network. Compare the published fixture with a browser-generated export; both implementations use the same reference rules. Do not use synthetic verification results as an attestation of a production LoopGrid deployment.
