Skip to main content

L{CORE} Privacy Architecture

Attribution: The attestor layer of L{CORE} is built on Reclaim Protocol's attestor-core. This documentation describes how we've extended their open-source TEE infrastructure for IoT attestation use cases.

Last Updated: 2025-01-14 Status: Production Design


About This Architecture

L{CORE} combines two open-source projects to create decentralized IoT attestation:

  1. Reclaim Protocol's attestor-core — The TEE-secured attestation layer that handles proof generation, signature verification, and zkTLS. This is Reclaim's code, not ours.

  2. Cartesi Rollups — The deterministic compute layer that stores attestations in SQLite with fraud-proof verification.

Our contribution is the IoT integration layer: device SDKs (C, Python, TypeScript), did:key identity management, privacy bucketing for sensor data, and the bridge between IoT devices and Reclaim's attestor infrastructure.


Why L{CORE}?

BenefitDescription
No Lock-InDeploy on any EVM chain. Run on any infrastructure. Switch chains without rewriting your application.
No FeesZero protocol fees. Zero token requirements. You pay gas costs on your chosen chain—that's it.
Full ComputeNot a sandbox. Full Linux environment. Run SQLite, Python libraries, existing codebases—anything that runs on Linux.
Self-SovereignRun your own attestors. Own your infrastructure. No dependency on external networks or third-party uptime.
Device-FirstC SDK for resource-constrained embedded devices. Real IoT attestation, not just mobile apps.

Compatibility

ComponentSupported
ChainsArbitrum, Arbitrum Orbit chains (Locale Network, City Chains)
InfrastructureSelf-hosted, AWS, GCP, Azure, EigenCloud TEE
DevicesESP32, Arduino, ARM Cortex-M, Raspberry Pi, NVIDIA Jetson
LanguagesPython, TypeScript, C

Limitations

LimitationDetails
EVM onlyCurrently requires EVM-compatible chain for settlement
Determinism requiredCartesi rollup code must be deterministic (no external network calls, no random)
TEE for productionFull security guarantees require TEE deployment (EigenCloud)
LatencyOn-chain settlement adds latency vs centralized alternatives

Table of Contents

  1. Executive Summary
  2. Problem Statement
  3. Architecture Overview
  4. EigenCloud Deployment Architecture
  5. Encryption Model
  6. Data Flow Diagrams
  7. Database Schema
  8. Access Control Model
  9. API Reference
  10. Key Management
  11. Security Considerations
  12. Compliance
  13. Deployment Checklist

Executive Summary

L{CORE} is a privacy-preserving attestation data layer built on Cartesi rollups (Arbitrum Sepolia L2). It stores user attestation data (financial info, employment status, identity claims) in a way that:

  1. Keeps data in Cartesi - Full database lives in Cartesi's SQLite, benefiting from fraud-proof verification
  2. Protects PII - All sensitive outputs are encrypted; only authorized parties can decrypt
  3. Enables privacy-preserving queries - Aggregate statistics available publicly, individual data protected
  4. Maintains regulatory compliance - GDPR/CCPA compliant by design

Built With

  • Reclaim Protocol - zkTLS proofs for data verification
  • Cartesi - Deterministic Linux VM for verifiable computation
  • EigenCloud - TEE infrastructure for secure deployment

The Core Innovation

All Cartesi outputs (notices, reports) containing sensitive data are encrypted with an admin public key. Only the TEE Attestor holds the corresponding private key and can decrypt responses. External parties see only encrypted blobs.


Problem Statement

Cartesi's Transparency Model

Cartesi is designed for verifiable computation, not private computation:

  • InputBox - Anyone can submit inputs (public)
  • Notices - Published on-chain, publicly verifiable (public)
  • Reports - Returned to callers, logged (semi-public)
  • Vouchers - Execute on L1 (public)

This transparency is a feature for gaming, DeFi calculations, and governance. But it's a critical flaw for personal data.

Our Requirements

RequirementCartesi DefaultOur Need
Attestation data storagePublicPrivate
Query responsesPublicEncrypted
Aggregate statisticsN/APublic (anonymized)
Access controlNoneRole-based
PII protectionNoneMandatory
  • GDPR (EU): Personal data must be protected; data subjects control access
  • CCPA (California): Consumers can opt out of data sharing
  • GLBA (US Financial): Financial data requires safeguards

Exposing PII on a public blockchain violates all of these.


Architecture Overview

flowchart TB
subgraph Clients["External Clients"]
U[Users via dApp]
D[dApps - Locale, Marketplace]
A[Admins Dashboard]
end

