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

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, 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, the Fernet instance is initialized using a secret key derived from the environment:

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:

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:


# 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:


# 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:

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:

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 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.

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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →