Subscriber Data Encryption
Relevant source files
The following files were used as context for generating this wiki page:- newsletter-program/src/core/subscriber-file-manager.js
- newsletter-program/README-SUBSCRIBER-MANAGEMENT.md
- newsletter-program/src/cli/subscriber-cli.js
- newsletter-program/src/core/secure-export.js
- newsletter-program/src/tests/integration/newsletter-subscription-flow.test.js
- themes/matrix-flow/README-newsletter.md
The subscriber management system is built with a strong emphasis on data privacy and security. A core component of this strategy is the encryption of sensitive subscriber information, specifically email addresses. This system uses a file-based architecture, storing all subscriber data in an encrypted JSON file, subscribers.encrypted.json. The encryption mechanism ensures that all email addresses are encrypted at rest using the AES-256-CBC algorithm, aligning with GDPR requirements for data protection.
This document outlines the architecture, data flow, and management of the subscriber data encryption feature. It covers the core components responsible for encryption and decryption, the structure of the encrypted data, and the command-line tools used to interact with the secure data store.
Sources: newsletter-program/README-SUBSCRIBER-MANAGEMENT.md, themes/matrix-flow/README-newsletter.md:29-30
Architecture
The encryption system is primarily managed by two core modules: SubscriberFileManager and EncryptionManager. These modules work together to abstract the complexity of encryption from other parts of the application, such as the CLI or potential web integrations.
EncryptionManager: A dedicated class responsible for all cryptographic operations. It handles the encryption and decryption of data using AES-256.SubscriberFileManager: This class manages all CRUD (Create, Read, Update, Delete) operations on thesubscribers.encrypted.jsonfile. It orchestrates the process of reading the file, decrypting emails for in-memory use, and encrypting emails before writing back to the file.subscribers.encrypted.json: A JSON file that serves as the database. It stores subscriber information with email addresses in an encrypted format.
The following diagram illustrates the relationship between these components.
This diagram shows how an external caller (like a CLI) interacts with the SubscriberFileManager, which in turn uses the EncryptionManager and the ENCRYPTION_KEY to securely read from and write to the subscribers.encrypted.json data file.
Sources: newsletter-program/README-SUBSCRIBER-MANAGEMENT.md, newsletter-program/src/core/subscriber-file-manager.js:5-10
Data Flow
Encryption Process (Adding a Subscriber)
When a new subscriber is added, their email address is immediately encrypted before being persisted to the disk. The SubscriberFileManager.addSubscriber() method orchestrates this process.
- The
addSubscribermethod receives the new subscriber's data, including the plaintext email. - It constructs a new subscriber object.
- Before saving, the
saveSubscribersmethod is called. This method iterates through all subscribers. - For each subscriber, it calls
encryption.encryptEmail()to get the encrypted version of the email. - The plaintext
emailproperty is removed, and theencryptedEmailproperty is added. - The entire data structure, now with encrypted emails, is written to
subscribers.encrypted.json.
Sources: newsletter-program/src/core/subscriber-file-manager.js:101-110, newsletter-program/src/core/subscriber-file-manager.js:127-149
The sequence diagram below details this flow.
This diagram shows the sequence of operations when adding a new subscriber, highlighting the call to the EncryptionManager before writing to the file system.
Sources: newsletter-program/src/core/subscriber-file-manager.js:101-110, 127-149
Decryption Process (Loading Subscribers)
When the application needs to work with subscriber data (e.g., to list subscribers or send a newsletter), the SubscriberFileManager.loadSubscribers() method reads the encrypted file and decrypts the emails for in-memory processing.
- The
loadSubscribersmethod reads the raw JSON content fromsubscribers.encrypted.json. - It parses the JSON into a JavaScript object.
- It iterates through the
subscribersarray. - For each subscriber, it calls
encryption.decryptEmail()on theencryptedEmailfield. - A new
emailproperty with the plaintext value is added to the in-memory subscriber object. - The method returns the full data object with all subscriber emails decrypted.
Sources: newsletter-program/src/core/subscriber-file-manager.js:50-80
Encrypted Data Structure
The subscriber data is stored in subscribers.encrypted.json. The root object contains metadata about the file, and the subscribers array holds the individual records. Within each subscriber object, the email is stored in the encryptedEmail field as a string.
The structure of the encryptedEmail object, as described in the documentation, includes the encrypted data, the initialization vector (IV), and the key version, which is crucial for key rotation. However, the implementation in subscriber-file-manager.js appears to treat encryptedEmail as a single string, suggesting the IV and encrypted data might be concatenated.
{
"version": "1.0.0",
"createdAt": "2025-10-31T10:00:00Z",
"lastModified": "2025-10-31T15:30:00Z",
"keyVersion": "env-v1",
"subscribers": [
{
"id": "uuid",
"encryptedEmail": "iv_hex:encrypted_hex",
"status": "active",
"subscribedAt": "2025-10-31T10:00:00Z",
"source": "website",
"preferences": {
"frequency": "weekly",
"topics": ["development"]
},
"unsubscribeToken": "secure-token"
}
]
}
This JSON snippet shows the structure of the subscribers.encrypted.json file, with the encryptedEmail field holding the protected data.
Sources: newsletter-program/README-SUBSCRIBER-MANAGEMENT.md, newsletter-program/src/core/subscriber-file-manager.js:105-109
Security and Key Management
Algorithm and Configuration
The system employs a robust encryption standard to protect subscriber data.
| Feature | Description |
|---|---|
| Algorithm | AES-256-CBC is used for symmetric encryption. |
| Key Source | The 32-byte encryption key is read from the ENCRYPTION_KEY environment variable. It must be Base64 encoded. |
| IV Generation | A random 16-byte Initialization Vector (IV) is generated for each encryption operation to ensure uniqueness. |
Sources: newsletter-program/README-SUBSCRIBER-MANAGEMENT.md, newsletter-program/src/tests/integration/newsletter-subscription-flow.test.js:29-31
Key Generation and Rotation
A new encryption key can be generated using a Node.js command.
node -e "
const crypto = require('crypto');
const key = crypto.randomBytes(32).toString('base64');
console.log('ENCRYPTION_KEY=' + key);
"
Sources: newsletter-program/README-SUBSCRIBER-MANAGEMENT.md
The system includes a CLI command for key rotation, npm run subscribers rotate-key. This command is intended to re-encrypt all data with a new key. However, the underlying function migrateToNewKey() in SubscriberFileManager is not yet implemented and will throw an error.
Sources: newsletter-program/src/cli/subscriber-cli.js:258-274, newsletter-program/src/core/subscriber-file-manager.js:464-468
Data Anonymization for Exports
In addition to encryption for data at rest, the SecureExportManager provides functionality for anonymizing data during exports. This is achieved by applying a one-way SHA256 hash to email addresses. This is used when creating anonymized datasets for research or analysis, ensuring that personally identifiable information is removed.
This diagram shows the email anonymization process using SHA256 hashing during a secure data export.
Sources: newsletter-program/src/core/secure-export.js:93-100, newsletter-program/src/core/secure-export.js:18-20
Summary
The subscriber data encryption system is a critical feature for ensuring user privacy and GDPR compliance. By using AES-256 encryption for emails at rest, managing keys via environment variables, and abstracting cryptographic operations within dedicated managers, the system provides a secure foundation for the newsletter program. The file-based approach is protected by ensuring that sensitive data within subscribers.encrypted.json is never stored in plaintext. Further security is provided through one-way hashing for data anonymization in exports.