Skip to main content

Verifiable credential file

Certichain exports a credential package in two standard shapes. Both carry the same Certichain proof: the issuer's EIP-712 signature over the on-chain credential, with the signed values next to it. The flow (who exports, who checks, what the screens say) is in Verifiable credentials and Open Badges.

PropertyVC 2.0 fileOpen Badges 3.0 file
File namecredential-<first 8 hex characters of the id>.vc.jsonbadge-<first 8 hex characters of the id>.ob.json
Media typeapplication/jsonapplication/json
type["VerifiableCredential", "CertichainCredential"]["VerifiableCredential", "OpenBadgeCredential"]
Produced byexportVc in src/lib/vc/export.tsexportOpenBadge in src/lib/vc/openbadges/export.ts, or exportStandardOpenBadge in src/lib/vc/openbadges/standard/export.ts
Parsed byparseVc in src/lib/vc/parse.tsparseOpenBadge in src/lib/vc/openbadges/parse.ts
Maximum size65,536 characters (MAX_VC_CHARS)Same

parseCredentialFile in src/lib/vc/detect.ts tells the two apart: a type array that contains OpenBadgeCredential is read as a badge, anything else as a VC. Upload panels refuse files over 64 KiB before reading them.

Both parsers accept exactly the keys listed below and refuse any other key, any other context and any other type. Free text (holder name, student id, programme, award) is 1 to 500 characters; the optional issuer name is 1 to 200.

VC 2.0 document​

FieldTypeRequiredMeaning and validation
@contextarrayyesExactly two entries: https://www.w3.org/ns/credentials/v2 and the embedded Certichain context object. The embedded object is compared canonically with CERTICHAIN_CONTEXT; any edit is refused.
idstringyesurn:certichain:eip155:<chainId>:<CredentialRegistry address>:<credentialId>. The id must be the EIP-712 digest of the signed fields.
typearrayyes["VerifiableCredential", "CertichainCredential"], in that order.
issuerobjectyesid: did:pkh:eip155:<chainId>:<issuer address>. Optional name, not signed, advisory only.
validFromstringyesissuedAt as YYYY-MM-DDTHH:MM:SSZ. Only this spelling is accepted.
validUntilstringnovalidUntil in the same form. Absent means no expiry (signed value 0).
credentialSubjectobjectyesSee below.
credentialStatusobject or arrayyesThe chain status entry alone, or an array of exactly three: the chain entry, then a revocation and a suspension list entry.
proofobjectyesThe Certichain proof. See below.

credentialSubject​

FieldTypeRequiredMeaning
recordTypenumberyesRecord type id, 0 to 2^32 - 1. Must equal the signed recordType.
idstringnodid:pkh of the holder wallet. Added only when the holder ticks the opt-in. Checked against HolderRegistry.holderOf on chain.
holderNamestringnoShown name. Proven by metadataHash.
studentIdstringnoShown student id. Proven by metadataHash and, with subjectSalt, by subjectCommitment.
programmestringnoShown programme. Proven by metadataHash.
awardstringnoShown award. Proven by metadataHash.

If holderName, studentId and metadataSalt are all absent, the details are "unproven" (shown as Credential verified, details not proven). If only some are present, it is a mismatch.

credentialStatus entries​

EntryFields
Chain entrytype CertichainChainStatus, chainId (decimal string), registry (address), credentialId (32 bytes). Must name the signing domain's chain and registry and the recomputed id.
List entryid = <statusListCredential>#<statusListIndex>, type BitstringStatusListEntry, statusPurpose revocation or suspension, statusListIndex (decimal string below 131,072), statusListCredential (a list address under /api/status-lists/<chainId>/<registry>/<issuer>/<purpose>/<list>).

The list entries are not covered by the issuer signature. The app recomputes them from the chain and refuses entries that point anywhere else. See Status lists.

proof (cryptosuite certichain-eip712-2026)​

FieldTypeRequiredMeaning and validation
typestringyesDataIntegrityProof.
cryptosuitestringyescertichain-eip712-2026.
proofPurposestringyesassertionMethod.
createdstringyesEqual to validFrom (the signed issuedAt).
verificationMethodstringyes<issuer did:pkh>#blockchainAccountId.
proofValuestringyesf followed by the lowercase hex of the signature (multibase base16). For an externally owned account the only accepted spelling is 65 bytes ending in 1b or 1c; any other value is left to the chain's ERC-1271 check.
signingDomainobjectyesname Certichain, version 1, chainId (decimal string), verifyingContract (the CredentialRegistry). Name and version are pinned.
signedFieldsobjectyesThe eight values the issuer signed (below).
metadataSaltstringno32-byte salt that opens metadataHash.
subjectSaltstringno32-byte salt that opens subjectCommitment.

signedFields:

FieldTypeRange
issueraddress
subjectCommitmentbytes32
documentHashbytes32SHA-256 of the issued document
metadataHashbytes32
recordTypenumber0 to 2^32 - 1
issuedAtnumberUnix seconds
validUntilnumberUnix seconds, 0 for no expiry
noncestringDecimal, up to 2^256 - 1

Example VC​

This is the shared golden vector vc/test-vectors/golden-vc.json (a test credential for "Asha Rai", S-1001, issued by a Hardhat test account). The embedded context is shortened here; the real file carries the whole object.

{
"@context": [
"https://www.w3.org/ns/credentials/v2",
{ "@version": 1.1, "@protected": true, "CertichainCredential": "urn:certichain:vocab:CertichainCredential", "...": "..." }
],
"id": "urn:certichain:eip155:11155111:0x475Bb8555d160c85f0175df117576193984C8083:0x6c4ee9b64594000fcff7304678b1db3fbee9a5bb3c56436d8156c44d521c618f",
"type": ["VerifiableCredential", "CertichainCredential"],
"issuer": { "id": "did:pkh:eip155:11155111:0x70997970C51812dc3A010C7d01b50e0d17dc79C8" },
"validFrom": "2027-01-15T08:00:00Z",
"credentialSubject": {
"recordType": 4,
"holderName": "Asha Rai",
"studentId": "S-1001",
"programme": "Computer Science",
"award": "BSc"
},
"credentialStatus": {
"type": "CertichainChainStatus",
"chainId": "11155111",
"registry": "0x475Bb8555d160c85f0175df117576193984C8083",
"credentialId": "0x6c4ee9b64594000fcff7304678b1db3fbee9a5bb3c56436d8156c44d521c618f"
},
"proof": {
"type": "DataIntegrityProof",
"cryptosuite": "certichain-eip712-2026",
"proofPurpose": "assertionMethod",
"created": "2027-01-15T08:00:00Z",
"verificationMethod": "did:pkh:eip155:11155111:0x70997970C51812dc3A010C7d01b50e0d17dc79C8#blockchainAccountId",
"proofValue": "f56a1fae66d584b7470d4db1f4e74db59e7b1566c75b3cb0d4801505a173a9dae6e183967eb06543cc1c9056b25a6cc5b0c56743ead537d90f657e0898beff0c71c",
"signingDomain": { "name": "Certichain", "version": "1", "chainId": "11155111", "verifyingContract": "0x475Bb8555d160c85f0175df117576193984C8083" },
"signedFields": {
"issuer": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
"subjectCommitment": "0x0de72e2aa4fde8c5058a57074f6a80d78d9d6f95b172f36e3e60522ad51639e6",
"documentHash": "0xba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
"metadataHash": "0xad52dfcdc40c7e615de64764601fcb8dcc869d5dcf9201e88117aff776610e28",
"recordType": 4,
"issuedAt": 1800000000,
"validUntil": 0,
"nonce": "7"
},
"metadataSalt": "0xa1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1",
"subjectSalt": "0xb2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2"
}
}

Open Badges 3.0 document​

A badge has the same id, validFrom, validUntil and Certichain proof as the VC of the same package. These members differ:

FieldTypeRequiredMeaning
@contextarrayyesVC 2.0 context, https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json, and the embedded Certichain context (with an extra keyBinding term when the badge has the standard proof).
typearrayyes["VerifiableCredential", "OpenBadgeCredential"].
namestringyesThe award, else the programme, else the record type name.
issuerobjectyesid (did:pkh, or the derived did:key with the standard proof), type ["Profile"], optional name, and with the standard proof one otherIdentifier entry { type: "IdentifierEntry", identifier: <did:pkh>, identifierType: "ext:didPkh" }.
credentialSubjectobjectyestype ["AchievementSubject"], optional id (did:pkh), optional identifier (two IdentityObjects with hashed: false: identityType name and ext:studentId, the value in identityHash), achievement, recordType, optional programme and award.
credentialSubject.achievementobjectyesid urn:certichain:achievement:<chainId>:<issuer>:<recordType>:<8 hex>, type ["Achievement"], achievementType, name, description ("<type name> recorded on the Certichain registry."), criteria.narrative (fixed text), optional fieldOfStudy (the programme).
credentialStatusobjectyesOne entry: the chain entry, or the revocation BitstringStatusListEntry when lists are on.
evidencearraynoOne Evidence with id <site>/verify?id=<credentialId>, name "Certichain anchor", description "Checks this credential against the Certichain registry."
proofobject or arrayyesThe Certichain proof alone, or [standardProof, certichainProofWithKeyBinding].

Every badge claim that is not signed must be derivable from signed values or be fixed text. The checker recomputes the name, achievement, achievement type, identifiers and evidence and reports any difference by name (identifier, achievementType, name, achievement, evidence, title, issuerKey).

Standard proof and key binding​

With the standard proof, proof[0] is an eddsa-rdfc-2022 Data Integrity proof made by the issuer's derived Ed25519 key. Its verificationMethod is <did:key>#<multibase key>. proof[1] is the Certichain proof plus:

FieldTypeMeaning
keyBinding.keyVersionnumberKey version (1 in the app).
keyBinding.publicKeystring32-byte Ed25519 public key, hex. Must be the key in the issuer did:key.
keyBinding.attestationstringThe issuer wallet's EIP-712 signature over OpenBadgeKeyBinding { registry, issuer, publicKey, keyVersion } (domain Certichain keys, version 1, chain id).

issuerKey fails unless the otherIdentifier names the registered did:pkh, the verification method matches the did:key, the public key matches, and the attestation recovers to the issuer for this registry and chain.

Versioning​

  • There is no version member. The format is pinned by the exact context object, the types and the cryptosuite name. A change to the embedded context changes its canonical form, which a test pins (src/lib/vc/golden.test.ts), and older parsers would refuse it.
  • The proof never depends on JSON-LD processing, so the embedded context could later move to a URL without changing any signature.
  • The context and schema files are kept in the repository under vc/contexts/ (certichain-v1.jsonld with its SHA-256, credentials-v2.json, ob-v3p0-context-3.0.3.json) and vc/schemas/ (certichain-credential.schema.json, ob_v3p0_achievementcredential_schema.json).

How it is checked​

  1. Parse (parseVc or parseOpenBadge): size, JSON, exact keys, pinned context and types, formats of every field.
  2. Offline (verifyViewOffline, plus verifyOpenBadgeOffline and checkOpenBadge for badges): consistency of claims with the signed fields, id recomputation, details against the hashes, signature recovery, expiry, badge claims, key binding, standard proof.
  3. On chain (VcService): same chain and registry, credential found, anchor equals signed fields, issuer registered now, isValid, ERC-1271 when needed, holder binding when a subject id is present.
  4. Status lists (StatusListChecker), only when the file has list entries and the page has lists configured.

Source files​

  • src/lib/vc/constants.ts, src/lib/vc/types.ts, src/lib/vc/fields.ts, src/lib/vc/parse.ts, src/lib/vc/export.ts, src/lib/vc/verify.ts, src/lib/vc/detect.ts
  • src/lib/vc/status/entry.ts, src/lib/vc/status/location.ts
  • src/lib/vc/openbadges/types.ts, src/lib/vc/openbadges/constants.ts, src/lib/vc/openbadges/export.ts, src/lib/vc/openbadges/parse.ts, src/lib/vc/openbadges/verify.ts
  • src/lib/vc/openbadges/standard/key.ts, src/lib/vc/openbadges/standard/export.ts, src/lib/vc/openbadges/standard/eddsa-rdfc-2022.ts
  • vc/cryptosuite-certichain-eip712-2026.md, vc/contexts/, vc/schemas/, vc/test-vectors/golden-vc.json

Tests​

  • src/lib/vc/parse.test.ts, src/lib/vc/export.test.ts, src/lib/vc/verify.test.ts, src/lib/vc/detect.test.ts, src/lib/vc/golden.test.ts, src/lib/vc/schema.test.ts
  • src/lib/vc/openbadges/parse.test.ts, export.test.ts, verify.test.ts, schema.test.ts
  • src/lib/vc/openbadges/standard/key.test.ts, export.test.ts, verify.test.ts, verify.errors.test.ts, eddsa-rdfc-2022.test.ts
  • interop/ (Digital Bazaar verifier checks)