Verification chain
The five cryptographic checks every LearnCoin verifier runs, and exactly which of them require LearnCoin's infrastructure (none).
Verification is the whole point. Any party — recruiter, HR system, AI agent, EUDI wallet — can confirm a LearnCoin credential is authentic using only:
- The signed JSON-LD document (the recipient has their own copy)
- Network access to Base (the public chain)
- Network access to the issuer's DID document
No LearnCoin API call is required. That's a design commitment and a contract commitment, not a performance optimization.
The five checks
Every Blockcerts-compatible verifier runs five checks. Failure on any one = credential not valid.
1. Schema
The document conforms structurally to the three standards in its @context:
- Required W3C VC 2.0 fields present:
@context,type,issuer,issuanceDate,credentialSubject,proof. - Required OB 3.0 fields present:
typeincludesOpenBadgeCredential;credentialSubject.typeincludesAchievementSubject;credentialSubject.achievementis a valid Achievement object. - Required Blockcerts v3 fields present:
proof.typeisMerkleProof2019;proof.verificationMethodis a DID URL;proof.proofValueis a valid base58-encoded proof structure.
Schema failure usually means the document was truncated or tampered with at the structural level (not a content change — that's caught by the signature check).
2. Signature
Cryptographic signature validates against the issuer DID's verificationMethod.
The verifier:
- Canonicalizes the credential (URDNA2015 → N-Quads).
- Hashes the canonical form with SHA-256 — this is the leaf hash.
- Decodes
proof.proofValue— extracts the Merkle path (sibling hashes from leaf to root) and the ECDSA signature over the root. - Walks the Merkle path:
hash(leaf || sibling1)→hash(result || sibling2)→ … → computed root. - Resolves
proof.verificationMethod— fetches the issuer's DID document, extracts the matching public key. - Verifies the ECDSA signature over the computed root using that public key.
If any step fails → schema-valid but crypto-tampered. The document's contents don't match the signed proof.
3. Issuer
The issuer DID resolves, and the verificationMethod used for signing is listed in the DID document.
The verifier:
- Parses
proof.verificationMethod— e.g.did:web:learncoin.me#tenant-01HXYZ-key-1. - Resolves the DID document. For
did:web:learncoin.me, this means fetchinghttps://learncoin.me/.well-known/did.json. - Finds the matching key in the DID document's
verificationMethodarray. - Confirms the key is not marked
revokedin the DID document.
Why this matters: if a key was compromised and we rotated, the DID document would flag the old key. Credentials signed with the old key (before the rotation) still verify at the raw crypto level, but the DID-level check can surface them for special handling.
4. Revocation
The credential is not present in the issuer's revocation status list.
Today: LearnCoin exposes revocation status via a simple lookup on the public verification page and via GET /v1/credentials/{id}. Verifiers that fetch either see revoked: true/false.
On the roadmap: BitstringStatusList (the standards-track successor to StatusList2021). Once shipped, verifiers will discover revocation via a credentialStatus reference embedded in the signed JSON-LD, and the revocation list itself will be a static file with its own signature — fully checkable without a LearnCoin API call.
5. Erasure
If the recipient exercised GDPR erasure, the personal fields (credentialSubject.name) are redacted on the public verification page. The cryptographic proof still validates — erasure doesn't break crypto.
A verifier sees:
- Signed document with
"name": "Ada Lovelace"→ still valid. - Verification page showing
Name: Redacted on recipient request→ erasure applied, but proof holds.
The design is deliberate: a recipient erasing their own PII shouldn't also invalidate credentials that have been legitimately shared.
Who runs these checks?
The public verification page (learncoin.me/c/{id})
Runs all five checks client-side in the viewer's browser using the blockcerts-verifier Web Component. Nothing is sent to a LearnCoin server during verification — the browser fetches the credential JSON, the DID document, and the Base anchor transaction directly.
Open DevTools on a verification page and watch the network tab — you'll see direct calls to basescan.org and learncoin.me/.well-known/did.json, nothing posting verification results back.
Blockcerts native wallets (iOS + Android)
Run the same five checks offline once the credential is imported. No LearnCoin server required after the initial import.
cert-verifier-js library
import { Certificate } from "@blockcerts/cert-verifier-js";
const cert = new Certificate(signedCredentialJsonLd);
const result = await cert.verify();
// result.status === "success" when all five checks pass
// result.checks is an array of { code, label, status, errorMessage }LearnCoin maintains a fork (via the barcelova GitHub org) that adds Base as a supported chain. Upstream PR is open.
Your own code
import { verifyCredential } from "@blockcerts/cert-verifier-js";
export async function verifyLearnCoinCredential(signedJsonLd: unknown) {
const result = await verifyCredential(signedJsonLd, {
chains: ["bitcoin-mainnet", "ethereum-mainnet", "base-mainnet", "base-sepolia"],
});
return {
valid: result.status === "success",
checks: result.checks,
reason: result.status === "failure" ? result.errorMessage : undefined,
};
}AI agents
An AI agent doing due diligence on a credential URL can verify it by:
GET https://learncoin.me/c/{id}— returns the signed JSON-LD + metadata.- Running the five checks via
cert-verifier-jsor equivalent. - Reading the "Cryptographically verified by LearnCoin · Powered by Blockcerts" attribution on the rendered page.
Because verification is client-side, an agent can trust its own verification result without trusting LearnCoin's claim that the credential is valid.
What makes a credential invalid
| Condition | Which check fails? | Public page shows |
|---|---|---|
| Document was truncated or malformed | Schema | Error |
| Document was modified after signing | Signature | "Signature mismatch" |
| Issuer's DID doesn't resolve | Issuer | Error |
Signing key is marked revoked in DID document | Issuer | Warning |
Issuer called /credentials/{id}/revoke | Revocation | "Status: Revoked" |
| Recipient requested GDPR erasure | (Not a failure) | "Redacted on recipient request" — credential still verifies |
| Anchor transaction isn't on Base | Signature | "Anchor not found" |
| Anchor transaction reverted | Signature | Rare; would be flagged |
What makes a credential VALID forever
Three things need to be true at verification time:
- The signed JSON-LD document exists (the recipient has their own copy + LearnCoin's
/c/{id}serves it). - The anchor transaction is on Base (Base is a public chain; this isn't under LearnCoin's control).
- The issuer DID document resolves (served from learncoin.me; we've forked all critical verification libraries onto the
barcelovaGitHub organization so that if LearnCoin disappears, the verification stack still works from mirrors).
None of these three require LearnCoin to be operational. That's the "credentials outlive the platform" commitment, written into the architecture rather than promised in marketing copy.
See also
- /docs/concepts/credentials — anatomy of the signed document
- /docs/api/endpoints#get-v1credentialsid — API for fetching
- /trust — full posture page
- /standards — audit-verified standards conformance