Skip to main content

Verifier

A verifier is anyone who wants to know whether a credential is genuine and still stands: an admissions officer, an employer, a scholarship committee, or a program running on someone's server. A verifier needs no account and no wallet. Everything a verifier checks is read from the public chain through the site's RPC endpoint, or checked in the browser against data the issuer signed.

This page also covers two special kinds of verifier:

  • a relying party's backend, which calls the verification API or uses the SDK instead of the web page, and
  • an access requester, who proves through the access gate that a holder admitted them. This is the only verifier action that needs a wallet.

How the app recognises a verifier​

It does not. There is no on-chain check for "verifier". The pages a verifier uses (/, /verify, /present, /open, /access) have no role gate, and the top bar always shows the Verify workspace with Look up a credential (/verify) and Scan a phone (/present). See Roles for the full list of open routes.

A verifier who connects a wallet that happens to be an issuer, guardian or holder still sees the same verify pages. Under a found credential the page always offers Manage as issuer, but the status tools open only for the issuer key now in charge of that credential (see What a verifier cannot do).

What a verifier sees​

RouteWhat it is for
/verifyLook up a credential by id, or open a verification link (/verify?id=0x...). Shows the result and the Further checks for files the holder gave you.
/presentCheck a credential shown as a QR code on a phone or printed certificate, offline first, then online. Optionally send the holder a challenge.
/openOpen a document from a capability link a holder sent (the key is in the part of the link after #).
/accessProve access to a credential after a holder admitted your access code.

On v0.2 deployments /verify shows the older form (Verify an academic record, field Record ID) instead of the credential lookup. The rest of this page describes v0.3.

Actions​

Look up a credential​

  1. Open /verify, or follow a verification link or QR code that leads to /verify?id=....
  2. Paste the id into Credential id (0x followed by 64 hex characters) and press Verify.
  3. Read the result:
    • Credential found on-chain with the record type, the issue date (and expiry, if any), the issuer's address and the document hash.
    • The issuer signature line: Issuer signature valid or Issuer signature INVALID.
    • The status banner (Active, suspended, revoked or superseded, with the reason). For a superseded credential the page checks the named replacement, which may live in another contract set.
    • Notes about the contract set the credential was found in and whether the issuer key was later retired.
What the lookup does not check

The page itself says so: "This page shows that the credential exists on-chain and compares the document hash. It does not yet check whether the issuer is still registered." It shows the issuer as an address, not a name. The verifiable credential and disclosure checks on the same page, and the verification API, do report an issuer that is no longer registered ("The issuer is not registered."). A guardian's issuer suspension does show, because it makes the status read as not valid.

A lookup that cannot reach the chain shows a problem notice instead of a result. It never shows "not found" for a network failure: Credential not found means the chain answered and has no such id in any listed contract set.

Full flow: Verification. Status meanings: Lifecycle.

Check what the holder gave you​

Under Further checks ("Check what the holder gave you"), each panel takes one file and checks it in the browser against the credential on screen:

PanelFileWhat a pass means
Check a document (field Document file)The PDF or other documentDocument matches the issued credential: its SHA-256 equals the hash the issuer signed.
Check holder details (field Credential package file)The credential packageHolder details match the on-chain commitments: the name, student ID and other details open the salted commitments on chain.
Check a verifiable credential fileA W3C verifiable credential or Open BadgeCredential verified, or Credential verified, details not proven when the file shows no details. Status list entries are compared with the chain when the file has them.
Check a disclosure (field Disclosure file)A selective disclosure fileDisclosure verified: the shown fields were signed by the issuer and the file was signed by the wallet bound to the student. Copy the Challenge first and give it to the holder.

The document check never uploads the file. It hashes it locally. Flows: Document storage, Selective disclosure, Verifiable credentials, Status lists.

Check a credential in person​

  1. Open /present (Scan a phone in the top bar). The page says no wallet is needed.
  2. Under Scan a credential code, point the camera at the holder's QR code, choose a picture of it, or paste the code into Or paste a code or link and press Check.
  3. The signature is checked in the browser first, so a result appears even without a network; the chain is asked afterwards when there is one.
  4. Optional, Ask the holder to prove it is theirs: type Your name, shown to the holder (optional), show the challenge QR to the holder, then scan the signed answer they show back. Press New challenge for another holder. A signed answer cannot be replayed.

Flow: Phone wallet. Code formats: Compact codes.

Open a shared document​

A holder can send a link of the form /open#.... Opening it downloads the encrypted container, decrypts it in the browser with the key from the link, and checks it against the credential on chain before offering it for saving. The key never reaches the server, because browsers do not send the part after #. Flow: Sharing.

Call the verification API or use the SDK​

A backend can ask GET /api/v1/credentials/{id} or POST /api/v1/verify and check the signed answer against a pinned signer address, or use @certichain/sdk, which does those checks itself. The API is built but answers only once the operator sets its signer key (API_SIGNER_KEY); see Operator. Pages: REST API, SDK, Verification API.

Prove access to a credential​

This is for someone a holder wants to let in without revealing which of the admitted people is asking.

  1. Open /access. Under Your access identity, press Use my wallet (the same wallet always gives the same identity), Make a new identity, or load a saved identity file. For a new identity, press Download identity file and keep it private.
  2. Press Copy access code and send the code to the holder. They admit it from their credential list (Manage access).
  3. Under Prove access, paste the Credential id and press Prove access. The proof is made in the browser and sent as a transaction from the connected wallet. The result is a receipt on chain, not a key: the holder still has to share the document.

A pause stops proving. A requester who was never admitted gets a refusal before anything is sent. Flow: ZK access.

What a verifier cannot do​

  • Change anything. A verifier's checks are reads. The only transaction a verifier ever sends is proveAccess, which records a receipt.
  • Use the issuer tools. Below a found credential the page shows "Issued this credential? Connect the issuing wallet to change its status." with a Manage as issuer button. With any wallet other than the key now in charge of that issuer, the page answers The connected wallet is not the issuer of this credential.
  • See personal data from the chain. The chain holds only salted commitments and hashes. Names and student IDs come only from files the holder chooses to give.
  • Open an encrypted document without a package, a shared file made for the verifier's share code, or a capability link from the holder.
  • Learn that a credential was never issued from a failed read. Every page distinguishes "the chain said no" from "the chain could not be read".

Keys a verifier holds​

None, with two exceptions: an access requester controls a wallet (to send the proof) and, if they made a new identity, the identity file. A backend that uses the API may hold an API key, which only raises its rate limits; the server stores only its SHA-256 hash.

Checklist​

  • Look the credential up by id, not by a name or student ID.
  • Read all three lines: found on chain, issuer signature valid, status Active.
  • Check the issuer address is the address the institution publishes, and, when it matters, that it is still registered (the VC check, the API or the block explorer show this; the lookup page does not).
  • If you received a document, run Check a document and expect Document matches the issued credential.
  • If you need the holder's name or ID, ask for the package or a disclosure file and check it on the same page.
  • For a disclosure, give the holder a fresh Challenge and check the file they return against it.
  • Treat any "could not be checked" notice as no answer, and try again.

Source files​

  • src/app/verify/CredentialVerifyContent.tsx, src/app/verify/CredentialSheet.tsx, src/app/verify/CredentialStatusBanner.tsx, src/app/verify/ManageCredential.tsx
  • src/app/verify/DocumentCheckPanel.tsx, HolderDetailsPanel.tsx, VcCheckPanel.tsx, DisclosurePanel.tsx, FurtherChecks.tsx
  • src/app/present/PresentContent.tsx, src/app/present/ChallengePanel.tsx
  • src/app/open/OpenLinkContent.tsx
  • src/app/access/AccessRequestView.tsx, IdentitySection.tsx, ProveSection.tsx
  • src/components/ui/LookupForm.tsx, src/components/layout/navigation.ts

Tests​

  • E2E: e2e/journeys/issue-and-verify.journey.ts, revoke-and-reverify.journey.ts, vc-export.journey.ts, open-badge.journey.ts, selective-disclosure.journey.ts, mobile-wallet.journey.ts, share-and-replace.journey.ts, access-proof.journey.ts, verification-api.journey.ts, contract-sets.journey.ts
  • Rendered and unit tests: src/app/verify/CredentialVerifyContent.test.tsx, CredentialStatusBanner.test.tsx, ManageCredential.test.tsx, DocumentCheckPanel.test.tsx, HolderDetailsPanel.test.tsx, VcCheckPanel.test.tsx, DisclosurePanel.test.tsx, lookup.test.ts, replacement-check.test.ts; src/app/present/PresentContent.test.tsx; src/app/open/OpenLinkContent.test.tsx; src/app/access/AccessRequestView.test.tsx