# How Does FreeLLMAPI Store Keys Securely? AES-256-GCM Encryption Explained

> Discover how FreeLLMAPI securely stores API keys using AES-256-GCM encryption in SQLite. Learn how your credentials remain safe and are only decrypted in memory.

- Repository: [Tashfeen/freellmapi](https://github.com/tashfeenahmed/freellmapi)
- Tags: deep-dive
- Published: 2026-08-29

---

**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 ciphertext
- `key_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`](https://github.com/tashfeenahmed/freellmapi/blob/main/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.

```typescript
// 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`](https://github.com/tashfeenahmed/freellmapi/blob/main/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`](https://github.com/tashfeenahmed/freellmapi/blob/main/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`](https://github.com/tashfeenahmed/freellmapi/blob/main/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`](https://github.com/tashfeenahmed/freellmapi/blob/main/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.

```typescript
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`](https://github.com/tashfeenahmed/freellmapi/blob/main/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**.

```typescript
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`](https://github.com/tashfeenahmed/freellmapi/blob/main/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.

```bash

# 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`](https://github.com/tashfeenahmed/freellmapi/blob/main/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.js`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/crypto.js) protects all credentials before they reach the SQLite `api_keys` table.
- **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`](https://github.com/tashfeenahmed/freellmapi/blob/main/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_KEY` without 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`](https://github.com/tashfeenahmed/freellmapi/blob/main/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`](https://github.com/tashfeenahmed/freellmapi/blob/main/docs/clients.md) and [`docs/api.md`](https://github.com/tashfeenahmed/freellmapi/blob/main/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`](https://github.com/tashfeenahmed/freellmapi/blob/main/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`](https://github.com/tashfeenahmed/freellmapi/blob/main/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.