Verification

These functions check evidence you have been given, rather than producing any. They are the Python counterpart of the checks the Integrity explorer runs in the browser, and they answer two separate questions:

  • Is this statement still what it says it is? Every lineage statement is identified by a hash of its own canonicalized content, so recomputing that hash and comparing it with the statement's @id detects any modification after the fact.
  • Is this credential's signature good, and is it about the statement I think it is? A valid signature over some other subject proves nothing about the statement in hand, so the two checks belong together.

Both run fully offline. JSON-LD contexts resolve against documents compiled into the package or supplied by you via contexts=, and credentials are restricted to DID methods that can be resolved from the identifier itself, so verification never reaches the network. This is what makes them usable in an air-gapped or reproducible pipeline.

There is no manifest-level entry point. Verifying a whole manifest means looping these two functions over its statements, which is what the explorer does:

import json
from eqty_sdk import verify_statement, verify_vc

manifest = json.loads(open("manifest.json").read())
contexts = manifest.get("contexts")

for statement in manifest["statements"].values():
    if not verify_statement(json.dumps(statement), contexts):
        print("modified since it was created:", statement["@id"])

verify_statement

Pass the manifest's contexts map whenever you have one. Statements reference their context by CID ("@context": "urn:cid:..."), and while the common contexts ship inside the package, a statement written against a custom context cannot be canonicalized without it.

A True result means every field the statement's @context defines is unmodified. It does not mean the bytes are unmodified: the identifier commits to the statement's canonicalized RDF, and JSON-LD expansion drops keys the context does not define.

verify_statement builtin

verify_statement(
    statement_json: str,
    contexts: Optional[Dict[str, Any]] = None,
) -> bool

Verifies that a lineage statement's content still hashes to its @id.

Canonicalizes the statement as JSON-LD, recomputes its BLAKE3 RDFC CID and compares it with the @id the statement carries. Returns False when they differ, meaning the statement was modified after it was created.

contexts maps a JSON-LD context URI to its context document, for contexts this build does not embed. Values may be dicts or JSON strings, so a manifest's contexts field can be passed straight through. Note that a supplied context takes precedence over an embedded one of the same URI.

Runs fully offline. A context that is neither embedded nor supplied raises rather than being fetched.

A True result means every field the statement's @context defines is unmodified. It does not mean the bytes are unmodified: the identifier commits to the statement's canonicalized RDF, and JSON-LD expansion drops keys the context does not define.

Raises ValueError if the statement is not a JSON object or has no string @id, and RuntimeError if a context cannot be resolved.

verify_vc

Supply statement_id whenever you know which statement the credential is supposed to attest. Without it, only the signature is checked.

Supply contexts whenever the credential's @context references a document this build does not embed. Verification re-expands the credential, so an unresolvable context means the proof cannot be checked at all — and because nothing is fetched, that surfaces as False rather than an error, indistinguishable from a bad signature. If a credential you expect to be valid returns False, an unsupplied context is the first thing to check.

This verifies the cryptographic proof alone. Whether a credential has since been revoked or suspended lives in its status list, which has to be fetched over the network and is deliberately not consulted here.

verify_vc builtin

verify_vc(
    vc_json: str,
    statement_id: Optional[str] = None,
    contexts: Optional[Dict[str, Any]] = None,
) -> bool

Verifies a W3C Verifiable Credential's proof offline.

When statement_id is given, also checks that the credential's credentialSubject.id is that statement. A valid signature over some other subject says nothing about the statement in hand.

Returns False when the credential does not verify or is bound to a different subject. Raises ValueError when the input is not a credential, meaning it is not JSON, has no credentialSubject, or has more than one subject. Raises RuntimeError when the credential's DID method would need network access: only did:key, did:jwk and did:pkh resolve offline.

Checks the cryptographic proof only. Revocation and suspension live in the credential's status list, which is fetched over the network and is not consulted here.

contexts maps a JSON-LD context URI to its context document, for contexts this build does not embed. Values may be dicts or JSON strings, so a manifest's contexts field can be passed straight through. Note that a supplied context takes precedence over an embedded one of the same URI.

Runs fully offline. Verifying a credential re-expands it, so a context that is neither embedded nor supplied is never fetched — it reports False, alongside the other reasons a proof may not check out.