Skip to main content

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​

StandardBodyWhat Certichain uses it forCode
Verifiable Credentials Data Model 2.0W3CThe VC export of a credential packagesrc/lib/vc/
Verifiable Credential Data Integrity 1.0W3CThe proof format inside VCs, badges and status listssrc/lib/vc/export.ts
did:pkhW3C CCG methodNames an Ethereum address as a DID (issuer, holder, status list publisher)didPkh in src/lib/vc/constants.ts
did:keyW3C CCG methodNames the issuer's derived Ed25519 key in a badgesrc/lib/vc/openbadges/standard/did-key.ts
eddsa-rdfc-2022 cryptosuiteW3CThe second, standard proof on an Open Badgesrc/lib/vc/openbadges/standard/eddsa-rdfc-2022.ts
Open Badges 3.01EdTechThe Open Badge exportsrc/lib/vc/openbadges/
Bitstring Status List 1.0W3CRevocation and suspension lists for exported VCssrc/lib/vc/status/
EIP-712EthereumTyped-data signaturessrc/lib/eip712.ts and others, see Signatures
EIP-191, EIP-4361 (SIWE)EthereumIssuer sign-insrc/lib/server/sign-in.ts
ERC-1271EthereumSignatures from contract walletsblockchain/contracts/v03/CredentialRegistry.sol
RFC 8785 (JCS)IETFCanonical JSON for hashingsrc/lib/commitments.ts, src/lib/api/envelope.ts
RFC 9180 (HPKE)IETFWrapping document keyssrc/lib/doc-crypto/wrap.ts
RFC 6979IETFDeterministic ECDSA, relied on for key derivationSee Signatures
RFC 5869 (HKDF)IETFDeriving keys from signaturessrc/lib/doc-crypto/identity-key.ts
RFC 3339IETFTimestamps in VCs and status listssrc/lib/vc/time.ts
OpenAPI 3.1OpenAPI InitiativeThe verification API description, served at /api/v1/openapi.jsonsrc/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 propertyValueSource in the package
@contextThe VC 2.0 context and the embedded Certichain contextFixed
idurn: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 knowncredential.issuer
validFromThe issue time in RFC 3339credential.issuedAt
validUntilPresent only when the credential has an expirycredential.validUntil
credentialSubjectrecordType, and when the package has details: holderName, studentId, programme, award; an id (did:pkh) when the holder's address is knowndisplay
credentialStatusSee status belowChain and configuration
proofSee the cryptosuite belowSignature 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 } : {}),
};
  • proofValue is the 65-byte signature in multibase base16 (prefix f).
  • signingDomain and signedFields carry exactly what the issuer signed, so the checker recomputes the EIP-712 digest, recovers the signer and compares it with the did:pkh issuer. The salts let it confirm that the claims in credentialSubject match the signed metadataHash and subjectCommitment.
  • 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:pkh wraps an existing blockchain account: did:pkh:eip155:11155111:0xAbC... is the Sepolia address 0xAbC.... Certichain uses it for issuers, holders and the status list publisher, because their keys are Ethereum keys.
  • did:key encodes a public key directly in the identifier: did:key:z6Mk... for an Ed25519 key (multicodec header plus key, base58btc, multibase prefix z). Certichain uses it for the issuer's derived badge key. src/lib/vc/openbadges/standard/did-key.ts refuses anything but a bare Ed25519 did: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 propertyCertichain value
@contextVC 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 typeOne of the Open Badges achievement types (for example BachelorDegree, Certificate, Diploma), listed in ACHIEVEMENT_TYPES in src/lib/vc/openbadges/constants.ts
EvidenceA "Certichain anchor" link that opens the check
credentialStatusThe chain status entry, or the revocation list entry when status lists are configured

Certichain exports two forms:

  1. Badge with the Certichain proof (src/lib/vc/openbadges/export.ts): the same certichain-eip712-2026 proof as the VC, with a did:pkh issuer.
  2. Badge with two proofs (exportStandardOpenBadge in src/lib/vc/openbadges/standard/export.ts): an eddsa-rdfc-2022 proof by the issuer's wallet-derived Ed25519 key, which any Data Integrity verifier can check with no plugin, followed by the Certichain proof carrying a keyBinding, the issuer wallet's OpenBadgeKeyBinding signature over that key. The issuer profile is the did:key, with the registered did:pkh listed as an ext:didPkh identifier.
What the standard proof alone tells a verifier

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.

PropertyCertichain value
List length131,072 bits (LIST_SIZE in src/lib/vc/status/bitstring.ts)
EncodingGZIP, then multibase base64url without padding (prefix u)
Purposesrevocation and suspension, one list each
List credential types["VerifiableCredential", "BitstringStatusListCredential"], subject type BitstringStatusList
Time to live300,000 ms (5 minutes), in the subject's ttl
Served at/api/status-lists/..., one list per chain, registry, issuer, purpose and list number
IndexThe credential's position in its issuer's confirmed issuance order
ProofDataIntegrityProof 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
IssuerThe 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).

Off until configured

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.ts
  • src/lib/vc/status/bitstring.ts, entry.ts, list-credential.ts, location.ts
  • src/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.ts
  • src/lib/api/openapi.ts
  • vc/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.ts
  • src/lib/vc/status/bitstring.test.ts, entry.test.ts, list-credential.test.ts, location.test.ts, derive.test.ts
  • src/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.ts
  • src/lib/api/openapi.test.ts
  • interop/test/
  • E2E journeys e2e/journeys/vc-export.journey.ts, open-badge.journey.ts, status-lists.journey.ts