Cosignet — offline evidence pack
================================

This archive is self-contained. It proves, on a machine with NO network access
and WITHOUT any Cosignet account, that a specific human approved exactly one
action with their passkey, and that the approval is committed in an append-only,
Bitcoin-anchored transparency log.

What's inside
-------------
  bundle.json                 The verification bundle for one log entry:
                              the leaf, inclusion proof, credential public key,
                              raw WebAuthn assertion, and the signed tree head.
  sth_public_key.txt          The Ed25519 public key that signs the tree head.
                              PIN this out-of-band (compare against the value you
                              trust) — do not take our word for it.
  tree_head.ots               OpenTimestamps proof anchoring the tree head into
                              Bitcoin (present once the anchor has upgraded). A
                              trust-minimized timestamp independent of Cosignet.
  verifier_node_example.js    Reference verifier (Node, zero-dependency).
  verifier_py_example.py      Reference verifier (Python; stdlib for steps 1-2,
                              `cryptography` for signature checks).
  verifier_node_example.d.ts  TypeScript types for the Node verifier.
  run_node.mjs                One-shot runner: verifies bundle.json (Node).
  run_python.py               One-shot runner: verifies bundle.json (Python).

Verify it (pick one; no network required)
-----------------------------------------
  Node   (>= 18):   node run_node.mjs
  Python (>= 3.8):  python3 run_python.py     # pip install cryptography for full check

Expected output on success:
  { "ok": true, "sth_signature": "verified" }

What each step checks
---------------------
  1. leaf hash   recompute the RFC-6962 leaf from the entry fields
  2. inclusion   the leaf is committed under the signed tree-head root
  3. WebAuthn    the assertion was signed by the published credential key AND its
                 challenge binds the payload hash — i.e. the human approved THIS
                 exact action on their device, not some other one
  4. STH         the tree head is Ed25519-signed by sth_public_key.txt

If bundle.json includes the raw payload (public-reveal entries), the verifiers
also confirm SHA-256(payload) == payload_hash. For private entries, hold the
payload out-of-band and pass it to verifyBundle(bundle, rawPayload).

Independence
------------
sth_signature: "verified" means the tree head checked out against the pinned key.
Even if you don't trust that key, tree_head.ots anchors the same tree head into
the Bitcoin blockchain — verify it with any OpenTimestamps client (`ots verify`).

Source of truth: the verifiers are vendored from the public SDK repo
(cosignet-public/sdk/security/verifiers). They are byte-for-byte copies; keep in
sync with that repo. The live JSON for this entry is also served at
<base>/public/log/entries/<id>/verification.
