End-to-end encrypted medical data at scale
The common DNA across all entities
id
Every entity in Cardinal has a unique id field
550e8400-e29b-41d4-a716-446655440000
Code
type|code|version
SNOMED-CT|200773006|1
Why UUID v4? No server coordination needed — clients generate unique IDs independently, enabling offline-first architectures.
rev
Revision tracking for conflict detection
<number>-<hash>
1-abc123
2-def456
Client A reads entity (rev: 3-xyz) Client B reads entity (rev: 3-xyz) Client A updates → succeeds (rev: 4-abc) Client B updates → CONFLICT (stale rev)
⚠️ Only present on root entities — nested entities (Service, Content, SubContact) don't have revisions
Connect Cardinal entities to external systems
system
http://hospital.org/mrn
value
MRN-12345
type
{ type: "MR" }
assigner
"St. Mary's Hospital"
system + value
Two collections of Code references on every entity
LOINC|97062-4|2.38
Both reference CodeStub objects — lightweight pointers to full Code entities stored as root-level objects.
Full codification definition
label
searchTerms
region
Examples: SNOMED-CT, LOINC, ICD-10, ATC
Lightweight reference
code
version
tags
codes
Code: { id: "SNOMED-CT|200773006|1", label: { en: "Allergic rhinitis" } } CodeStub: { id: "SNOMED-CT|200773006|1", type: "SNOMED-CT", code: "200773006" }
Every entity also carries audit and lifecycle metadata
created
modified
author
responsible
deletionDate
Soft deletion: Entities are never physically removed — deletionDate marks them as deleted while preserving the audit trail.
Patient → Contact → Service
┌─────────────────────────────────────────────────────────┐ │ PATIENT │ │ The person receiving care │ │ │ │ ┌───────────────────────────────────────────────────┐ │ │ │ CONTACT │ │ │ │ One encounter / event │ │ │ │ │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ │ │ SERVICE │ │ SERVICE │ │ SERVICE │ │ │ │ │ │ Blood │ │ Diagnosis │ │ Prescr. │ │ │ │ │ │ pressure │ │ │ │ │ │ │ │ │ │ ┌───────┐ │ │ ┌───────┐ │ │ ┌───────┐ │ │ │ │ │ │ │CONTENT│ │ │ │CONTENT│ │ │ │CONTENT│ │ │ │ │ │ │ └───────┘ │ │ └───────┘ │ │ └───────┘ │ │ │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ └───────────────────────────────────────────────────┘ │ │ │ │ ┌───────────────────────────────────────────────────┐ │ │ │ CONTACT (another visit) │ │ │ │ ... │ │ │ └───────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────┘
The subject of medical treatment — the central entity
firstName
lastName
dateOfBirth
gender
birthSex
addresses
ssin
nationality
languages
picture
🔒 Encrypted by default: notes[].markdown • FuzzyDate format: YYYYMMDD (e.g., 19850315)
A single encounter or event — consultation, lab result, phone call
openingDate
closingDate
descr
encounterType
services
subContacts
🔒 Encrypted by default: descr, notes[].markdown, and all Services
The atomic unit of medical information
Represents any observation or action relevant to a patient's health:
content
valueDate
Service stores its value in a Content object — supports many data types
numberValue
120.5
stringValue
"Normal sinus rhythm"
booleanValue
true
measureValue
{ value: 120, unit: "mmHg", min: 90, max: 140 }
medicationValue
timeSeries
fuzzyDateValue
20240315
binaryValue
documentId
compoundValue
Patient: John Doe (id: p-123) │ └── Contact: "Annual checkup — 2024-03-15" ( descr encrypted) │ ├── Service: "Blood Pressure" │ └── Content: measureValue { value: 120, unit: "mmHg" } │ ├── Service: "Heart Rate" │ └── Content: measureValue { value: 72, unit: "bpm" } │ ├── Service: "Diagnosis" │ └── Content: stringValue "Mild hypertension" │ └── codes: [SNOMED-CT|38341003|1] │ └── Service: "Prescription" └── Content: medicationValue { ... }
Links Services to persistent structures. Logical organisation of the services. As opposed to contacts that provide a temporal organisation.
HealthElement: "Hypertension" (ongoing condition) │ Time │ Logical link │ Contact 2024-01 ─── SubContact ─── Service: BP 140/90 │ Contact 2024-03 ─── SubContact ─── Service: BP 130/85 │ Contact 2024-06 ─── SubContact ─── Service: BP 125/80 │ Contact 2024-09 ─── SubContact ─── Service: BP 120/78 ▼
SubContacts can reference:
HealthElement, HealthcareParty, Device & User
A medical condition or event that persists over time
note
notes[].markdown
careTeam
episodes
plansOfAction
Any actor in patient care
The patient themselves:
Medical device or software
Login identity, linked to max. one of:
Privacy by design, not by policy
Medical data requires the highest level of protection
The Cardinal guarantee: Even if the server is fully compromised, patient data remains encrypted. The server never sees plaintext medical content.
┌─────────────────────────────────────────────────────────┐ │ Layer 1: PERSONAL KEYS (RSA) │ │ One per Data Owner — stored on device only │ │ Used to decrypt exchange keys │ ├─────────────────────────────────────────────────────────┤ │ Layer 2: EXCHANGE KEYS (AES) │ │ One per delegator → delegate pair │ │ Encrypted with both parties' public RSA keys │ │ Signed with HMAC-SHA256 │ ├─────────────────────────────────────────────────────────┤ │ Layer 3: ENTITY KEYS (AES) │ │ One per encryptable entity │ │ Encrypts actual medical content │ │ Stored encrypted in SecureDelegation │ └─────────────────────────────────────────────────────────┘
Dr. Alice Dr. Bob ┌──────┐ ┌──────┐ │ RSA │ │ RSA │ │ Keys │ │ Keys │ └──┬───┘ └──┬───┘ │ ┌──────────────────┐ │ │ decrypt │ Exchange Key │ decrypt │ ├────────────> │ (AES) │ <─────────────┤ │ │ encrypted with │ │ │ │ both RSA keys │ │ │ └────────┬─────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────┐ │ │ │ Entity Key │ │ │ │ (AES) │ │ │ └────────┬─────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────┐ │ │ │ Encrypted │ │ │ │ Patient Data │ │ │ └──────────────────┘ │
Internal encrypted metadata (all encryptable entities):
encryptedSelf
encryptionKeys
securityMetadata
secretForeignKeys
cryptedForeignKeys
How data owners share access while preserving privacy
┌───────────────────────────────────────────────┐ │ SecurityMetadata │ │ │ │ ┌──────────────────────────────────────────┐ │ │ │ SecureDelegation (Alice → Bob) │ │ │ │ • Exchange data ID │ │ │ │ • Encrypted entity key │ │ │ │ • Access level: READ or READ_WRITE │ │ │ │ • Delegation key (hashed) │ │ │ └──────────────────────────────────────────┘ │ │ │ │ ┌──────────────────────────────────────────┐ │ │ │ SecureDelegation (Alice → Patient) │ │ │ │ • Exchange data ID │ │ │ │ • Encrypted entity key │ │ │ │ • Access level: READ │ │ │ │ • Delegation key (hashed, anonymous) │ │ │ └──────────────────────────────────────────┘ │ └───────────────────────────────────────────────┘
HCPs & some Devices
Patients
Key insight: Even if the server knows Dr. Alice shared data with someone, it cannot determine that the someone is Patient Jane — unless it has the exchange key.
How Patient Contact links stay private
┌─────────────────────┐ │ Patient │ │ id: p-123 │ │ secretId: s-xyz │ └────────┬────────────┘ │ (encrypted link) ┌──────────────┼──────────────┐ ▼ ▼ ▼ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ Contact A │ │ Contact B │ │ Contact C │ │ secretId: │ │ secretId: │ │ secretId: │ │ s-xyz │ │ s-xyz │ │ s-xyz │ └────────────┘ └────────────┘ └────────────┘
s-xyz
p-123
The SDK handles all cryptographic complexity automatically
// Initialize encryption metadata before creating an entity const contact = new DecryptedContact({ descr: "Annual checkup" }) // SDK encrypts, creates delegations, manages keys const created = await sdk.contact.createContactWithPatient( patient, contact, { delegates: { [otherDoctorId]: AccessLevel.Write } } ) // SDK decrypts transparently on retrieval const retrieved = await sdk.contact.getContact(created.id) console.log(retrieved.descr) // "Annual checkup" ← decrypted
Shared structures (id, rev, identifiers, tags, codes) provide consistency across all entities
identifiers
Patient → Contact → Service models the chronological medical record with Content as the data container
HealthElement + SubContact add persistent medical context across encounters
Three-layer encryption (RSA → AES Exchange → AES Entity) ensures zero-knowledge server architecture
Secure delegations enable fine-grained sharing with anonymous patient support
The SDK abstracts it all — developers focus on medical data, not cryptography
Cardinal SDK Documentation docs.icure.com