Skip to main content

Manual checks

The automated suites stop at what a machine can drive: the E2E harness uses an injected test wallet, an in-memory upload stand-in and a local chain. These scripts cover the rest: a real wallet extension (MetaMask), the real upload route with Pinata, a phone camera, and the public test network. Run them after a change to a flow, before a release, and when the handbook lists a manual check as open.

Each script names the role, the starting route, the labels to press (in bold, as in the app) and the expected result. "Wallet: signature" means a signing prompt that sends nothing; "Wallet: transaction" means a transaction that costs gas.

Test data only

Use synthetic names and documents ("Asha Rai", student ID "S-1001"). Every page shows "Test network demo. Do not upload real personal documents. Data written to the blockchain is public." Never import a Hardhat development key into a wallet you use on a public network.

Setting up​

Local chain​

  1. Start a node and keep it running: cd blockchain && npx hardhat node (JSON-RPC at http://127.0.0.1:8545, chain 31337). It prints the development accounts and their keys.

  2. In a second terminal deploy the v0.3 set with a guardian and register an issuer:

    cd blockchain
    GUARDIAN_ADDRESS=<account 4> npm run deploy:v03:local
    ISSUERS="<account 1>=Test University" npm run issuers:v03:local
    npm run deploy:access:local # optional, for the access checks
  3. In the repository root, give the app a local environment (names only shown; see Configuration): NEXT_PUBLIC_CONTRACT_VERSION=v03, NEXT_PUBLIC_NETWORK_ID=31337, NEXT_PUBLIC_RPC_URL=http://127.0.0.1:8545, NEXT_PUBLIC_START_BLOCK=0, a SESSION_SECRET of at least 32 characters, and a test Pinata credential (PINATA_JWT with file read and write permission) with NEXT_PUBLIC_PINATA_GATEWAY_URL. Redis is not needed outside production; the server uses an in-memory store.

  4. npm run dev and open http://localhost:3000 in Chrome or Firefox (Safari may refuse the Secure session cookie on http://localhost).

  5. In MetaMask add the network (RPC http://127.0.0.1:8545, chain id 31337) and import development accounts 0 (admin), 1 (issuer), 2 (student) and 4 (guardian), the same roles the E2E harness gives them.

After restarting the node, redeploy and reset MetaMask's activity for the local network, or nonces will not match. See Local development.

Sepolia​

Use the deployed site, a test issuer wallet registered in the active set, and a student wallet with a little Sepolia ETH. As of 2026-10-24 the active set is 0.3.0. Admin actions on Sepolia go through the Safe and, for a set under the timelock, through a scheduled operation; they are decisions of the Safe owners, not steps to try from the site.

Verifier (no wallet)​

Use a private window with no wallet extension, or the visitor role on a phone.

StepActionExpected
V1Open /verify, paste a known credential id into Credential id, press VerifyCredential found on-chain, Issuer signature valid, status Active, the issuer address and the document hash. The note says the page does not yet check whether the issuer is still registered
V2Paste 0x followed by 64 zeros, press VerifyCredential not found: "No credential with this id exists on this network. Check the id and try again."
V3Paste abc, press Verify"This is not a valid credential id. It must be a 0x-prefixed 32-byte hex value."
V4Local only: stop the Hardhat node, look up a valid id"Could not reach the network, so the credential was not checked. This is not a verification result. Please try again." (never "not found")
V5Under Check a document, choose the issued document, then a different fileDocument matches the issued credential, then Document does NOT match the issued credential
V6Under Check holder details, choose the credential packageHolder details match the on-chain commitments with the name and student ID
V7Under Check a verifiable credential file, choose an exported VCCredential verified (or Credential verified, details not proven when the file shows no details)
V8Open the verification link from a QR code with the phone cameraThe same result as V1, with no wallet

Holder (account 2, or the Sepolia student wallet)​

StepActionExpected
H1Connect and sign inSent to /student, page My credentials, top bar shows My credentials
H2Under Accept an invitation, choose the Invitation file from I5, press Accept invitationWallet: transaction. Success notice; credentials issued with delivery appear under Issued to this wallet
H3On a row, press Show QR, then VerifyA QR of the verification link; /verify shows the credential
H4Under Open a document, choose the package, press Open documentThe document opens; a link to check the credential is still active
H5With a second wallet open /student, press Show my share code, Copy codeWallet: signature (may be asked). A ccshare1_... code
H6Back as the holder, paste it into Recipient's share code, press Create shared file"Shared file saved..." The second wallet opens it under Open a shared document; a third wallet cannot
H7Press Create a link, confirm Create link, Copy link, open it in a private window/open decrypts and checks the document, then offers it
H8Verifier copies the Challenge under Check a disclosure; holder uses Share some details only, picks one field, pastes the challenge, presses Create disclosure fileWallet: signature. The verifier gets Disclosure verified showing only that field
H9On a phone, open /wallet, add the credential (file, code or scan), Add to this phone, Show the code; scan it on /present with another deviceThe verdict appears offline first, then online

Issuer (account 1, or the Sepolia test issuer)​

StepActionExpected
I1Connect and sign inSent to /dashboard with the registered name and the Registered issuer badge
I2Issue a credential: fill Student Name, Student ID, choose a Record Type, choose the Document file, press Encrypt and uploadWallet: signature (Sign-In with Ethereum, first upload of the session). IPFS Hash and Document SHA-256 filled
I3Leave delivery unticked and press Issue RecordThe dialog "Issue without delivering to a wallet?"; press Go back and tick Deliver to the student's linked wallet
I4Press Issue RecordWallet: two signatures (delivery and the credential), then a transaction. The card says Credential issued; press Download credential package
I5Students: enter the Student ID and Student wallet, choose Valid for, press Create invitationWallet: signature. The invitation file is saved; hand it to the holder (H2)
I6On the dashboard row, press Revoke, pick a Reason, press Revoke credentialWallet: transaction. "Credential revoked. Anyone who checks it now sees it as revoked." V1 now shows revoked
I7Issue another credential, press Re-issue on its row and issue the replacementWallet: signatures, then one transaction (issueReplacing). The old credential shows superseded and names the new one
I8On /verify for an active credential, press Manage as issuer, then Suspend and Confirm suspend; then ReinstateWallet: transaction each. The status banner follows
I9Record types: add a Name, press Add type; then Turn offWallet: transaction each. The type appears, then shows Inactive and is no longer offered when issuing
I10Students: Look up by student ID, Unlink wallet, confirm UnlinkWallet: transaction. The holder's inbox no longer lists the credential

Guardian (account 4, or the Sepolia guardian)​

StepActionExpected
G1Connect and sign inSent to /admin, page Governance, state Running, the admin address and no pending transfer
G2Press Pause the systemWallet: transaction. State Paused
G3As the issuer, open Issue a credentialIssue Record is disabled and the paused notice is shown
G4As the issuer, revoke a credential (I6); as a visitor, verify one (V1)Both still work
G5Restart (admin, see A2)State Running again; issuing works

On Sepolia, pause only with the Safe owners' agreement, because only they can restart a 0.3.0 set.

Admin​

StepActionExpected
A1cd blockchain && npm run governance:v03:sepolia (read only)Each registry's admin is the Safe or the timelock as recorded, and no unexpected pending transfer
A2Local only: npx hardhat console --network localhost, then const r = await ethers.getContractAt("IssuerRegistry", "<IssuerRegistry from the manifest>") and await r.unpause()The guardian's /admin page shows Running after a reload
A3Local only: register a second issuer with ISSUERS="<account 5>=Second College" npm run issuers:v03:localThat account's sign-in lands on /dashboard
A4Sepolia, with the Safe owners: build a proposal with npm run timelock:propose:sepolia, schedule it from the Safe/admin lists it under Queued admin actions as Waiting, later Ready; after execution the change is visible on chain

Recording the result​

Write down the date, the network, the build or commit, each step's result and any message that differed from this page, in the step's session log. A failing step is reported with the exact message and the wallet prompt that preceded it.

Source files​

  • The labels on this page come from src/app/verify/, src/app/student/, src/app/wallet/, src/app/present/, src/app/dashboard/, src/app/records/add/, src/app/students/invite/, src/app/records/types/, src/app/admin/
  • blockchain/scripts/deploy-v03.ts, add-issuers-v03.ts, deploy-access-gate.ts, governance-status.ts, timelock-propose.ts
  • src/lib/server/config.ts, src/lib/server/rate-limit.ts, src/lib/server/session.ts
  • docs/process/verification-checklist.md (the handbook's release checklist)