How Does FreeLLMAPI Store Keys Securely? AES-256-GCM Encryption Explained
FreeLLMAPI encrypts API keys using AES-256-GCM before storing them in SQLite, ensuring plaintext credentials never touch the disk and are only decrypted transiently in memory during request processing.
FreeLLMAPI implements a defense-in-depth strategy to protect sensitive LLM credentials. According to the tashfeenahmed/freellmapi source code, the application never persists raw API keys in configuration files or environment dumps; instead, it relies on strong at-rest encryption and strict memory management.
At-Rest Encryption Architecture
The foundation of FreeLLMAPI’s security model is AES-256-GCM encryption applied to all sensitive material before database storage.
SQLite Storage Schema
In the api_keys table, credentials are split across three separate columns to prevent reconstruction without the master key:
key_encrypted— The AES-256-GCM ciphertextkey_iv— The random initialization vector (nonce)key_auth_tag— The authentication tag for integrity verification
This separation ensures that even direct database access does not reveal usable credentials. The encryption helpers in server/src/lib/crypto.js handle the cryptographic operations, generating a random IV for every encryption operation and appending the auth tag to prevent tampering.
// Example: Storing a new key via the server API
import { encrypt } from './lib/crypto.js';
import { db } from './db';
function storeKey(prefix: string, plainKey: string) {
const { encrypted, iv, authTag } = encrypt(plainKey);
db.prepare(`
INSERT INTO api_keys (platform, key_encrypted, key_iv, key_auth_tag)
VALUES (?, ?, ?, ?)
`).run(`${prefix}_`, encrypted, iv, authTag);
}
Core Cryptographic Implementation
The encrypt() and decrypt() functions in server/src/lib/crypto.js implement AES-256-GCM with authenticated encryption. Each operation requires the application-wide ENCRYPTION_KEY supplied via environment variable. This design ensures that the database remains encrypted at rest, and decryption only occurs within the running application context.
Proxy URL and Credential Protection
FreeLLMAPI extends the same encryption guarantees to proxy configurations, which often contain embedded credentials.
Encrypting Per-Key Proxy URLs
When users supply a per-key proxy URL, the system treats it with the same sensitivity as the API key itself. The server/src/lib/key-proxy.ts file exports encryptProxyUrl() and decryptProxyUrl() functions that reuse the AES-256-GCM pipeline. Database migrations in server/src/db/migrate/defaults.ts add dedicated columns for encrypted proxy storage, mirroring the key_encrypted, key_iv, and key_auth_tag pattern used for primary credentials.
Masking Keys for UI Display
To prevent accidental secret leakage in dashboards or logs, server/src/lib/key-proxy.ts exposes maskKey() (and maskProxyUrl()), which replaces the secret portion of the credential with asterisks. This guarantees that even administrative interfaces never render plaintext secrets.
import { maskKey } from './lib/key-proxy.ts';
// Returns "sk-***" instead of the full key
const displayed = maskKey('sk-abcdef1234567890');
console.log(displayed);
Runtime Decryption and Memory Safety
In-memory exposure is minimized to the exact duration of request processing.
Transient Decryption During Requests
When a request arrives, the server queries the encrypted columns from SQLite, invokes decrypt() from crypto.js using the runtime ENCRYPTION_KEY, and injects the plaintext into the request pipeline. Once the request completes, the variable falls out of scope and becomes eligible for garbage collection. This approach ensures that raw keys never exist in memory longer than necessary.
import { decrypt } from './lib/crypto.js';
import { db } from './db';
function getKeyForPlatform(prefix: string) {
const row = db.prepare(`
SELECT key_encrypted, key_iv, key_auth_tag
FROM api_keys
WHERE platform = ?
`).get(`${prefix}_`);
if (!row?.key_encrypted) return null;
// Decrypted only at request time, never persisted
return decrypt(row.key_encrypted, row.key_iv, row.key_auth_tag).trim();
}
CLI Environment Isolation
For command-line usage, FreeLLMAPI supports a unified environment variable FREELLMAPI_API_KEY. The CLI entry point in cli/src/index.ts reads this variable directly from process.env and passes it to the request handler without ever writing it to disk. This mode is designed for ephemeral operations where database storage is not required.
# Never written to any file; exists only in shell memory
export FREELLMAPI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxx
npx freellmapi chat "Explain quantum tunneling"
Key Parsing and Validation
The server/src/lib/key-parser.ts module handles validation of <PREFIX>_API_KEY or <PREFIX>_KEY formats during ingestion. This ensures that only properly prefixed credentials enter the encryption pipeline, preventing malformed or accidental data from entering the secure storage layer.
Summary
- AES-256-GCM encryption in
server/src/lib/crypto.jsprotects all credentials before they reach the SQLiteapi_keystable. - Three-column separation (
key_encrypted,key_iv,key_auth_tag) ensures database compromises do not expose plaintext keys. - Proxy URLs receive identical encryption treatment via
server/src/lib/key-proxy.ts. - UI masking via
maskKey()prevents secret leakage in administrative interfaces. - Runtime decryption limits plaintext exposure to the duration of individual requests.
- CLI mode supports ephemeral usage via
FREELLMAPI_API_KEYwithout persisting values.
Frequently Asked Questions
What encryption algorithm does FreeLLMAPI use for API key storage?
FreeLLMAPI uses AES-256-GCM (Galois/Counter Mode) authenticated encryption. This algorithm provides both confidentiality and integrity verification through the authentication tag stored in the key_auth_tag column. The implementation resides in server/src/lib/crypto.js and generates a random IV for every encryption operation to prevent pattern analysis.
Where are API keys stored in FreeLLMAPI?
API keys are stored in an SQLite database within the api_keys table, specifically across three columns: key_encrypted, key_iv, and key_auth_tag. The plaintext is never written to configuration files, environment dumps, or logs. Documentation in docs/clients.md and docs/api.md explicitly warns users against storing raw keys in JSON or ENV files.
How does FreeLLMAPI prevent API keys from appearing in logs or the UI?
The system uses masking functions defined in server/src/lib/key-proxy.ts. The maskKey() function (and maskProxyUrl() for proxy credentials) truncates sensitive portions of the string, displaying only the prefix followed by asterisks. This ensures that dashboard views, error messages, and log entries never contain recoverable secret material.
Can FreeLLMAPI work without storing keys in the database?
Yes. The CLI mode supports ephemeral key usage via the FREELLMAPI_API_KEY environment variable. As implemented in cli/src/index.ts, the CLI reads this variable directly from the process environment and transmits it to the API without persisting it to disk or database. This mode is ideal for CI/CD pipelines or temporary testing scenarios.
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 →