# OmniRoute's Encryption Practices for API Keys at Rest: AES-256-GCM Field-Level Security

> Learn how OmniRoute secures API keys at rest using AES-256-GCM and scrypt for robust field level encryption. Protect your credentials with advanced security measures deployed in the diegosouzapw OmniRoute repository.

- Repository: [Diego Rodrigues de Sa e Souza/OmniRoute](https://github.com/diegosouzapw/OmniRoute)
- Tags: internals
- Published: 2026-07-21

---

**OmniRoute protects provider credentials using AES-256-GCM field-level encryption with scrypt-derived keys, storing secrets in SQLite with automatic legacy migration and graceful fallback mechanisms.**

The open-source OmniRoute repository (diegosouzapw/OmniRoute) implements comprehensive at-rest encryption for sensitive API keys and tokens. All cryptographic operations reside in [`src/lib/db/encryption.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/encryption.ts), which enforces authenticated encryption while maintaining backward compatibility with older data formats.

## Core Encryption Architecture

OmniRoute's security model centers on **field-level encryption** applied individually to credential fields rather than full-database encryption.

### AES-256-GCM Implementation

The system uses the **AES-256-GCM** authenticated encryption algorithm as defined by the constants in [`src/lib/db/encryption.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/encryption.ts):

```typescript
const ALGORITHM = "aes-256-gcm";
const AUTH_TAG_LENGTH = 16;

```

These settings enforce Galois/Counter Mode with a full 128-bit authentication tag. According to the source code, this configuration provides both confidentiality and integrity protection, preventing tampering attacks such as tag-truncation forgeries.

### Key Derivation with scrypt

The encryption key derives from the `STORAGE_ENCRYPTION_KEY` environment variable using **scryptSync**:

```typescript
const STATIC_SALT = "omniroute-field-encryption-v1";
const derivedKey = scryptSync(secret, STATIC_SALT, KEY_LENGTH);

```

The static, versioned salt ensures consistent key derivation across all operations. This approach eliminates key-mismatch bugs that occurred with previous dynamic-salt implementations.

## Field-Level Encryption Workflow

Individual credential fields undergo encryption before persistence in the SQLite database.

### Storage Format and Versioning

Encrypted values follow a structured string format:

```

enc:v1:<iv_hex>:<ciphertext_hex>:<authTag_hex>

```

This prefix system enables the codebase to detect encrypted values at runtime and supports future format upgrades without breaking existing data.

### Double-Encryption Prevention

The `encrypt()` function includes a short-circuit check that prevents redundant encryption operations. According to lines 20-22 in [`src/lib/db/encryption.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/encryption.ts), if the plaintext already starts with `"enc:v1:"`, the function returns the input unchanged. This safeguard prevents malformed ciphertexts and unnecessary CPU consumption.

## Backward Compatibility and Migration

OmniRoute maintains support for legacy encryption schemes while automating migration to current standards.

### Legacy Key Support

The module retains a `getLegacyDynamicKey()` function for reading data encrypted with older dynamic-salt methods. During decryption, the system attempts the static key first, then falls back to the legacy derivation. As implemented in lines 90-98, the decryption logic tries the static key initially, and only attempts the legacy key if the first attempt fails.

### Automatic Migration Utilities

The `migrateLegacyEncryptedString()` function detects outdated encryption formats and re-encrypts them using the current static-key method. This utility ensures all stored secrets eventually conform to the latest security scheme without manual intervention.

## Configuration and Environment Setup

Proper configuration requires setting a high-entropy encryption key before runtime.

### STORAGE_ENCRYPTION_KEY Requirements

OmniRoute expects a base64-encoded 32-byte value in the `STORAGE_ENCRYPTION_KEY` environment variable. Generate a compliant key using:

```bash
export STORAGE_ENCRYPTION_KEY=$(openssl rand -base64 32)

```

The application reads `process.env.STORAGE_ENCRYPTION_KEY` during initialization to derive the master encryption key.

### Graceful Degradation Behavior

If `STORAGE_ENCRYPTION_KEY` is missing, the encryption module falls back to **passthrough mode** (plaintext storage) and logs a warning. Lines 62-64 in [`src/lib/db/encryption.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/encryption.ts) implement this fallback to prevent application crashes while clearly signaling misconfiguration through log output.

## Practical Implementation Examples

Implement field-level encryption using the helper functions exported from the encryption module.

### Encrypting Connection Credentials

Use `encryptConnectionFields()` to secure an entire connection object before database storage:

```typescript
import { encryptConnectionFields } from "@/lib/db/encryption";

const connection = {
  provider: "openai",
  apiKey: "sk-abcdef123456",
  accessToken: null,
};

encryptConnectionFields(connection);
// connection.apiKey now contains "enc:v1:..." ciphertext

```

### Decrypting Retrieved Records

After fetching from the database, decrypt fields using `decryptConnectionFields()`:

```typescript
import { decryptConnectionFields } from "@/lib/db/encryption";

const row = await db.getConnection(providerId);
const plaintextConnection = decryptConnectionFields(row);
// plaintextConnection.apiKey restored to original value

```

### Migrating Legacy Data

Execute a one-time migration script to upgrade old encryption formats:

```typescript
import { migrateLegacyEncryptedString } from "@/lib/db/encryption";

function migrateRow(row) {
  const fields = ["apiKey", "accessToken", "refreshToken"];
  let updated = false;
  
  for (const field of fields) {
    const value = row[field];
    if (value?.startsWith("enc:v1:")) {
      const result = migrateLegacyEncryptedString(value);
      if (result.updated) {
        row[field] = result.value;
        updated = true;
      }
    }
  }
  return updated;
}

```

## Summary

- OmniRoute implements **AES-256-GCM authenticated encryption** for API keys stored in SQLite, with all logic contained in [`src/lib/db/encryption.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/encryption.ts).
- **scrypt key derivation** uses a static, versioned salt (`"omniroute-field-encryption-v1"`) to ensure consistent encryption keys across sessions.
- The **`enc:v1:`** prefix format enables version detection and prevents double-encryption while supporting future upgrades.
- **Graceful degradation** to passthrough mode occurs when `STORAGE_ENCRYPTION_KEY` is unset, preventing crashes but logging warnings.
- **Backward compatibility** functions support legacy dynamic-salt encryption while `migrateLegacyEncryptedString()` automates upgrades to the current static-key format.
- Field-level helpers **`encryptConnectionFields()`** and **`decryptConnectionFields()`** centralize credential protection across the application's database layer.

## Frequently Asked Questions

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

OmniRoute uses **AES-256-GCM** (Galois/Counter Mode) with a 16-byte authentication tag. This authenticated encryption mode provides both confidentiality and integrity verification, preventing attackers from tampering with encrypted credentials without detection.

### How does OmniRoute derive the encryption key from the environment variable?

The system passes the `STORAGE_ENCRYPTION_KEY` environment variable through **scryptSync** with a static salt value of `"omniroute-field-encryption-v1"`. This produces a deterministic 32-byte derived key that remains consistent across application restarts, avoiding key-mismatch issues present in earlier dynamic-salt implementations.

### What happens if I don't set the STORAGE_ENCRYPTION_KEY environment variable?

If the encryption key is missing, OmniRoute enters **passthrough mode** and stores API keys in plaintext while emitting a warning log. The application continues running to maintain availability, but this configuration exposes sensitive credentials and should only occur during development or misconfiguration scenarios.

### Can OmniRoute decrypt API keys encrypted with older versions of the software?

Yes. The decryption logic first attempts verification with the current static-derived key, then automatically falls back to the legacy dynamic-key derivation method. The `migrateLegacyEncryptedString()` utility can re-encrypt legacy data with the current scheme, ensuring seamless upgrades without losing access to existing stored credentials.