Envelope Encryption System
Relevant source files
The following files were used as context for generating this wiki page:
The Envelope Encryption System is the core security architecture of Barekey, designed to manage encrypted environment variables (secrets) for developer teams. It implements a multi-tier key hierarchy where individual secret values are encrypted with project-specific keys, which are themselves protected by a master key. This system ensures that secrets remain encrypted at rest within the database while allowing for efficient, project-scoped key management and future rotation capabilities.
The system's primary goal is to provide a "vault" for sensitive data where the exposure of any single key is contained. Currently, encryption and decryption are handled server-side within Convex functions, though the architecture is designed to support a long-term goal of client-side encryption.
Sources: AGENTS.md:5-9, AGENTS.md:65-67, pkg/convex/lib/encryption.ts:133-140
Key Hierarchy and Architecture
The system uses a tiered approach to key management, often referred to as "envelope encryption." This structure allows the system to protect data at rest without requiring the master key to directly encrypt every individual piece of data.
Hierarchy Levels
- Master KEK (Key Encryption Key): A single 32-byte key stored as a Base64 string in the environment variable
BAREKEY_MASTER_KEY_B64. It is used to "wrap" or encrypt the per-project DEKs. - Project DEK (Data Encryption Key): A unique AES-GCM key generated for each project. This key is stored in the
projectKeystable, encrypted by the Master KEK. - Encrypted Secret: The actual environment variable value, encrypted using the Project's DEK.
Sources: AGENTS.md:73-82, pkg/convex/lib/encryption.ts:7-11, pkg/convex/lib/encryption.ts:68-80
Encryption Flow Diagram
The following diagram illustrates the relationship between the Master KEK, Project DEKs, and individual secrets.
Implementation Details
The system relies on the Web Crypto API (crypto.subtle) for all cryptographic operations. It specifically utilizes the AES-GCM algorithm for both key wrapping and data encryption.
Technical Constants
| Constant | Value | Description |
|---|---|---|
AES_GCM_ALGORITHM | "AES-GCM" | The primary encryption algorithm used. |
DEK_BYTES_LENGTH | 32 | 256-bit key length for AES. |
GCM_IV_BYTES_LENGTH | 12 | Standard Initialization Vector length for GCM. |
MASTER_KEY_ENV_NAME | "BAREKEY_MASTER_KEY_B64" | The environment variable name for the Master KEK. |
Sources: pkg/convex/lib/encryption.ts:5-10
Key Management Functions
The system manages the lifecycle of Project DEKs through the ensureProjectDek function. This function performs a "lookup-or-create" operation:
- If a key already exists for the project, it fetches the
encryptedDekfrom theprojectKeystable and decrypts it using the Master KEK. - If no key exists, it generates a new 32-byte random key, encrypts it with the Master KEK, and persists it.
Sources: pkg/convex/lib/encryption.ts:117-157
Data Format
Ciphertext is stored as a concatenated string in the format: [Base64 IV].[Base64 Ciphertext]. This ensures the unique Initialization Vector (IV) required for AES-GCM is always available for decryption.
Sources: pkg/convex/lib/encryption.ts:37-39, pkg/convex/lib/encryption.ts:103-111
Encryption and Decryption Workflows
Secret Encryption Process
Encryption occurs during the application of "draft" changes to project variables. When a user saves a new or updated variable, the backend executes the following:
Secret Decryption Process
Decryption is request-scoped and triggered specifically when a user requests to "reveal" a value in the UI. Plaintext is never returned in bulk listings to minimize exposure.
| Function | Purpose | ephemeral? |
|---|---|---|
decryptSecretValueForProject | Decrypts a specific ciphertext using the project's DEK. | Yes |
decryptValueForCurrentOrgProjectStage | API endpoint that calls decryption for a specific variable ID. | Yes |
Sources: pkg/convex/project_variables.ts:255-296, pkg/convex/lib/encryption.ts:187-199
Data Models
projectKeys Table
This table stores the wrapped Project DEKs.
| Field | Type | Description |
|---|---|---|
projectId | Id<"projects"> | Reference to the owning project. |
orgId | string | Reference to the organization. |
encryptedDek | string | The DEK encrypted by the Master KEK ([IV].[Ciphertext]). |
dekVersion | number | Versioning for future rotation support (Current: 1). |
rotatedAtMs | number | Timestamp of last key rotation. |
Sources: pkg/convex/lib/encryption.ts:145-155
projectVariables Table
This table stores the actual encrypted secrets.
| Field | Type | Description |
|---|---|---|
name | string | The environment variable name (e.g., DATABASE_URL). |
kind | "secret" | "ab_roll" | The type of secret (determines behavior). |
encryptedValue | string | The value encrypted by the Project DEK. |
Sources: AGENTS.md:86-107, pkg/convex/project_variables.ts:188-195
Security Guidelines for Developers
- No Caching: The unwrapped DEK must never be cached or persisted. It must be unwrapped on-demand per request to remain stateless and ephemeral.
- Request Scoping: Decryption should only be performed for immediate UI/API response needs. Callers must not persist the returned plaintext.
- Environment Safety: The
BAREKEY_MASTER_KEY_B64must be exactly 32 bytes when decoded and must be kept secure as it is the root of trust for all project keys.
Sources: pkg/convex/lib/encryption.ts:77-80, pkg/convex/lib/encryption.ts:119-122, pkg/convex/lib/encryption.ts:187-190