본문으로 건너뛰기

Data Submission

Every platform — web, mobile, desktop wallet — generates cryptographic commitments client-side. The patient's raw data never leaves the device unencrypted. Only the compliance oracle can decrypt the value for evaluation.

Submission Flow

┌──────────────────────────────────────────────────────────────────┐
│ 1. PATIENT DEVICE │
│ │
│ value = 7500 (steps today) │
│ salt = randomBytes(32) │
│ dataType = "steps" │
│ │
│ commitment = Poseidon(salt, hash("steps"), 7500) │
│ → 0x1a2b3c4d... │
│ │
│ encrypted = NaCl.box(7500, oraclePublicKey) │
│ → 0x9f8e7d6c... │
│ │
│ Submit: { commitment, encrypted, salt, period: "2026-W15" } │
└──────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────┐
│ 2. BACKEND (stores, does NOT evaluate immediately) │
│ │
│ BlindSubmission { │
│ dataCommitment: "0x1a2b3c4d...", │
│ encryptedValue: "0x9f8e7d6c...", │
│ patientSalt: "a1b2c3...", │
│ evaluated: false │
│ } │
└──────────────────────────────────────────────────────────────────┘

▼ (cron, every 60 seconds)
┌──────────────────────────────────────────────────────────────────┐
│ 3. COMPLIANCE ORACLE │
│ │
│ patientValue = decrypt(encrypted) → 7500 │
│ threshold = decrypt(reqThreshold) → 5000 (hidden from patient) │
│ comparator = GTE │
│ │
│ 7500 >= 5000 → fulfilled = true │
│ │
│ proof = HMAC-SHA256(oracleSecret, "1|timestamp|hash|commitment")│
│ proofHash = SHA256(proof) → anchored in Merkle tree │
│ │
│ credits = baseCredit × activeMultiplier × dynamicMultiplier │
└──────────────────────────────────────────────────────────────────┘

Client Implementations

Sovereign Wallet (Electron) — Full Crypto

The desktop wallet has access to @ever-healthcare/ever-edh-core and can generate real Poseidon commitments:

import {
generateDataCommitment,
encodeSalt32,
scaleValue,
} from '@ever-healthcare/ever-edh-core/bio'

const salt = nacl.randomBytes(32)
const commitment = await generateDataCommitment(salt, 'steps', 7500)
// → Poseidon(salt[0..1], hash("steps"), 7500)
// → "0x1a2b3c4d..."

const encrypted = nacl.box(
new TextEncoder().encode('7500'),
nonce,
oraclePublicKey,
patientSecretKey,
)

Web (React SPA) — SHA-256 Fallback

Web browsers can't run Poseidon (requires circomlibjs WASM). The web client uses SHA-256 via Web Crypto API as a commitment fallback:

async function generateDataCommitment(salt, dataType, value) {
const data = new TextEncoder().encode(`${salt}|${dataType}|${value}`)
const hash = await crypto.subtle.digest('SHA-256', data)
return '0x' + Array.from(new Uint8Array(hash))
.map(b => b.toString(16).padStart(2, '0')).join('')
}
Commitment Compatibility

SHA-256 commitments are NOT compatible with Groth16 circuits (Phase 2). When ZK proofs are enabled, web clients will need to load the Poseidon WASM module or delegate commitment generation to a service worker.

Mobile (React Native) — SHA-256 Fallback

Similar to web, with platform-specific random number generation:

function generateSalt() {
const array = new Uint8Array(32)
// expo-crypto or react-native-get-random-values
crypto.getRandomValues(array)
return Array.from(array).map(b => b.toString(16).padStart(2, '0')).join('')
}

Period Format

Submissions are tagged with the period they cover:

FrequencyPeriod FormatExample
DailyISO date2026-04-08
WeeklyISO week2026-W15
BiweeklyISO month2026-04
MonthlyISO month2026-04
QuarterlyQuarter2026-Q2
AnnuallyYear2026

Duplicate Prevention

Each (enrollmentId, requirementKey, period) tuple allows exactly one submission. Resubmission for the same period is rejected with HTTP 409 Conflict.

Submission States

created → submitted → evaluated → [fulfilled | not_met]

credits_awarded

merkle_anchored
StateevaluatedfulfilledproofHashDescription
SubmittedfalsenullnullAwaiting oracle cron
Passtruetrue0x...Oracle verified, credits awarded
Failtruefalse0x...Oracle verified, criteria not met

Data Sources

SourceCodeDescription
Manual Uploadmanual_uploadPatient types/uploads value directly
HealthKit Synchealthkit_syncAuto-sync from Apple Health
Wearable Syncwearable_syncAuto-sync from Fitbit, Garmin, etc.
Lab Importlab_importParsed from FHIR/CCDA lab results
Sovereign Walletsovereign_walletSubmitted from desktop wallet with Poseidon

Design Improvement: Auto-Submission Pipeline

Currently, patients must manually trigger each submission. The improved design:

1. HealthKit/Google Fit Auto-Bridge

Wearable → HealthKit → Ever App Background Worker → Auto-Commit + Submit

Every night at midnight:
1. Read today's steps, heart rate, sleep from HealthKit
2. For each active enrollment requirement that matches:
a. Generate commitment
b. Encrypt value
c. Submit to /api/BlindSubmissions
3. Patient wakes up to "3 submissions auto-verified" notification

2. CCDA Auto-Parse

When a patient uploads a CCDA document (from a hospital visit), automatically extract compliance-relevant data:

CCDA Upload → Parse Vitals/Labs → Match to Enrollment Requirements → Auto-Submit

Example:
Patient uploads CCDA with:
- BP: 128/82
- Glucose: 105 mg/dL
- Weight: 74.2 kg

System finds matching requirements across all enrollments:
- Heart Failure program needs vitals → auto-submit BP + weight
- Diabetes program needs glucose → auto-submit glucose

3. Streaming Wearable Pipeline

For programs requiring daily data (heart rate, SpO2, activity):

Wearable SDK → Real-time Stream → Accumulator → Daily Commitment

Instead of asking the patient to submit daily:
- Background service accumulates readings throughout the day
- At end-of-day, commits the aggregated value
- Patient never touches anything — compliance is automatic

Security Considerations

ThreatMitigation
Replay attackPeriod-based deduplication + commitment includes salt
Value manipulation after submissionCommitment is binding (Poseidon/SHA-256 preimage)
Salt reuseFresh 32-byte salt per submission
Oracle compromiseProofs anchored in Merkle tree before oracle can tamper
Client-side key theftKeys stored in platform keychain (iOS Keychain, Android Keystore, Electron safeStorage)