How to Generate an ENCRYPTION_KEY for FreeLLMAPI

FreeLLMAPI requires a 32-byte (64-hex-character) encryption key that you can generate with a single Node.js command: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))".

FreeLLMAPI uses AES-256-GCM encryption to protect all stored provider API keys at rest. The encryption key must be provided via the ENCRYPTION_KEY environment variable and follows strict formatting requirements. This guide walks through generating a valid key and configuring it properly for both development and production environments.

Understanding the ENCRYPTION_KEY Requirements

FreeLLMAPI's encryption system, implemented in [server/src/lib/crypto.ts](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/crypto.ts), enforces these key constraints:

  • 32 bytes of random data (256 bits for AES-256)
  • 64 hexadecimal characters when encoded as a string
  • No placeholder values in production environments

The initEncryptionKey() function (lines 94-100) handles key initialization on server startup. In production, missing or invalid keys cause the server to abort immediately via missingKeyError() (lines 49-55), preventing any plaintext storage of provider credentials.

Generating a Valid ENCRYPTION_KEY

The FreeLLMAPI source code explicitly recommends this approach at [lines 30-34 of crypto.ts](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/crypto.ts#L29-L36):

node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

This command:

  • Generates 32 cryptographically secure random bytes using crypto.randomBytes(32)
  • Converts to lowercase hexadecimal with toString('hex')
  • Outputs a 64-character string ready for use

Example output:


9f1e2a5c7b3d4e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f

Method 2: OpenSSL Alternative

If Node.js isn't available, use OpenSSL:

openssl rand -hex 32

Both methods produce equivalent 64-character hex strings that satisfy the parseHexKey() validation in the source code.

Configuring ENCRYPTION_KEY in Your Environment

Step 1: Create or Edit .env File

FreeLLMAPI reads environment variables from a .env file in the repository root. Add your generated key:

echo "ENCRYPTION_KEY=9f1e2a5c7b3d4e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f" >> .env

Replace the hex string with your generated value. The .env.example file in the repository documents this format with a placeholder value.

Step 2: Restart the Server

The server must reload to pick up the new environment variable:


# Native development

npm run dev

# Docker deployment

docker compose down && docker compose up -d

Step 3: Verify Key Initialization

Confirm successful key loading via the /status endpoint:

curl http://localhost:3001/status | jq '.encryptionKeyFingerprint'

Expected output:

"sha256:a1b2c3d4e5f6..."

This fingerprint is computed by encryptionKeyFingerprint() (lines 73-76 in [server/src/lib/crypto.ts](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/crypto.ts)), hashing the key without exposing it. The [server/src/routes/status.ts](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/routes/status.ts) file exposes this via isEncryptionKeyInitialized().

Development vs. Production Behavior

Environment Key Requirement Fallback Behavior
Development Optional Auto-generates in-memory key or uses file-based key near SQLite DB
Production Mandatory Server aborts with missingKeyError() if ENCRYPTION_KEY is unset or placeholder

The development fallback exists for quick local testing but must not be used in production—provider keys would be unrecoverable if the in-memory key is lost.

Complete Configuration Example


# 1. Generate key

$ node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
a3f7e2d9c1b8a4f5e6d7c8b9a0f1e2d3c4b5a6f7e8d9c0b1a2f3e4d5c6b7a8f9

# 2. Persist to environment

$ echo "ENCRYPTION_KEY=a3f7e2d9c1b8a4f5e6d7c8b9a0f1e2d3c4b5a6f7e8d9c0b1a2f3e4d5c6b7a8f9" > .env

# 3. Start server and verify

$ npm run dev &
$ curl -s http://localhost:3001/status | grep encryptionKeyInitialized
"encryptionKeyInitialized": true

Key Implementation Files in FreeLLMAPI

Summary

  • FreeLLMAPI ENCRYPTION_KEY must be 32 bytes (64 hex characters) for AES-256-GCM encryption
  • Generate with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
  • Configure in .env file as ENCRYPTION_KEY=<64-hex-chars>
  • Verify via /status endpoint checking encryptionKeyFingerprint
  • Production requires explicit keys—development fallbacks are insecure for live deployments
  • Reference [server/src/lib/crypto.ts](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/crypto.ts) for all implementation details

Frequently Asked Questions

What happens if I don't set ENCRYPTION_KEY in production?

The server calls missingKeyError() (lines 49-55 in [crypto.ts](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/crypto.ts)) and terminates immediately. This hard failure prevents the application from starting without encryption, ensuring no provider credentials are stored unencrypted.

Can I reuse the same ENCRYPTION_KEY across multiple deployments?

Yes, but this reduces security isolation. FreeLLMAPI encrypts provider keys with this key, so compromise of one deployment exposes others. Best practice: generate unique keys per environment and store them in secure secret management systems.

How do I rotate an existing ENCRYPTION_KEY?

Key rotation requires re-encrypting all stored provider keys. FreeLLMAPI does not currently expose automated key rotation—implement this by decrypting with the old key and re-encrypting with a new key via the encryption functions in [server/src/lib/crypto.ts](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/crypto.ts).

Why does the status endpoint show a fingerprint instead of the full key?

The encryptionKeyFingerprint() function (lines 73-76) computes a SHA-256 hash truncated to 16 hex characters. This allows verification that a key is loaded without exposing the sensitive key material through API responses or logs.

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 →