07. Exports
Draft 0.1. Reference implementation: shared/verifiableCredentials.js, api/issue-credential.js, api/verify-credential.js, api/did.js, api/jsonld-context.js, api/protocol-spec.js. Vector v20. No code is specified in this chapter; every mapping not backed by code is marked planned with the gap named.
7.1 What exists today
The reference implementation issues one kind of W3C Verifiable Credential: a vault-field credential over a category of the family's profile, signed with the organization key. It does not yet export a transcript, a course receipt, a work sample, or an attestation as a credential; those are the receipts of chapter 02, and their export is the subject of sections 7.3 and 7.4.
The issued credential
POST /api/issue-credential (family session; 20 per minute per IP) takes {categoryId, fields, provenance, personName} and returns:
| field | type | value or rule |
|---|---|---|
@context | array | ["https://www.w3.org/2018/credentials/v1", "https://eformogi.com/credentials/v1"] |
id | URI | urn:eformogi:vc:<32 hex random> |
type | array | ["VerifiableCredential", <one of the six types below>] |
issuer | object | {id: "did:web:eformogi.com", name: "Eformogi"} |
issuanceDate | ISO 8601 | |
credentialSubject | object | id: "did:eformogi:user:<account uuid>", one property per claim, displayName when given |
credentialStatus | object | {id: "https://api.eformogi.com/credentials/status/<id>", type: "StatusList2021Entry"} |
eformogiMetadata | object | {version: "1.0.0", verificationDetails {field: {method, source, confidence, sourceDocument, extractedAt}}, fieldCount} |
proof | object | {type: "DataIntegrityProof", cryptosuite: "eddsa-jcs-2022", created, verificationMethod: "did:web:eformogi.com#key-1", proofPurpose: "assertionMethod", proofValue: "z..."} |
Credential types: EformogiFamilyProfile, EformogiEducationRecord, EformogiMedicalRecord, EformogiIdentityDocument, EformogiFinancialRecord, EformogiInsuranceRecord. Provenance sources map to verification methods: document_extraction to DocumentExtraction, ai_extraction to AIExtraction, institutional_api to InstitutionalVerification, user_input to SelfAsserted, parent_entry to ParentAsserted, imported to ImportedData, verified to IndependentVerification.
Fields named ssn, creditCard, bankAccount, driversLicense, or passportNumber are stripped before issuance and MUST never appear in a credential.
The proof
The suite is Data Integrity eddsa-jcs-2022: signInput = sha256(utf8(JCS(proof options))) || sha256(utf8(JCS(document))), 64 bytes, signed raw with Ed25519; proofValue is multibase base58btc. JCS here is the canonical form of chapter 02, section 2.3, and every non-integer number in the document is coerced to a string before signing (jcsCoerceNumbers), so a GPA of 3.7 travels as "3.7". Any receiver verifies against the JWK at /.well-known/did.json with no secret and no call to the operator (vector v20). The former symmetric suite HmacSha256Signature2024 is retired and refused with a re-issue message.
POST /api/verify-credential (public) returns {verified, checks: {proof, issuer, revocation, expiration}, credential: {...summary}}; revocation is read from the operator's verifiable_credentials table, not from a status list.
Known gaps in the existing shape
| gap | status |
|---|---|
@context is the VC Data Model 1.1 context while the code describes itself as 2.0-compatible; issuanceDate is the 1.1 name (2.0 uses validFrom) | planned: move to https://www.w3.org/ns/credentials/v2 and validFrom |
credentialStatus names StatusList2021Entry but no status list is served; revocation is a database read | planned: publish a BitstringStatusList, or drop the field until it is real |
credentialSubject.id is did:eformogi:user:<uuid>, a non-resolvable DID method | planned: the learner's did:key once wallet custody exists (chapter 04) |
https://eformogi.com/credentials/v1 context resolves to api/jsonld-context.js, which maps vault fields (identity, contact, education, medical, financial, family, activities, employment, provenance) to schema.org and eformogi: terms; it does not define receipt or attestation terms | planned: a receipt context |
api/protocol-spec.js (FDEP 1.0.0-draft, 2026-03-02) still states credentialIntegrity: 'HMAC-SHA256 proof' | stale text; planned: update or retire (it is a Rule 8 audit item) |
7.2 The DID document
/.well-known/did.json publishes did:web:eformogi.com with one verification method (#key-1, JsonWebKey2020, the Ed25519 JWK of chapter 04), used for authentication and assertionMethod, and services #schema, #verify, #revocations, #issue, #vault, #context, #openapi, #fdep, and #receipt-log (type TransparencyLog, endpoint /api/tree-heads). A receiver resolving the issuer of any signed artifact in this specification lands here.
7.3 Mapping to Open Badges 3.0 (AchievementCredential)
Open Badges 3.0 is a VC profile: an AchievementCredential whose credentialSubject is an AchievementSubject holding an Achievement. The natural unit is one course row (cr_) or one work sample (ws_), with attestations as Evidence and the family as Profile issuer. Status: planned end to end; no OB3 credential is issued today.
| OB3 field | source in the Record | status |
|---|---|---|
@context | https://www.w3.org/ns/credentials/v2, https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json | planned |
type | ["VerifiableCredential", "AchievementCredential"] | planned |
id | https://eformogi.com/verify/<cr_ or ws_ id> | planned (gap: a resolvable credential URL that returns the VC, not the HTML page) |
issuer (Profile) | family: id = family did:key (chapter 04), name = the family's issuer string; organization issuer: planned with the issuer rail | planned (gap: Profile.type for a family issuer; OB3 assumes an organization) |
validFrom | issued_at (cr_: the row's created_at) | planned |
credentialSubject.type | AchievementSubject | planned |
credentialSubject.id | learner did:key | planned (gap: learners have no key today; the family key is per account, not per person) |
credentialSubject.achievement.type | Achievement | planned |
credentialSubject.achievement.name | course_title or ws_.title | planned |
credentialSubject.achievement.description | statement-free description of the course or entry | planned |
credentialSubject.achievement.criteria.narrative | for cr_: term, credits; for ws_: entry_kind, occurred_at | planned |
credentialSubject.achievement.achievementType | Course for cr_; Competency or LearningProgram for ws_ by kind | planned |
credentialSubject.achievement.creditsAvailable | credits (numeric) | planned (gap: credits is a string in the hash domain by design; the numeric projection is display-only) |
credentialSubject.result[] (Result) | family_stated_grade as value, resultDescription "Family-graded" | planned |
credentialSubject.source | the family Profile | planned |
evidence[] (Evidence) | one entry per attestation: id = /verify/<att_ id>, name = signer name and role, description = statement, genre = "witness attestation", audience = signer organization | planned (gap: OB3 Evidence has no slot for signed_grade disagreement; carry it in description and keep the authoritative rendering on the verify page) |
credentialStatus | BitstringStatusList entry driven by revoked_at | planned (gap: no status list today; revocation is online-only) |
proof | eddsa-jcs-2022 by the family key, plus the organization key as a second proof for the ceremony | planned (gap: the family key is escrowed; an OB3 issuer proof by an escrowed key must be disclosed as such) |
endorsement[] (EndorsementCredential) | organization countersignature (chapter 03, section 3.5) as an endorsement of the ceremony | planned (gap: the countersignature endorses processing, not the achievement; the endorsement text must say that) |
image, alignment[] | none | not applicable |
7.4 Mapping to Comprehensive Learner Record v2.0 (ClrCredential)
CLR v2 wraps many AchievementCredentials plus a ClrSubject and optional associations into one record. The natural unit is the transcript (tr_), holding one AchievementCredential per cr_. Status: planned; no CLR is issued today.
| CLR v2 field | source in the Record | status |
|---|---|---|
type | ["VerifiableCredential", "ClrCredential"] | planned |
id | https://eformogi.com/verify/<tr_ id> | planned |
issuer | family Profile (as above) | planned |
validFrom | tr_.issued_at | planned |
credentialSubject.type | ClrSubject | planned |
credentialSubject.id | learner did:key | planned (same gap as OB3) |
credentialSubject.verifiableCredential[] | one OB3 AchievementCredential per cr_ on the transcript | planned |
credentialSubject.achievement[] | the achievements referenced above | planned |
credentialSubject.association[] | tr_ to cr_ provenance (tr_receipt_id) as isChildOf | planned |
credentialSubject.identifier[] | the tr_ id and its sha256 as IdentifierEntry (identityType: "identifier") | planned |
credentialSubject.narrative | program, philosophy (only if the family opts in; it is on the printed transcript but not on the public payload today) | planned (gap: privacy decision) |
credentialSubject.activityStartDate / activityEndDate | earliest and latest year on the rows | planned |
credentialSchema | the CLR JSON schema | planned |
proof | as OB3 | planned |
tier chips (tier_counts, "issuer-declared" muting) | no CLR slot | gap: CLR has no notion of per-row trust tier; the honesty rendering stays on the verify page and the mapping MUST NOT upgrade a chip into a Result the ledger did not corroborate |
transcript_eligible: false objects (wn_, wl_) | excluded from CLR by rule | not applicable |
7.5 Rules for any export (MUST)
- An export MUST link back to the receipt id it was derived from, so a receiver can drop back to chapter 06 and verify the source independently of the export's own proof.
- An export MUST NOT contain more of the family's content than the public verify payload does, unless the family explicitly includes it at export time.
- An export MUST carry the same honesty statements as the verify page (chapter 06, section 6.6) in a human-readable field, and MUST label any proof by an escrowed key as escrowed (chapter 04, section 4.7).
- An export MUST NOT promote a self-declared witness or an issuer-declared tier into a claim that the receiver could read as third-party verification.
- Numbers in signed documents MUST be JCS-safe (strings for anything non-integer).
7.6 What this does not prove
A credential proves the organization key signed a document at a time. It does not prove the provenance labels inside it are true (they are the family's and the extraction pipeline's labels), and a verified: true from the verification endpoint is a proof and issuer check, not an endorsement of the claims. Planned mappings prove nothing until they ship; this chapter records intent so a standards reviewer can mark gaps against a fixed text.