subgraph TEE["TEE Attestor Container :8001"]
AUTH[Authentication Layer]
AUTHZ[Authorization Layer]
KEY[Admin Private Key]

AUTH --> AUTHZ
AUTHZ --> KEY
end

subgraph Cartesi["Cartesi Rollup Container :10000"]
RS[Rollup Server]
DAPP[L\{CORE\} DApp]
PUB[Admin Public Key]
DB[(SQLite Database)]

RS --> DAPP
DAPP --> PUB
DAPP --> DB
end

subgraph Chain["Arbitrum Sepolia"]
IB[InputBox Contract]
FP[Fraud Proof System]
end

Clients -->|HTTPS| TEE
TEE -->|Internal HTTP| Cartesi
Cartesi -->|Settlement| Chain

style TEE fill:#4CAF50,color:#fff
style Cartesi fill:#2196F3,color:#fff
style Chain fill:#9C27B0,color:#fff

Component Responsibilities

ComponentResponsibility
TEE AttestorAuthentication, authorization, encryption/decryption, external API
Cartesi RollupState management, deterministic computation, SQLite storage
ArbitrumSettlement, fraud proofs, InputBox contract

EigenCloud Deployment Architecture

L{CORE} runs as two separate containers in EigenCloud's TEE environment:

flowchart TB
subgraph EigenCloud["EigenCloud TEE Infrastructure"]
subgraph C1["Container 1: Attestor"]
direction TB
A1[Node.js 20]
A2[Reclaim SDK]
A3[Encryption Service]
A4[External APIs]

A1 --> A2
A1 --> A3
A1 --> A4
end

subgraph C2["Container 2: Cartesi Node"]
direction TB
B1[RISC-V VM]
B2[L\{CORE\} DApp]
B3[(SQLite)]

B1 --> B2
B2 --> B3
end
end

subgraph External["External Services"]
RC[Reclaim Protocol]
SB[(Supabase)]
RPC[Blockchain RPC]
end

C1 <-->|Port 10000| C2
C1 --> External

style C1 fill:#4CAF50,color:#fff
style C2 fill:#2196F3,color:#fff

Why Two Containers?

RequirementAttestorCartesi Node
External NetworkYES - Reclaim, Supabase, RPCNO - Must be deterministic
State ManagementStatelessFull SQLite state
ScalingHorizontalVertical
Port800110000

Critical Design Decision: Cartesi rollups MUST be deterministic to enable fraud proofs. Any external network call would break determinism. By separating containers:

  1. Attestor handles all non-deterministic operations (external APIs, encryption with random nonces)
  2. Cartesi Node remains purely deterministic (state derived only from InputBox inputs)

Production Deployment

ServiceIP AddressPortImage
Cartesi Node${CARTESI_NODE_IP}10000modernsociety/lcore-cartesi-node:eigencloud
Attestor${ATTESTOR_IP}8001modernsociety/lcore-attestor:eigencloud

Contract Addresses (Arbitrum Sepolia)

ContractAddress
DApp0xAE0863401D5B953b89cad8a5E7c98f5136E9C26d
InputBox0x59b22D57D4f067708AB0c00552767405926dc768
Authority0x08cC70a34EA78F35a871822F685dCB99EE079A08
History0xF1A186AFC0C794dA242fcE50052592dDA30F0457

Encryption Model

Overview

We use asymmetric encryption (public/private key pair) to protect sensitive outputs:

sequenceDiagram
participant C as Cartesi DApp
participant A as Attestor (TEE)
participant U as User/dApp

Note over C: Has Admin PUBLIC Key
Note over A: Has Admin PRIVATE Key

C->>C: Prepare sensitive response
C->>C: Generate ephemeral keypair
C->>C: Encrypt with admin public key
C->>A: Return encrypted blob
A->>A: Decrypt with admin private key
A->>A: Generate TEE signature proof
A->>U: Return plaintext + proof

Encryption Algorithm

Algorithm: X25519-XSalsa20-Poly1305 (NaCl "box")

Why this algorithm:

  • Fast (important for RISC-V performance)
  • Well-audited (libsodium/tweetnacl)
  • Authenticated encryption (integrity + confidentiality)
  • Available in both Node.js and RISC-V

What Gets Encrypted

Data TypeEncrypted?Reason
Attestation data (parameters, context)YESContains PII
Attestation metadata (id, owner, provider)YESCan identify individuals
Bucket values for specific usersYESReveals financial info
Aggregate counts ("47 users have >$10k")NOAnonymized, no PII
Provider schemasNOPublic configuration
Schema admins listNOPublic configuration
Error messagesNONo PII

Encryption Format

