# How API Keys Are Encrypted and Stored in FreeLLMAPI: AES-256-GCM Implementation

> Discover how FreeLLMAPI encrypts and stores API keys using AES-256-GCM. Learn about secure storage practices with ciphertext, IV, and auth tag in SQLite for protected API access.

- Repository: [Tashfeen/freellmapi](https://github.com/tashfeenahmed/freellmapi)
- Tags: internals
- Published: 2026-09-02

---

**FreeLLMAPI protects every provider API key using AES-256-GCM encryption, storing only the encrypted ciphertext, random IV, and authentication tag in SQLite while decrypting credentials only in memory during active requests.**

FreeLLMAPI implements a zero-trust "encrypt-at-rest" architecture that safeguards third-party LLM credentials from database breaches. Understanding exactly how API keys are encrypted and stored in FreeLLMAPI reveals a security model where the encryption key never leaves the server process, ensuring raw provider tokens remain inaccessible even if the underlying storage is compromised. The system uses Node.js native crypto modules and a unified bearer token abstraction to isolate client applications from provider-specific secrets.

## The AES-256-GCM Encryption Pipeline

FreeLLMAPI employs **AES-256-GCM** (Galois/Counter Mode) for authenticated encryption. This provides both confidentiality and integrity verification, ensuring stored keys cannot be tampered with or read without the master secret.

### Key Derivation and Environment Configuration

On server startup, FreeLLMAPI initializes its encryption context by either generating a fresh 256-bit secret or reading the `ENCRYPTION_KEY` variable from the environment file. This secret remains resident only in process memory and is never persisted to disk. As implemented in the server bootstrap logic, the application fails safe if neither a generated nor configured key is available.

### Per-Key Encryption Process

When a new provider key arrives via the UI or the `/keys` REST endpoint defined in [`server/src/routes/keys.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/routes/keys.ts), the server executes the following cryptographic sequence:

1. **Generates a cryptographically random IV** using `crypto.randomBytes(12)` to ensure unique ciphertexts for identical keys.
2. **Creates the cipher** via `crypto.createCipheriv('aes-256-gcm', secret, iv)`, binding the 256-bit master secret to the per-key IV.
3. **Encrypts the raw provider key**, extracting both the `encrypted_key` ciphertext and the 16-byte `auth_tag` (authentication tag) required for GCM verification.
4. **Persists the three components**—`encrypted_key`, `iv`, and `auth_tag`—alongside the platform label into the SQLite database via [`server/src/services/declarative-config.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/declarative-config.ts).

The raw provider key exists only as a transient variable during this encryption routine and is immediately cleared from memory after the database transaction commits.

## Database Storage Schema

FreeLLMAPI stores encrypted credentials in a dedicated SQLite table with a schema designed to prevent data leakage. The migration file [`server/src/db/migrations/20260805_000002_client_profiles.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/db/migrations/20260805_000002_client_profiles.ts) defines the `api_keys` table structure, explicitly separating the encrypted payload from its non-sensitive metadata.

### The api_keys Table Structure

Each row contains:
- **`encrypted_key`**: The AES-256-GCM ciphertext (binary/blob format).
- **`iv`**: The 12-byte initialization vector unique to this encryption operation.
- **`auth_tag`**: The authentication tag proving ciphertext integrity.
- **`platform`**: Human-readable label (e.g., "openai", "anthropic") stored in plaintext.
- **`label`**: User-defined description for key management.

Because the database contains only these three cryptographic pieces—and never the master `ENCRYPTION_KEY`—a compromised SQLite file yields no usable credentials to an attacker.

## Runtime Decryption and Memory Safety

During request handling, the router logic in [`server/src/services/router.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/router.ts) performs just-in-time decryption to inject provider keys into upstream LLM API calls. This design minimizes the window of exposure for plaintext secrets.

### Per-Request Decryption Flow

For each incoming inference request:

- The router retrieves the specific row (`encrypted_key`, `iv`, `auth_tag`) from the `api_keys` table based on the requested model provider.
- It invokes the internal `decrypt` helper, which reconstructs the decipher using `crypto.createDecipheriv('aes-256-gcm', secret, iv)` with the same master secret and stored IV.
- The function calls `decipher.final()` and verifies the `auth_tag` against the stored value; mismatching tags immediately throw integrity errors.

The plaintext key resides **only in memory** for the duration of the request lifecycle and is explicitly dereferenced before the HTTP response returns. The system never writes decrypted keys to logs, disk, or error traces.

### Memory Safety Guarantees

FreeLLMAPI leverages JavaScript's scoped variable handling and explicit buffer clearing where possible to prevent key material from persisting in memory pools (heaps) beyond the active request. Because decryption happens inside [`server/src/services/router.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/router.ts) and not in the public-facing route handlers, client applications interact solely with the **unified FreeLLMAPI bearer token**, never touching the underlying provider secrets.

## Managing Encrypted Keys via REST API

The public API in [`server/src/routes/keys.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/routes/keys.ts) provides endpoints for adding and listing keys while maintaining the encryption abstraction.

### Adding a Provider Key

To store a new encrypted credential:

```bash
curl -X POST http://localhost:3001/v1/keys \
  -H "Authorization: Bearer $(cat .env | grep ENCRYPTION_KEY | cut -d= -f2)" \
  -H "Content-Type: application/json" \
  -d '{
        "platform": "openai",
        "label": "My OpenAI Key",
        "apiKey": "sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      }'

```

The server encrypts the `apiKey` field and stores the `encrypted_key`, `iv`, and `auth_tag` in SQLite. The raw token never appears in server logs.

### Retrieving Masked Keys

Client applications fetch metadata without accessing encrypted payloads:

```bash
curl http://localhost:3001/v1/keys/1 \
  -H "Authorization: Bearer <unified-token>"

```

Response:

```json
{
  "id": 1,
  "platform": "openai",
  "label": "My OpenAI Key",
  "maskedKey": "sk-••••••••••••••••••••••••••••••"
}

```

### Using the Unified Token

Client SDKs send only the FreeLLMAPI bearer token. The router decrypts the underlying provider key on-the-fly:

```bash
export OPENAI_API_KEY=$(curl -s http://localhost:3001/v1/keys | jq -r .unifiedKey)

openai api chat.completions.create -m gpt-4o -g "Hello!"

```

The provider-specific secret remains encrypted at rest and is only decrypted within the [`server/src/services/router.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/router.ts) process boundary during the active API call.

## Summary

FreeLLMAPI implements a defense-in-depth strategy for credential storage:

- **AES-256-GCM encryption** provides authenticated confidentiality for all provider keys.
- **Three-part storage** (`encrypted_key`, `iv`, `auth_tag`) in the SQLite `api_keys` table ensures database dumps reveal no usable secrets.
- **Just-in-time decryption** inside [`server/src/services/router.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/router.ts) keeps plaintext keys only in volatile memory for the duration of requests.
- **Master key isolation** via the `ENCRYPTION_KEY` environment variable prevents decryption by anyone without server process access.
- **Unified bearer token** abstraction isolates client applications from provider-specific credentials.

## Frequently Asked Questions

### What encryption algorithm does FreeLLMAPI use for API keys?

FreeLLMAPI uses **AES-256-GCM** (Galois/Counter Mode) authenticated encryption. This algorithm combines the AES block cipher with 256-bit keys and GCM mode to provide both confidentiality and integrity verification via authentication tags.

### Where is the master encryption key stored?

The master encryption key is either generated as a 256-bit random secret on server startup or loaded from the `ENCRYPTION_KEY` environment variable defined in the `.env` file. According to the source code in [`server/src/services/declarative-config.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/declarative-config.ts), this key never leaves process memory and is never written to the SQLite database.

### Can someone read my API keys if they steal the database file?

No. The SQLite database contains only the `encrypted_key` ciphertext, `iv`, and `auth_tag` for each credential. Without the master `ENCRYPTION_KEY` that resides only in the server's environment or memory, the stored cryptographic material cannot be decrypted into usable provider tokens.

### How long do decrypted keys remain in memory?

Decrypted keys exist only for the duration of an active request. The [`server/src/services/router.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/router.ts) implementation decrypts the provider key immediately before calling the upstream LLM API and explicitly dereferences the plaintext variable before returning the response, ensuring keys are not retained in memory between requests or written to logs.