Encryption Architecture

End-to-End Encryption for Sensitive Data

Overview

Snuggli implements AES-256-GCM application-level encryption for all sensitive user data. This ensures that even if the database is compromised, user content remains encrypted and unreadable.

Encrypted Data Types

  • Araba Messages: All chat content between users and Araba
  • Journal Entries: Title, content, and gratitude items
  • Mood Notes: Personal notes attached to mood entries
  • Status Posts: User-generated content in community
  • Conversation Previews: Message summaries

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                        CLIENT (Browser)                          │
├─────────────────────────────────────────────────────────────────┤
│  ┌──────────────┐    ┌───────────────┐    ┌──────────────────┐ │
│  │ User writes  │───▶│ Encrypt with  │───▶│ Send encrypted   │ │
│  │ content      │    │ derived key   │    │ to server        │ │
│  └──────────────┘    └───────────────┘    └──────────────────┘ │
│                              ▲                                   │
│                              │ Request key                       │
│                              ▼                                   │
│                    ┌───────────────────┐                        │
│                    │ Key Cache (5 min) │                        │
│                    └───────────────────┘                        │
└─────────────────────────────────────────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────────┐
│                    BACKEND FUNCTIONS                             │
├─────────────────────────────────────────────────────────────────┤
│  ┌──────────────────┐                                           │
│  │ getEncryptionKey │◀─── Master Key (ENV/KMS)                  │
│  │  - Auth user     │                                           │
│  │  - Derive key    │───▶ Per-user key = HKDF(master, user_id)  │
│  │  - Audit log     │                                           │
│  └──────────────────┘                                           │
│                                                                  │
│  ┌────────────────────┐                                         │
│  │ adminDecryptData   │◀─── SuperAdmin only                     │
│  │  - Require reason  │                                         │
│  │  - Create audit    │───▶ Immutable audit log                 │
│  │  - Decrypt         │                                         │
│  └────────────────────┘                                         │
│                                                                  │
│  ┌─────────────────────┐                                        │
│  │ rotateEncryptionKeys│◀─── Quarterly rotation                 │
│  │  - Admin only       │                                        │
│  │  - Re-encrypt all   │───▶ New key version                    │
│  └─────────────────────┘                                        │
└─────────────────────────────────────────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────────┐
│                        DATABASE                                  │
├─────────────────────────────────────────────────────────────────┤
│  ┌─────────────────────────────────────────────────────────┐   │
│  │ Message                                                  │   │
│  │  - content: "[ENCRYPTED]"                                │   │
│  │  - content_encrypted: "base64..."                        │   │
│  │  - content_iv: "base64..."                               │   │
│  │  - _encryption: { version: "v1", algorithm: "AES-GCM" }  │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                  │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │ SecurityLog (Immutable Audit Trail)                      │   │
│  │  - All key access logged                                 │   │
│  │  - All admin decryption logged                           │   │
│  │  - Cannot be deleted                                     │   │
│  └─────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────┘
            

Key Management

Master Key Storage

  • Stored in environment variables (ENCRYPTION_MASTER_KEY)
  • In production: Use AWS KMS, GCP KMS, or Azure Key Vault
  • NEVER stored in database or code repository
  • Access restricted to backend service role only

Per-User Key Derivation

  • Each user gets a unique encryption key
  • Derived using HKDF: key = HKDF(masterKey, userId + email)
  • Keys are never stored, always derived on-demand
  • Cached client-side for 5 minutes only

Key Rotation (Quarterly)

  • New master key set as ENCRYPTION_MASTER_KEY_NEW
  • Run rotation function to re-encrypt all data
  • Old key versions maintained for backward compatibility
  • Audit log created for all rotation events

Access Control

RoleCan Decrypt Own DataCan Decrypt Others' DataRequirements
User✓ Yes✗ NoAuthentication
Admin✓ Yes⚠ LimitedJustification + Audit Log
Support✓ Yes✗ NoMetadata only

Admin Decryption Warning

When admins access encrypted user data, they must provide a written justification. All access is permanently logged and cannot be deleted. Users may request access logs under GDPR/POPIA.

Developer Guide

Creating Encrypted Records

import { createEncrypted } from '@/components/security/EncryptedEntityWrapper';

// Instead of:
// await base44.entities.Message.create({ content: text });

// Use:
await createEncrypted('Message', { content: text });

Reading Encrypted Records

import { listDecrypted, filterDecrypted } from '@/components/security/EncryptedEntityWrapper';

// List with decryption
const messages = await listDecrypted('Message', '-created_date', 50);

// Filter with decryption
const myMessages = await filterDecrypted('Message', 
  { conversation_id: convId }, 
  'created_date', 
  100
);

React Query Integration

import { useEncryptedEntity } from '@/components/security/EncryptedEntityWrapper';

function MyComponent() {
  const messageEntity = useEncryptedEntity('Message');
  
  const { data } = useQuery({
    queryKey: ['messages'],
    queryFn: () => messageEntity.filter({ conversation_id: id }),
  });
  
  const mutation = useMutation({
    mutationFn: (content) => messageEntity.create({ content }),
  });
}

Data Deletion & Retention

Account Deletion

When a user deletes their account:

  • All encrypted records are permanently deleted
  • User's encryption key salt is invalidated
  • Deletion event is logged (without content)
  • No recovery is possible after deletion

Retention Policy

  • Chat messages: Retained for 12 months, then deleted
  • Journal entries: Retained indefinitely until user deletes
  • Mood data: Retained indefinitely until user deletes
  • Security logs: Retained for 7 years (compliance)

Key Rotation Procedure

  1. Generate new master key
    openssl rand -base64 32
  2. Set as ENCRYPTION_MASTER_KEY_NEW

    Add to environment variables in dashboard

  3. Run dry-run rotation
    await base44.functions.invoke('rotateEncryptionKeys', {
      newKeyVersion: 'v2',
      dryRun: true
    });
  4. Execute rotation
    await base44.functions.invoke('rotateEncryptionKeys', {
      newKeyVersion: 'v2',
      dryRun: false
    });
  5. Update primary key

    Move ENCRYPTION_MASTER_KEY_NEW to ENCRYPTION_MASTER_KEY

Schedule: Key rotation should be performed quarterly (every 90 days) or immediately if a key compromise is suspected.