Replace the raw JSONL response with a zip container:
events.jsonl — one SyncChange per line, as before
manifest.json — formatVersion, schemaVersion, userId, eventCount,
eventsSha256, apps, timestamps, schemaVersionMin/Max
Single DB pass: events.jsonl is written while a sha256 hasher tees
every byte of the uncompressed JSONL. The manifest lands as a second
zip entry after the stream closes, so eventsSha256 is filled without
rescanning.
Integrity-check on the restore side becomes trivial (re-hash the
decompressed events.jsonl and compare). Signature over manifest.json
is deferred to a later phase; sha256 already catches corruption.
Client-side: default filename + UI label updated to .mana. Fetch flow
is unchanged — browser gets a zip blob and triggers a download.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>