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('')
}
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:
| Frequency | Period Format | Example |
|---|---|---|
| Daily | ISO date | 2026-04-08 |
| Weekly | ISO week | 2026-W15 |
| Biweekly | ISO month | 2026-04 |
| Monthly | ISO month | 2026-04 |
| Quarterly | Quarter | 2026-Q2 |
| Annually | Year | 2026 |
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
| State | evaluated | fulfilled | proofHash | Description |
|---|---|---|---|---|
| Submitted | false | null | null | Awaiting oracle cron |
| Pass | true | true | 0x... | Oracle verified, credits awarded |
| Fail | true | false | 0x... | Oracle verified, criteria not met |
Data Sources
| Source | Code | Description |
|---|---|---|
| Manual Upload | manual_upload | Patient types/uploads value directly |
| HealthKit Sync | healthkit_sync | Auto-sync from Apple Health |
| Wearable Sync | wearable_sync | Auto-sync from Fitbit, Garmin, etc. |
| Lab Import | lab_import | Parsed from FHIR/CCDA lab results |
| Sovereign Wallet | sovereign_wallet | Submitted 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
| Threat | Mitigation |
|---|---|
| Replay attack | Period-based deduplication + commitment includes salt |
| Value manipulation after submission | Commitment is binding (Poseidon/SHA-256 preimage) |
| Salt reuse | Fresh 32-byte salt per submission |
| Oracle compromise | Proofs anchored in Merkle tree before oracle can tamper |
| Client-side key theft | Keys stored in platform keychain (iOS Keychain, Android Keystore, Electron safeStorage) |