How to Generate an Encryption Key for FreeLLMAPI: Production and Development Setup
FreeLLMAPI requires a 32-byte hexadecimal encryption key set via the ENCRYPTION_KEY environment variable or stored in a .encryption-key file to encrypt provider API keys at rest using AES-256-GCM.
FreeLLMAPI, the open-source LLM aggregation proxy developed by tashfeenahmed, safeguards stored provider credentials using AES-256-GCM encryption. To generate an encryption key for FreeLLMAPI, you must create a cryptographically secure 64-character hex string (representing 32 bytes) and configure it before starting the server. The application supports both explicit environment variable configuration for production and automatic file-based generation for local development.
Understanding FreeLLMAPI's Encryption Requirements
FreeLLMAPI encrypts all stored provider API keys at rest using AES-256-GCM. The encryption key must be exactly 32 bytes (represented as a 64-character hexadecimal string). According to the source code in server/src/lib/crypto.ts, the server accepts keys from two sources with the following precedence:
- The
ENCRYPTION_KEYenvironment variable (required in production) - A
.encryption-keyfile located adjacent to the SQLite database (auto-generated in development)
The .encryption-key file is created with ** restrictive 0600 permissions** (read/write for owner only), ensuring no other system users can access the sensitive material.
Methods to Generate an Encryption Key
Production Setup Using the ENCRYPTION_KEY Variable
For production deployments, you must explicitly set the ENCRYPTION_KEY environment variable. As documented in docs/env/01-variables.md, the server reads this variable at startup and uses it directly for cryptographic operations.
Generate a secure key using Node.js crypto:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
Add the output to your .env file:
ENCRYPTION_KEY=your_generated_64_character_hex_string_here
This approach is referenced in the .env.example file, which includes a placeholder and the generation command for quick setup.
Development Mode Auto-Generation
When ENCRYPTION_KEY is absent, FreeLLMAPI automatically initializes a key for local development. The function initializeEncryptionKey() in server/src/lib/crypto.ts performs the following steps:
- Checks for an existing
.encryption-keyfile - If missing, generates a fresh 32-byte key using
crypto.randomBytes(32).toString('hex') - Writes the key to
.encryption-keywith0600permissions - Stores a SHA-256 fingerprint in the SQLite
settingstable for integrity verification
This behavior is handled automatically during the dev-bootstrap.sh script execution, which sets up the development environment without manual intervention.
Manual File Creation
If you prefer to create the .encryption-key file manually without relying on the bootstrap process, use this command to ensure proper permissions:
node -e "require('fs').writeFileSync('.encryption-key', require('crypto').randomBytes(32).toString('hex'), {mode: 0o600})"
Technical Implementation of Key Management
The core encryption logic resides in server/src/lib/crypto.ts. When the server starts, it calls initializeEncryptionKey(), which determines the key source based on environment configuration. If using the file-based approach, the function ensures the key persists across restarts while maintaining strict file system permissions.
For integrity verification, the implementation includes encryptionKeyFingerprint(), which calculates a SHA-256 hash of the active key and stores it in the database. This fingerprint allows FreeLLMAPI to detect mismatched keys during backup restoration operations or when migrating data between instances, as detailed in docs/env/02-security-and-keys.md.
Configuration Examples
One-liner to generate and append to .env:
ENCRYPTION_KEY=$(node -e "process.stdout.write(require('crypto').randomBytes(32).toString('hex'))") && printf 'ENCRYPTION_KEY=%s\n' "$ENCRYPTION_KEY" >> .env
Programmatic generation in TypeScript:
import { randomBytes } from 'node:crypto';
import { writeFileSync } from 'node:fs';
const keyHex = randomBytes(32).toString('hex');
writeFileSync('.encryption-key', keyHex, { mode: 0o600 });
console.log('Encryption key written to .encryption-key');
Verify the current key fingerprint:
import { encryptionKeyFingerprint } from './server/src/lib/crypto.js';
console.log('Key fingerprint:', encryptionKeyFingerprint());
Summary
- FreeLLMAPI uses AES-256-GCM encryption requiring a 32-byte (64 hex character) key
- Production deployments must set the
ENCRYPTION_KEYenvironment variable explicitly - Development mode automatically generates
.encryption-keywith0600permissions viainitializeEncryptionKey()inserver/src/lib/crypto.ts - Key integrity is maintained through SHA-256 fingerprints stored in the SQLite database
- Reference
docs/env/01-variables.mdfor environment configuration anddocs/env/02-security-and-keys.mdfor security best practices
Frequently Asked Questions
What happens if I don't set the ENCRYPTION_KEY environment variable?
If ENCRYPTION_KEY is not set, FreeLLMAPI operates in non-production mode and automatically generates a key by calling initializeEncryptionKey(). This creates a .encryption-key file next to the SQLite database with restrictive 0600 permissions. While convenient for development, this auto-generation is not recommended for production deployments where explicit key management is required.
How secure is the .encryption-key file?
The .encryption-key file is created with Unix permissions 0600, meaning only the file owner can read or write it. According to the implementation in server/src/lib/crypto.ts, the server explicitly sets these permissions during the initializeEncryptionKey() routine to prevent unauthorized access by other system users. However, you must ensure the underlying file system and server user permissions are properly configured.
Can I regenerate or rotate the encryption key?
The source code analysis reveals that FreeLLMAPI tracks key identity using encryptionKeyFingerprint(), which stores a SHA-256 hash in the SQLite settings table to detect mismatched keys during restore operations. While you can manually delete the .encryption-key file or change the ENCRYPTION_KEY variable to trigger generation of a new key, be aware that changing the encryption key will render existing encrypted provider API keys undecryptable. You would need to re-enter all provider credentials after rotation.
Where does FreeLLMAPI store the encryption key fingerprint?
The key fingerprint is stored in the SQLite database's settings table. The function encryptionKeyFingerprint() in server/src/lib/crypto.ts calculates this SHA-256 hash and persists it during initialization. This allows the system to verify key consistency across server restarts and detect potential configuration errors or tampering attempts.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →