Skip to Content
ConceptsCore ProtocolMaster Data Registry

Master Data Registry

wallet.credentials alone stores a credential as an opaque JSON blob keyed only to issuer_id — nothing for an institution’s actual regulatory obligations (reporting, identifier verification, framework-aligned qualifications) to attach to. This is the master data model that fills that gap: jurisdictions, qualification frameworks, catalog entries, code sets, regulatory schemes, and learner records.

Registration → issuance flow

Three layers, one governing rule

Build once, configure as needed: what’s true in every jurisdiction lives in schema; what’s true only under one country’s regulation lives in data.

LayerContainsChanges when
Core domainlearner, catalog_entry, enrolment, outcome, issuerThe product’s capabilities change
Configurationjurisdiction, qualification_framework, code_set, regulatory_schemeA new market is onboarded — data only
Adapterscheme_field_map, export jobs (NAT files, SDR, ILR…)A regulator changes its export format

Onboarding a new country is a configuration exercise (seed data + at most one new export job), not a migration against core tables.

Service & schema ownership

SchemaServiceHoldsSensitivityRESTgRPC
catalogProgram CatalogJurisdictions, frameworks, catalog entries + versions, code sets, schemes, issuer scheme registrationsPublic/internal reference8085 (actuator only)50054
learnerLearner RecordsLearner identity, national identifiers, enrolments, outcomesRestricted PII8086 (actuator only)50055
authorityIssuer RegistryIssuers, DIDs, keysConfidential808350053
walletCredential IssuanceCredentials, extended with soft FKs belowConfidential808150051

Learner Records is its own service — not folded into Program Catalog — because a national identifier plus date of birth is regulated PII (Privacy Act 1988, GDPR, UK-DPA, PIPEDA). One service means one encryption-at-rest policy and one retention clock.

Core entities

TableKey columnsNote
catalog.jurisdictionjurisdiction_code (PK), name, regulator_name, statusISO 3166-1/2 codes, e.g. AU, AU-QLD, NZ
catalog.qualification_framework / framework_levelrank, labelAQF, NZQCF, EQF, RQF, SCQF, NSQF, DQR, CNCP/RNCP, QFEmirates, ISCED 2011 — only frameworks with a numbered level structure are seeded
catalog.catalog_entry / catalog_entry_versionentry_type: QUALIFICATION · UNIT · SKILL_SET · MICRO_CREDENTIAL · IDENTITY_DOCUMENT · LICENSEVersioned — superseding inserts a new row, never mutates the old one
catalog.external_referenceowner_table, owner_id, register_code, external_idPolymorphic pointer to any external register (TGA, NZQA, …)
learner.learner / learner_identifierlegal_name, dob (encrypted); identifier_type: USI · NSN · …
learner.enrolmentdelivery_mode, funding_source (both code_set FKs)Learner + catalog entry version + issuer, over a period
learner.outcomeresult_code, is_rpl, assessor_refThe fact a credential ultimately certifies
wallet.credentials (extended)learner_id, catalog_entry_version_id, outcome_id — all nullable soft FKsNull for credentials issued outside a tracked enrolment (most BYOK issuers)

Catalog entry versioning

At most one version per catalog_entry_id has effective_to IS NULL (the current version), enforced with:

CREATE UNIQUE INDEX idx_catalog_entry_version_current ON catalog.catalog_entry_version (catalog_entry_id) WHERE effective_to IS NULL;

A credential issued under version N remains fully resolvable forever — rows are never mutated or deleted.

Configuration entities

TablePurpose
catalog.code_set / code_valueThe one generic lookup mechanism — every jurisdiction-specific enum (delivery mode, funding source, outcome result, identifier type) is rows here, never a CHECK constraint
catalog.regulatory_scheme / scheme_field_mapA named reporting/identity-verification protocol, plus a data-driven map from core columns to that scheme’s export fields
catalog.issuer_scheme_registrationLinks an issuer to a jurisdiction’s regulator: registration number + scope (JSONB array of catalog_entry_id) + status

scheme_field_map.source_expr resolution strategies

source_kindMeaningExample
FIELD_PATHDot-path into the core entity graphoutcome.result_code
CONSTANTLiteral value"AU"
CODE_LOOKUPDot-path, resolved through code_setenrolment.delivery_mode → NAT00090 code
CONCAT|-separated list joinedlearner.family_name|learner.given_name

One FieldMapResolver implementation per source_kind; adding a fifth strategy is strictly additive.

PII encryption

learner_identifier.identifier_value_ciphertext uses AES-256-GCM at the application layer, with its own master key (LEARNER_REGISTRY_KMS_MASTER_KEY) — never shared with signing-key or SSO secrets, so a compromise of one doesn’t expose the others. The cipher primitive itself (AesGcmFieldEncryptor) lives in backends/shared, reused by every service that needs field-level encryption.

Referential integrity

All cross-schema references (catalog ↔ learner ↔ authority ↔ wallet) are soft FKs, validated via gRPC at write time — see Database Schema for the soft-FK pattern.

Identity documents and licences

Two catalog_entry types are not learning outcomes:

  • IDENTITY_DOCUMENT — establishes who someone is: passports, national ID cards, residence and work permits, visas, civil-status records.
  • LICENSE — authorises an activity: driving, flying, a profession, working with children.

These exist because Custodians (Custodian Onboarding) collect and attest exactly these documents. Seeded per-jurisdiction (AU, US, GB, IN, CA, SG, AE, NZ, EU, DE, FR, plus a GLOBAL default) — the custodian picker searches every jurisdiction, not only the custodian’s own, ranked with a small preference for the custodian’s own jurisdiction and GLOBAL first. Only records that never lapse (civil-status records, lifetime numbers) are seeded never_expires; everything else leaves validity unset since the printed expiry governs.