Independent Verification of an Asqav Receipt

You can check an Asqav receipt on your own machine without an Asqav account. The format and verifier are published. Retain the signed bytes and public-key material whose provenance you have established, then check the signature locally. This checks the signed record under that key; it does not establish the truth or completeness of the submitted event.

Local verification lets a reviewer examine a signed record without relying on the issuer's hosted answer. It supports integrity review, but the signature alone does not establish event truth, evidentiary weight or compliance with recordkeeping obligations.

What independent verification means

Three properties have to hold together, and Asqav publishes all three:

With the receipt, trusted key material and verifier retained, signature checking runs locally without querying Asqav. You still rely on the signing key's custody, the provenance of the public key and the verifier. A holder of the private key can sign another record; publication alone does not prevent that. Offline verification checks the bytes you retained against the key you selected.

The exact offline checks

The standalone verifier checks signed payloads, predecessor links and supported anchor proofs. Its cryptographic checks use the dependencies and trust material described below.

ML-DSA-65 receipt verification uses dilithium-py. Supported classical signatures and TSA certificates use cryptography. Missing dependencies or required trust material can leave a check unverifiable; a skipped cryptographic check does not establish authenticity.

How an auditor verifies a receipt offline

A regulator, auditor, or counterparty who receives a receipt runs the following. None of it requires an Asqav account or login.

  1. Save the open verifier. It lives at python/src/asqav/verifier/verify_receipt.py in the public asqav-sdk repository. Read it first. It is one file.
  2. Install the dependencies for receipt signatures and TSA certificates:
    bash
    python -m pip install dilithium-py cryptography
  3. Save the receipt and the public key directory:
    bash
    curl -fsS -H 'Accept: application/json' 'https://api.asqav.com/api/v1/verify/<receipt-id>' > receipt.json
    curl -fsS 'https://api.asqav.com/.well-known/jwks.json' > jwks.json
  4. Verify with no further network access. The --offline flag forbids the tool from reaching out again:
    bash
    python verify_receipt.py --receipt receipt.json --jwks jwks.json --offline
  5. To check the hash-chain link, also save the predecessor receipt and pass it:
    bash
    python verify_receipt.py --receipt receipt.json --jwks jwks.json \
        --predecessor previous.json --offline

For RFC 3161 anchors, add --tsa-key FILE with a TSA public key or certificate you have chosen to trust. For OpenTimestamps, add --bitcoin-headers FILE with trusted Bitcoin block information. Supplying those files supports the corresponding proof checks; it does not make the standalone verifier validate a certificate chain to a public root or independently authenticate the Bitcoin header source.

The tool prints a per-axis report and an overall verdict. Exit code 0 means verified or verified_keyed; exit code 1 means unverified with failure_class: invalid; exit code 2 means unverified with failure_class: unverifiable. Expiry is reported separately from the verdict, and an omitted predecessor alone does not block verification. To check the negative case, alter a field in the signed payload and confirm that signature verification fails under the same public key.

The public shape fixture can be inspected without an account:

bash
python verify_receipt.py --id sig_example_regulator_cold_verify_2026

This fixture contains placeholder signature and anchor values, and its issuer key is not in the public directory. It illustrates the response format; it is not a cryptographically verified receipt. The standalone verifier reports unverified with failure_class: unverifiable and exit code 2, with its signature and anchor checks skipped. To check a real receipt, supply its signed bytes, matching trusted public-key material and any trust inputs required by its anchors; a real receipt ID alone does not guarantee a verified result.

Use the standalone verifier above to inspect individual timestamp anchors and their proofs.

Which members of an exported entry are signed

An Audit Pack entry is the signed payload, its signature object and anchors. Beside them the export adds plain copies of some signed values. The copies are there for convenience and no signature covers them, so the verifier compares each one with the signed value it restates. A copy that disagrees fails the outer_copies axis, and the verdict becomes unverified with failure_class: invalid even when the signature itself verifies.

Outer memberSigned value it must equal
agent_idpayload.agent_id
action_idpayload.action_id
action_typepayload.action_type, present in payload-mode receipts only
receipt_typepayload.type
supersedespayload.supersedes
signed_atpayload.issued_at, the same instant to the microsecond. An observation receipt may carry its caller-observed context.ts instead, because that is the time its record stores
algorithmsignature.alg
signature_b64signature.sig, the same decoded bytes
message_b64the canonical (JCS) bytes of the payload, which are the bytes that were signed
message_sha256the SHA-256 of those bytes, compared whenever the signature verified over them
public_key_b64the key the signature verified under; listed instead when the signature did not verify, for example after a key rotation
compliance_modetrue, whenever a payload object is present
signature as a flat string, signature_envelopethe signature object the signature check used, whichever spelling carries it
any other top-level member named like a signed member, such as issued_at, decision, org_id or typethat signed member
signature.<name>the signed member of the same name, when the signature object carries extras

These members are not signed, and the verifier lists them in the axis note as "not signed, do not rely on" on a pass as well as on a failure: signature_id, capture_topology, stale_pending, corrupted, corruption_message, signed_bytes_withheld, action_type on a hash-mode receipt (its signed bytes carry no action type), and any other name the receipt never signed, such as verified, kid or agent_name. Some copies cannot be judged and are listed instead of failed. That covers an observation signed_at when the signed context.ts is withheld by the default export or written in a form the check does not read, and the digest and key copies when the signature did not verify. A member that is absent or null is not compared, except compliance_mode, which must be true whenever it is present. A flat hash-mode receipt (mode: hash, no payload object) signs its own fields, so it has no copies. Its signature covers v, mode, hash, hash_algo, metadata, server_timestamp, action_id, agent_id, org_id, policy_digest and policy_decision. Every other member is listed as not signed, apart from these. algorithm and anchors are read by their own checks. The signature string that was verified (signature_b64, or signature when signature_b64 is empty) is not listed, and neither is a second string with the same decoded bytes. Any other signature, such as an object or a differing string, is listed. A payload that is not null is listed too. key_id only picks which published key to try, and nothing signs it. It is left unlisted when the signature verified under the published key whose kid it names, and listed otherwise. Read the agent, the action and the time from the signed payload.

The check reads the same signature object the signature axis verified, so a flat sig string or a signature_envelope object is compared like the object form. The hosted /api/v1/verify/<id> response is a display projection; its own members such as verified and verification_url are listed as not signed. The public verify page compares a pasted asqav-native entry the same way. It shows a warning above the result when a copy disagrees and a "Not signed, ignore" line naming the unsigned members, including on a pass.

What offline verification proves, and what it does not

Read the per-axis results: signature verification, a predecessor check and anchor proof verification establish different properties. A passing signature does not verify the truth of the event or substitute for checking its anchors.

Offline verification with the open verifier proves:

Offline verification with the open verifier does not, on its own, prove:

An audit pack can supply the surrounding records and available anchor material. Preserve the signed bytes and trusted key material when exporting. Checking a timestamp certificate chain requires the relevant certificates, trust roots and a verifier that implements that check; supplying an export does not add that check to the standalone verifier.

The signed bytes and a trusted public key support local signature verification. An audit pack supplies additional material for checks of the record set and available anchors, subject to the verifier's supported checks and the trust assumptions for each proof. Retain that material, the verifier and its dependencies for future audits; do not depend on the live key directory remaining available.

How this supports records review

SEC Rule 17a-4(f)(3)(v)(A) permits the required undertakings to be signed by either a designated executive officer or a designated third party. The unaffiliated-party definition in paragraph (f)(1)(vi) applies to the third-party option. Receipt verification supports integrity checks; it does not itself satisfy the undertaking or the recordkeeping requirements.

A reviewer can check a receipt without querying Asqav or the customer whose agent submitted it. With fixed signed bytes, a fixed public key and a compatible verifier, the signature result is reproducible. The reviewer must still establish the key's provenance and custody, and seek corroboration for event truth, timing and completeness.

See Asqav as third-party notary for the notary role this verification underpins, SEC 17a-4(f)(3)(v), DORA Articles 28-30, and NYDFS 23 NYCRR 500.06 for the regulation walkthroughs, and post-quantum signed receipts for the ML-DSA-65 algorithm choice and threat model.

References