interface EncryptedOutput {
version: 1; // Schema version for future upgrades
algorithm: 'nacl-box'; // Encryption algorithm identifier
nonce: string; // Base64-encoded 24-byte nonce
ciphertext: string; // Base64-encoded encrypted data
publicKey: string; // Ephemeral public key for this message
}

Decryption Proofs

When the Attestor decrypts an encrypted response, it generates a decryption proof - a TEE signature proving that the decryption was performed correctly by a trusted enclave.

interface DecryptionProof {
ciphertextHash: string; // SHA256 of encrypted payload
plaintextHash: string; // SHA256 of decrypted JSON
timestamp: number; // Unix timestamp of decryption
teeAddress: string; // Attestor wallet address
signature: string; // ECDSA signature
}

Data Flow Diagrams

Flow 1: Attestation Ingestion

sequenceDiagram
participant U as User dApp
participant A as Attestor (TEE)
participant R as Rollup Server
participant L as L\{CORE\} DApp

U->>A: 1. Create claim
A->>A: 2. Sign claim (TEE signature)
A->>A: 3. Discretize (bucketize data)
A->>R: 4. Submit to /input/advance
R->>L: 5. Forward to DApp
L->>L: 6. Verify TEE signature
L->>L: 7. Check schema exists
L->>L: 8. Store attestation
L->>L: 9. Store buckets
L->>L: 10. Store encrypted data
L->>R: 11. Encrypted Notice
R->>A: 12. Encrypted response
A->>A: 13. Decrypt with admin key
A->>U: 14. Success response

Flow 2: dApp Query (with Grant)

sequenceDiagram
participant D as dApp (Locale)
participant A as Attestor (TEE)
participant R as Rollup Server
participant L as L\{CORE\} DApp

D->>A: 1. Query with API key + grant
A->>A: 2. Verify API key
A->>R: 3. Submit to /inspect
R->>L: 4. Forward to DApp
L->>L: 5. Check grant exists
L->>L: 6. Verify not expired
L->>L: 7. Fetch attestation
L->>L: 8. Encrypt response
L->>R: 9. Encrypted Report
R->>A: 10. Encrypted response
A->>A: 11. Decrypt with admin key
A->>D: 12. Plaintext data

Flow 3: Public Aggregate Query

sequenceDiagram
participant P as Anyone (Public)
participant A as Attestor
participant R as Rollup Server
participant L as L\{CORE\} DApp

P->>A: 1. Aggregate query (no auth)
A->>R: 2. Submit query
R->>L: 3. Forward to DApp
L->>L: 4. Compute aggregate
L->>L: 5. Check k-anonymity
L->>L: 6. Return COUNT only (NOT encrypted)
L->>R: 7. Plaintext Report
R->>A: 8. Plaintext response
A->>P: 9. { count: 47 }

Database Schema

Tables Overview

erDiagram
provider_schemas {
string provider PK
string flow_type PK
int version
string domain
json bucket_definitions
json data_keys
int freshness_half_life
}

schema_admins {
string wallet_address PK
string added_by
bool can_add_providers
bool can_add_admins
}

encryption_config {
string key_id PK
string public_key
string algorithm
int created_at
}

attestations {
string id PK
string attestation_hash
string owner_address
string domain
string provider
string flow_type
int valid_from
int valid_until
string tee_signature
string status
float freshness_score
}

attestation_buckets {
string attestation_id FK
string bucket_key
string bucket_value
}

attestation_data {
string attestation_id FK
string data_key
string encrypted_value
string encryption_key_id
}

access_grants {
string id PK
string attestation_id FK
string grantee_address
string granted_by
json data_keys
string grant_type
int expires_at_input
string status
}

