# How Open-Notebook Encrypts AI Credentials: Fernet Symmetric Encryption Mechanism Explained

> Open-Notebook secures AI credentials with Fernet symmetric encryption. Learn how secrets decrypt on-the-fly using the ENCRYPTION_KEY environment variable.

- Repository: [Luis Novo/open-notebook](https://github.com/lfnovo/open-notebook)
- Tags: deep-dive
- Published: 2026-06-21

---

**Open-Notebook protects stored AI credentials using Fernet symmetric encryption from Python's cryptography library, decrypting secrets on-the-fly via the `ENCRYPTION_KEY` environment variable.**

Open-Notebook is an open-source knowledge management system that stores sensitive AI provider credentials directly in SurrealDB. To prevent unauthorized access to API keys and secrets, the application implements a robust encryption mechanism that ensures plaintext credentials never touch the database. According to the source code in [`open_notebook/domain/credential.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/credential.py), the system uses **Fernet symmetric encryption** to seal secrets before persistence, decrypting them only when needed for LLM API calls.

## How Fernet Encryption Works in Open-Notebook

The encryption mechanism relies on a single symmetric key managed outside the codebase. When a credential is created, the raw secret is encrypted using Fernet's authenticated encryption, producing a base64-encoded token that is safe to store in SurrealDB records.

### The Encryption Key Source

In [`open_notebook/domain/credential.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/credential.py), the Fernet instance is initialized using a secret key derived from the environment:

```python
from cryptography.fernet import Fernet
import os

fernet = Fernet(os.getenv("ENCRYPTION_KEY"))

```

The `ENCRYPTION_KEY` environment variable must contain a valid Fernet key (32 bytes encoded in URL-safe base64). This key is loaded once at module initialization and reused for all encryption and decryption operations, ensuring consistent protection across the application lifecycle.

### The Credential Model Structure

The `Credential` class stores the encrypted payload in the `encrypted_secret` field rather than the raw API key:

```python
class Credential(ObjectModel):
    provider: str
    encrypted_secret: str   # <-- Fernet token stored here

```

This design guarantees that database dumps, backups, or logs containing the `encrypted_secret` field remain useless to attackers without access to the `ENCRYPTION_KEY` environment variable.

## Encrypting and Decrypting Credentials

The `Credential` class provides methods to handle the cryptographic boundary between plaintext user input and database storage.

### Creating Encrypted Credentials

When users add a new AI provider credential, the `create()` method encrypts the secret before instantiation:

```python

# From open_notebook/domain/credential.py

@classmethod
def create(cls, provider: str, secret: str) -> "Credential":
    encrypted = fernet.encrypt(secret.encode()).decode()
    return cls(provider=provider, encrypted_secret=encrypted)

```

The `fernet.encrypt()` call generates a Fernet token that includes the ciphertext, a timestamp, and an HMAC signature. This token is decoded to a UTF-8 string for JSON serialization before storage.

### Retrieving Secrets for API Calls

When the application needs to initialize an AI provider client, the `get_secret()` method decrypts the token:

```python

# From open_notebook/domain/credential.py

def get_secret(self) -> str:
    return fernet.decrypt(self.encrypted_secret.encode()).decode()

```

This method decodes the base64 token, verifies the HMAC signature to detect tampering, and returns the original secret string to the provider service layer.

## Security Benefits of the Fernet Implementation

The Fernet-based encryption mechanism provides three critical security properties for stored AI credentials:

- **Confidentiality**: The symmetric AES-128 encryption in CBC mode ensures that the SurrealDB database contents reveal no information about the actual API keys without the `ENCRYPTION_KEY`.
- **Integrity**: Each Fernet token includes an HMAC-SHA256 signature. Any modification to the `encrypted_secret` field in the database triggers a `cryptography.fernet.InvalidToken` exception during decryption, preventing tampered credentials from being used.
- **Key Rotation Support**: Because the encryption key is externalized to an environment variable, operators can rotate keys by updating the deployment configuration and re-encrypting credentials, without modifying application code.

## Configuration Requirements

To enable the encryption mechanism, operators must set the `ENCRYPTION_KEY` environment variable before starting the application. Generate a valid key using Python's cryptography library:

```python
from cryptography.fernet import Fernet
key = Fernet.generate_key()
print(key.decode())  # Store this value in ENCRYPTION_KEY

```

Add the generated key to your environment or `.env` file:

```bash
ENCRYPTION_KEY=your-generated-key-here

```

Without this variable, the Fernet initialization will fail, preventing the application from starting or processing credentials.

## Summary

- Open-Notebook stores AI credentials in SurrealDB using **Fernet symmetric encryption** from the `cryptography` library.
- The `Credential` class in [`open_notebook/domain/credential.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/credential.py) handles encryption via `create()` and decryption via `get_secret()`.
- Secrets are encrypted before storage in the `encrypted_secret` field and decrypted only when needed for API calls.
- The encryption key is sourced from the `ENCRYPTION_KEY` environment variable, supporting secure key management and rotation.
- Fernet provides both confidentiality (AES-128-CBC) and integrity (HMAC-SHA256) protections for stored credentials.

## Frequently Asked Questions

### What encryption algorithm does Open-Notebook use for AI credentials?

Open-Notebook uses **Fernet symmetric encryption**, which combines AES-128 in CBC mode for confidentiality with HMAC-SHA256 for message authentication. This is implemented via Python's `cryptography` library in [`open_notebook/domain/credential.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/credential.py).

### How is the encryption key managed in Open-Notebook?

The encryption key is managed externally through the `ENCRYPTION_KEY` environment variable. The application initializes a `Fernet` instance from this environment variable at startup, ensuring the key never resides in source code or version control.

### What happens if the ENCRYPTION_KEY is lost?

If the `ENCRYPTION_KEY` is lost or rotated without re-encrypting existing credentials, the application cannot decrypt stored AI credentials. The `get_secret()` method will raise a `cryptography.fernet.InvalidToken` exception, and users will need to re-enter their API keys.

### Does Open-Notebook support key rotation for stored credentials?

While the Fernet implementation supports key rotation at the infrastructure level (by updating the `ENCRYPTION_KEY` environment variable), the application does not automatically re-encrypt existing database records. Operators must manually decrypt and re-encrypt credentials when rotating keys, or users must re-enter their secrets.