Standards
A credential is only useful if other systems can read it. Certichain keeps its own on-chain format small and exports to widely used standards so that a wallet, an employer's verifier or another registry can check a Certichain credential without Certichain software. This page explains each standard and how Certichain maps onto it.
Overview
| Standard | Body | What Certichain uses it for | Code |
|---|---|---|---|
| Verifiable Credentials Data Model 2.0 | W3C | The VC export of a credential package | src/lib/vc/ |
| Verifiable Credential Data Integrity 1.0 | W3C | The proof format inside VCs, badges and status lists | src/lib/vc/export.ts |
did:pkh | W3C CCG method | Names an Ethereum address as a DID (issuer, holder, status list publisher) | didPkh in src/lib/vc/constants.ts |
did:key | W3C CCG method | Names the issuer's derived Ed25519 key in a badge | src/lib/vc/openbadges/standard/did-key.ts |
eddsa-rdfc-2022 cryptosuite | W3C | The second, standard proof on an Open Badge | src/lib/vc/openbadges/standard/eddsa-rdfc-2022.ts |
| Open Badges 3.0 | 1EdTech | The Open Badge export | src/lib/vc/openbadges/ |
| Bitstring Status List 1.0 | W3C | Revocation and suspension lists for exported VCs | src/lib/vc/status/ |
| EIP-712 | Ethereum | Typed-data signatures | src/lib/eip712.ts and others, see Signatures |
| EIP-191, EIP-4361 (SIWE) | Ethereum | Issuer sign-in | src/lib/server/sign-in.ts |
| ERC-1271 | Ethereum | Signatures from contract wallets | blockchain/contracts/v03/CredentialRegistry.sol |
| RFC 8785 (JCS) | IETF | Canonical JSON for hashing | src/lib/commitments.ts, src/lib/api/envelope.ts |
| RFC 9180 (HPKE) | IETF | Wrapping document keys | src/lib/doc-crypto/wrap.ts |
| RFC 6979 | IETF | Deterministic ECDSA, relied on for key derivation | See Signatures |
| RFC 5869 (HKDF) | IETF | Deriving keys from signatures | src/lib/doc-crypto/identity-key.ts |
| RFC 3339 | IETF | Timestamps in VCs and status lists | src/lib/vc/time.ts |
| OpenAPI 3.1 | OpenAPI Initiative | The verification API description, served at /api/v1/openapi.json | src/lib/api/openapi.ts |
W3C Verifiable Credentials 2.0
A verifiable credential (VC) is a JSON-LD document in which an issuer makes claims about a subject, plus a proof that lets anyone check the issuer made them. Version 2.0 of the data model uses the context https://www.w3.org/ns/credentials/v2 and the properties issuer, validFrom, validUntil, credentialSubject, credentialStatus and proof.
JSON-LD gives every property a global meaning through @context, so two systems agree on what holderName means. Certichain embeds its own small context in each document (CERTICHAIN_CONTEXT in src/lib/vc/constants.ts) instead of fetching it from a URL; its terms live under urn:certichain:vocab:, and a test pins its digest so any edit is noticed.
How a credential package becomes a VC
| VC property | Value | Source in the package |
|---|---|---|
@context | The VC 2.0 context and the embedded Certichain context | Fixed |
id | urn:certichain:eip155:<chainId>:<registry>:<credentialId> | Domain and credential id |
type | ["VerifiableCredential", "CertichainCredential"] | Fixed |
issuer | { id: "did:pkh:eip155:<chainId>:<issuer>" }, with the registered name when known | credential.issuer |
validFrom | The issue time in RFC 3339 | credential.issuedAt |
validUntil | Present only when the credential has an expiry | credential.validUntil |
credentialSubject | recordType, and when the package has details: holderName, studentId, programme, award; an id (did:pkh) when the holder's address is known | display |
credentialStatus | See status below | Chain and configuration |
proof | See the cryptosuite below | Signature and signed fields |
The export never includes the document key, the CID or the selective disclosure data (exportVc in src/lib/vc/export.ts). It does include the holder details and their salts when the package has them, because those are what a verifier needs to check the metadata hash. A VC file is therefore personal data, and the holder decides whom to give it to.
The certichain-eip712-2026 cryptosuite
A Data Integrity proof is a proof object with a type of DataIntegrityProof, a named cryptosuite that says how to check it, a verificationMethod (the key) and a proofValue.
Certichain's proof reuses the issuer's existing EIP-712 credential signature instead of making a new one:
return {
type: "DataIntegrityProof",
cryptosuite: VC_CRYPTOSUITE,
proofPurpose: "assertionMethod",
created: issuedAt,
verificationMethod: `${issuer}${VERIFICATION_FRAGMENT}`,
proofValue: `f${pkg.signature.slice(2).toLowerCase()}`,
signingDomain: { name: domain.name, version: domain.version, chainId: domain.chainId, verifyingContract: getAddress(domain.verifyingContract) },
signedFields: { ...credential },
...(pkg.display && pkg.metadataSalt ? { metadataSalt: pkg.metadataSalt } : {}),
...(pkg.display && pkg.subjectSalt ? { subjectSalt: pkg.subjectSalt } : {}),
};
proofValueis the 65-byte signature in multibase base16 (prefixf).signingDomainandsignedFieldscarry exactly what the issuer signed, so the checker recomputes the EIP-712 digest, recovers the signer and compares it with thedid:pkhissuer. The salts let it confirm that the claims incredentialSubjectmatch the signedmetadataHashandsubjectCommitment.- The proof does not depend on JSON-LD processing, so changing the context later would not break any signature.
- The verification method is
did:pkh:...#blockchainAccountId.
The suite is written up in vc/cryptosuite-certichain-eip712-2026.md, with a golden test vector in vc/test-vectors/. Because it is Certichain's own suite, a generic VC library needs a small plugin to check it; the interop/ folder holds one and tests it against the Digital Bazaar @digitalbazaar/vc library.
Status in a VC
credentialStatus always contains a CertichainChainStatus entry naming the chain id, the registry and the credential id: the authoritative status is on chain. When status lists are configured, the export adds two BitstringStatusListEntry entries, one for revocation and one for suspension (statusOf in src/lib/vc/export.ts).
DIDs: did:pkh and did:key
A decentralized identifier (DID) is a URI that names a subject and resolves to keys that can sign for it, without a central registry of names.
did:pkhwraps an existing blockchain account:did:pkh:eip155:11155111:0xAbC...is the Sepolia address0xAbC.... Certichain uses it for issuers, holders and the status list publisher, because their keys are Ethereum keys.did:keyencodes a public key directly in the identifier:did:key:z6Mk...for an Ed25519 key (multicodec header plus key, base58btc, multibase prefixz). Certichain uses it for the issuer's derived badge key.src/lib/vc/openbadges/standard/did-key.tsrefuses anything but a bare Ed25519did:key.
Open Badges 3.0
Open Badges 3.0, from 1EdTech, is a VC 2.0 profile for achievements, widely supported by learning platforms and badge wallets. A badge is an OpenBadgeCredential whose subject is an AchievementSubject that names an achievement.
| Badge property | Certichain value |
|---|---|
@context | VC 2.0, https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json, and the embedded Certichain context |
type | ["VerifiableCredential", "OpenBadgeCredential"] |
credentialSubject.type | ["AchievementSubject"], with identity entries of type name and ext:studentId |
| Achievement type | One of the Open Badges achievement types (for example BachelorDegree, Certificate, Diploma), listed in ACHIEVEMENT_TYPES in src/lib/vc/openbadges/constants.ts |
| Evidence | A "Certichain anchor" link that opens the check |
credentialStatus | The chain status entry, or the revocation list entry when status lists are configured |
Certichain exports two forms:
- Badge with the Certichain proof (
src/lib/vc/openbadges/export.ts): the samecertichain-eip712-2026proof as the VC, with adid:pkhissuer. - Badge with two proofs (
exportStandardOpenBadgeinsrc/lib/vc/openbadges/standard/export.ts): aneddsa-rdfc-2022proof by the issuer's wallet-derived Ed25519 key, which any Data Integrity verifier can check with no plugin, followed by the Certichain proof carrying akeyBinding, the issuer wallet'sOpenBadgeKeyBindingsignature over that key. The issuer profile is thedid:key, with the registereddid:pkhlisted as anext:didPkhidentifier.
A verifier that checks only the eddsa-rdfc-2022 proof learns that the derived key signed the badge. It does not learn that the key belongs to a registered institution, nor whether the credential is still valid on chain. Certichain's checks read the key binding and the chain for that, as the code comment on exportStandardOpenBadge states.
The repository vendors the 1EdTech JSON schema and checks exported badges against it and against JSON-LD safe mode (src/lib/vc/schema.test.ts). The handbook notes that a run through an official Open Badges validator is still open.
The eddsa-rdfc-2022 cryptosuite
eddsa-rdfc-2022 is a W3C Data Integrity cryptosuite: the document is turned into RDF, put into a canonical form with the RDF Dataset Canonicalization algorithm (RDFC-1.0), hashed with SHA-256, and signed with Ed25519. Because canonicalisation works on meaning rather than JSON text, any equivalent serialisation verifies.
Certichain's signer is signUnsecured in src/lib/vc/openbadges/standard/eddsa-rdfc-2022.ts, with a fixed document loader that serves only the bundled contexts (document-loader.ts), so signing and checking never fetch from the network. The interop tests verify Certichain badges with Digital Bazaar's stock @digitalbazaar/eddsa-rdfc-2022-cryptosuite.
Bitstring Status List
A Bitstring Status List publishes the status of many credentials at once as a long string of bits. Each credential gets an index; bit i set means "revoked" (or "suspended", in a list with that purpose). A verifier downloads the list and reads one bit, and the publisher does not learn which credential was checked.
| Property | Certichain value |
|---|---|
| List length | 131,072 bits (LIST_SIZE in src/lib/vc/status/bitstring.ts) |
| Encoding | GZIP, then multibase base64url without padding (prefix u) |
| Purposes | revocation and suspension, one list each |
| List credential types | ["VerifiableCredential", "BitstringStatusListCredential"], subject type BitstringStatusList |
| Time to live | 300,000 ms (5 minutes), in the subject's ttl |
| Served at | /api/status-lists/..., one list per chain, registry, issuer, purpose and list number |
| Index | The credential's position in its issuer's confirmed issuance order |
| Proof | DataIntegrityProof with the certichain-eip712-2026 suite: an EIP-712 StatusList signature by the publisher key over the registry, issuer, purpose, list number, the hash of the encoded list and the validity window |
| Issuer | The publisher's did:pkh, from NEXT_PUBLIC_STATUS_LIST_PUBLISHER |
The server builds each list from the chain on request, so the lists follow suspend, reinstate and revoke on chain. A checker must pin the publisher address: the list's own issuer is the publisher, not the institution, so a generic library's issuer match has to be turned off and the publisher checked instead (this is one of the findings recorded in docs/interop/report.md).
Status lists are built but switched off until STATUS_LIST_SIGNER_KEY, NEXT_PUBLIC_STATUS_LIST_BASE_URL and NEXT_PUBLIC_STATUS_LIST_PUBLISHER are set. The publisher key must hold no funds. As of 2026-10-24 the handbook lists this configuration as waiting for the owner.
Details: Status lists (server) and Status lists (flow).
OpenAPI
The public verification API (GET /api/v1/credentials/{id}, POST /api/v1/verify, GET /api/v1/status) is described by an OpenAPI 3.1 document generated from src/lib/api/openapi.ts and served at /api/v1/openapi.json. The JavaScript SDK's types are bound to it. See REST API.
Source files
src/lib/vc/constants.ts,export.ts,parse.ts,verify.ts,time.ts,to-package.tssrc/lib/vc/status/bitstring.ts,entry.ts,list-credential.ts,location.tssrc/lib/vc/openbadges/constants.ts,export.ts,src/lib/vc/openbadges/standard/export.ts,key.ts,did-key.ts,eddsa-rdfc-2022.ts,document-loader.tssrc/lib/api/openapi.tsvc/cryptosuite-certichain-eip712-2026.md,vc/contexts/,vc/schemas/,vc/test-vectors/interop/(independent verifier tests)
Tests
src/lib/vc/export.test.ts,parse.test.ts,verify.test.ts,golden.test.ts,schema.test.ts,time.test.ts,to-package.test.ts,detect.test.tssrc/lib/vc/status/bitstring.test.ts,entry.test.ts,list-credential.test.ts,location.test.ts,derive.test.tssrc/lib/vc/openbadges/standard/eddsa-rdfc-2022.test.ts,export.test.ts,key.test.ts,did-key.test.ts,verify.test.ts,document-loader.test.tssrc/lib/api/openapi.test.tsinterop/test/- E2E journeys
e2e/journeys/vc-export.journey.ts,open-badge.journey.ts,status-lists.journey.ts