attestations ||--o{ attestation_buckets : has
attestations ||--o{ attestation_data : has
attestations ||--o{ access_grants : has
provider_schemas ||--o{ attestations : defines

Privacy Classifications

TableClassificationNotes
attestationsPRIVATEContains owner addresses, can identify users
attestation_bucketsPRIVATEReveals financial info
attestation_dataPRIVATERaw PII (already encrypted)
access_grantsPRIVATELinks entities
provider_schemasPUBLICConfiguration only
schema_adminsPUBLICAdmin list
encryption_configPUBLICPublic key is public

Access Control Model

Actor Types

ActorAuthenticationCan Access
AnonymousNonePublic aggregates only
UserWallet signatureTheir own attestation metadata, grant management
dAppAPI keyAttestations they've been granted access to
AdminAdmin session tokenEverything

Permission Matrix

ActionAnonymousUserdAppAdmin
View aggregate counts
View own attestation list
View attestation data✅ (with grant)
Create access grant✅ (own data)
Revoke access grant✅ (own grants)
Register provider schema
Add schema admin
View all attestations

API Reference

Cartesi Inspect Queries (Port 10000)

Direct queries to the Cartesi node use URL-encoded JSON:

# Query format
curl "http://${CARTESI_NODE_IP}:10000/inspect/$(python3 -c "import urllib.parse; print(urllib.parse.quote('{\"type\":\"QUERY_TYPE\",\"params\":{}}'))")"

Available Query Types:

Query TypeParametersDescription
all_provider_schemas{}List all registered provider schemas
attestations_by_owner{owner, limit, offset}Get attestations for an owner
check_access{attestation_id, grantee}Check if grantee has access
aggregate_by_bucket{domain, bucket_key}Get anonymized bucket counts

Attestor API Endpoints (Port 8001)

Public Endpoints

GET /api/health
GET /api/lcore/health
GET /api/lcore/status
GET /api/lcore/aggregates/count-by-bucket?domain=lending&bucket_key=balance
GET /api/lcore/aggregates/count-by-provider?domain=lending

User Endpoints (Wallet Signature Required)

POST /api/lcore/user/attestations
POST /api/lcore/user/grants
POST /api/lcore/user/revoke-grant

dApp Endpoints (API Key Required)

GET /api/lcore/dapp/attestation/:attestation_id
GET /api/lcore/dapp/grants

Admin Endpoints (Admin Auth Required)

POST /api/lcore/schema-admin
POST /api/lcore/provider-schema
GET /api/lcore/admin/attestations

Key Management

Key Separation

KeyPurposeAlgorithmLocation
Admin Encryption KeyEncrypt/decrypt L{CORE} responsesNaCl box (X25519)Private in TEE, Public in Cartesi
TEE Signing KeySign attestations & proofsECDSA (secp256k1)Private in TEE

Key Generation

node -e "
const nacl = require('tweetnacl');
const keypair = nacl.box.keyPair();
console.log('LCORE_ADMIN_PUBLIC_KEY=' + Buffer.from(keypair.publicKey).toString('base64'));
console.log('LCORE_ADMIN_PRIVATE_KEY=' + Buffer.from(keypair.secretKey).toString('base64'));
"

Key Storage

KeyLocationProtection
Admin Public KeyCartesi encryption_config tableNone needed (public)
Admin Private KeyAttestor environment variableTEE enclave

Security Considerations

Threat Model

ThreatMitigation
Anyone reads Cartesi outputsOutputs encrypted with admin key
Attacker submits malicious inputSignature verification in DApp
Replay attack on queriesNonce/timestamp in signed messages
TEE compromiseKey rotation capability, audit logs
Admin key theftKey stored in TEE, never transmitted
Brute force on encrypted dataNaCl box is computationally secure

What We Don't Protect Against

ThreatWhy Not Mitigated
Compromised TEE operatorTrust assumption; use remote attestation
Quantum computingNaCl not post-quantum; upgrade path available
Side-channel in RISC-VOut of scope for application layer

Compliance

GDPR Compliance

RequirementHow We Comply
Lawful basisUser consent (creates attestation voluntarily)
Data minimizationOnly store what's needed, bucketize
Right to accessUser can query their attestations
Right to erasureRevocation marks as deleted
Data protectionEncrypted outputs, access control
Breach notificationEncrypted data; breach reveals nothing

CCPA Compliance

RequirementHow We Comply
Right to knowUser can list their attestations
Right to deleteRevocation available
Right to opt-outUser controls grants
Non-discriminationAccess control is uniform

Deployment Checklist

Pre-Deployment

  • Generate admin keypair
  • Store private key in TEE-protected environment
  • Configure LCORE_ADMIN_PRIVATE_KEY environment variable
  • Build Docker images (attestor + cartesi-node)

Deployment

  • Push images to Docker Hub
  • Deploy Cartesi Node container to EigenCloud
  • Configure CARTESI_HTTP_ADDRESS=0.0.0.0
  • Deploy Attestor container to EigenCloud
  • Configure LCORE_NODE_URL to point to Cartesi Node

Post-Deployment Verification

  • Test Attestor health: curl http://ATTESTOR_IP:8001/api/health
  • Test Cartesi inspect: curl http://CARTESI_IP:10000/inspect/...
  • Test encrypted output flow end-to-end
  • Verify plaintext never appears in Cartesi outputs
  • Test access control (user, dApp, admin roles)

Ongoing

  • Monitor for key rotation needs
  • Review access logs for anomalies
  • Update provider schemas as needed
  • Regular security audits

Document History

VersionDateChanges
2.02025-01-14Updated for EigenCloud deployment, Mermaid diagrams
1.02025-01-12Initial architecture design