# OmniRoute API Key Lifecycle: Generation, Model Permissions, and IP Access Control

> Understand the OmniRoute API key lifecycle. Learn about secure generation, model permissions, and IP access control for robust API security. Optimize your key management.

- Repository: [Diego Rodrigues de Sa e Souza/OmniRoute](https://github.com/diegosouzapw/OmniRoute)
- Tags: how-to-guide
- Published: 2026-08-22

---

**The OmniRoute API key lifecycle is managed entirely through [`src/lib/db/apiKeys.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/apiKeys.ts), covering high-entropy generation in `createApiKey`, SHA-256 hashed storage, model-permission enforcement via `modelAccessMode`, IP allowlist validation in [`src/sse/services/auth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/sse/services/auth.ts), and eventual rotation or revocation through `regenerateApiKey` and `revokeApiKey`.**

**OmniRoute** treats **API keys** as first-class resources with a complete lifecycle tracked in SQLite. Every stage—from initial creation to final revocation—is centralized in the [`apiKeys.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/apiKeys.ts) module and enforced at the request boundary. Understanding the **OmniRoute API key lifecycle** is essential for securing model access and controlling network ingress.

## Creating an OmniRoute API Key

### The `createApiKey` Function

In [`src/lib/db/apiKeys.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/apiKeys.ts) (lines 61–66), the **`createApiKey`** function requires a machine identifier, a human-readable name, optional scopes, and an optional connection allowlist. It delegates entropy generation to **`generateApiKeyWithMachine`**, which produces the raw secret. The raw key is stored in the `key` column, while a **SHA-256** `key_hash` and a 12-character **`key_prefix`** are persisted for lookups and safe logging via `stmt.insertKey.run` (lines 95–107).

### Default Model Permissions

New keys default to **`modelAccessMode`: "all"** with an empty **`allowedModels`** array, meaning every model is permitted until explicitly restricted. This default is set during insertion and can be narrowed later through updates.

```ts
// Create a new key (e.g., from the CLI or a server-side script)
import { createApiKey } from "@/lib/db/apiKeys";

const newKey = await createApiKey(
  "My integration",
  "01F8MECHZX3TBDSZ7XRADM79VE",   // machineId
  ["read", "write"],               // optional scopes
  { allowedConnections: ["openai"] } // optional connection allowlist
);
// newKey.key holds the raw secret – store it securely!

```

## Database Schema and Persistence

### Core Table Definition

The **`api_keys`** table is defined in [`src/lib/db/core.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/core.ts) (line 307). It stores the raw token, hash, prefix, and metadata required for runtime decisions.

### Migration-Driven Schema Evolution

The schema is enriched automatically by [`src/lib/db/migrationRunner.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/migrationRunner.ts) (lines 526–531), which adds:

- `ip_allowlist` — a TEXT column holding a JSON array of permitted IPs.
- `revoked_at` and `expires_at` — timestamps that terminate validity.
- Columns for scopes, usage limits, and feature flags.

Migrations run automatically on startup, and the schema check is memoized after the first evaluation (`_schemaChecked`).

## Validating and Using an API Key

At runtime, the **`validateKey`** prepared statement in [`src/lib/db/apiKeys.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/apiKeys.ts) matches an incoming `Authorization: Bearer <key>` header or query-string token against the `key` or `key_hash` columns. On match, the full **`ApiKeyMetadata`** is loaded, including the model allowlist, IP allowlist, and activity timestamps.

## Model Permissions and Access Control

### Model Access Modes

The **`modelAccessMode`** field governs how `allowedModels` and `blockedModels` are interpreted. The default `"all"` mode permits every model; administrators can switch to explicit allowlists or blocklists by updating these arrays.

### Wildcard Pattern Matching

Helpers in [`src/lib/db/apiKeys/modelPermissions.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/apiKeys/modelPermissions.ts), such as **`modelPatternMatches`** and **`hasClaudeCodeWildcardPermission`**, evaluate wildcard patterns against the requested model name. This allows flexible policies like permitting all Claude variants without enumerating every release.

## IP Allowlist Enforcement

OmniRoute stores permitted IPs as a JSON array in the **`ip_allowlist`** column. During request processing, [`src/sse/services/auth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/sse/services/auth.ts) compares the client IP against this list and rejects requests from unrecognized addresses. There is no dedicated blocklist column; a deny-list effect is achieved by restricting `ip_allowlist` to only trusted IPs.

```ts
// Add an IP allow-list to an existing key
import { getApiKeyById, updateApiKey } from "@/lib/db/apiKeys";

const key = await getApiKeyById(keyId);
await updateApiKey(keyId, {
  ipAllowlist: ["203.0.113.45", "2001:db8::1"]   // only these IPs can use the key
});

```

## Regeneration and Revocation

### Rotating Secrets with `regenerateApiKey`

The **`regenerateApiKey`** function (lines 15–33 in [`src/lib/db/apiKeys.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/apiKeys.ts)) creates a fresh secret while preserving the original machine ID. It updates the `key`, `key_hash`, and `key_prefix` columns, clears all caches, and removes stale Redis auth cache entries. A **`logAuditEvent`** call (line 38) emits an `apiKey.regenerate` audit event, and **`backupDbFile("pre-write")`** preserves the database state before mutation.

```ts
// Regenerate an existing key (rotate secrets)
import { regenerateApiKey } from "@/lib/db/apiKeys";

const rotated = await regenerateApiKey(existingKeyId);
// rotated.key is the fresh secret; the old secret stops working immediately.

```

### Revoking a Key

Revocation sets the **`revoked_at`** timestamp and forces **`is_active`** to `0` (line 1165 in [`src/lib/db/apiKeys.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/apiKeys.ts)). Once revoked, the key instantly fails validation and cannot be used for future requests.

```ts
// Revoke a key (disable it permanently)
import { revokeApiKey } from "@/lib/db/apiKeys";

await revokeApiKey(keyId);   // sets revoked_at and marks is_active = false

```

## Auditing and Safety Mechanisms

Every mutating operation triggers **`backupDbFile("pre-write")`** to create a recoverable snapshot. Audit events are emitted through **`logAuditEvent`**, providing an immutable trail of creation, rotation, and revocation actions that spans the entire **OmniRoute API key lifecycle**.

## Summary

- OmniRoute centralizes the API key lifecycle in [`src/lib/db/apiKeys.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/apiKeys.ts), from creation to revocation.
- New keys are generated with `createApiKey` and default to `modelAccessMode: "all"`, allowing unrestricted model access until explicitly limited.
- The SQLite schema in [`src/lib/db/core.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/core.ts) is extended by [`migrationRunner.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/migrationRunner.ts) to support `ip_allowlist`, `revoked_at`, and usage-limit columns.
- Runtime validation in [`src/sse/services/auth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/sse/services/auth.ts) enforces IP restrictions by comparing the client address against the JSON allowlist.
- Secrets are rotated safely via `regenerateApiKey`, which updates hashes, clears caches, and emits `apiKey.regenerate` audit events.
- Revocation immediately disables a key by setting `revoked_at` and `is_active = 0`.

## Frequently Asked Questions

### How is an OmniRoute API key generated?

An API key is generated by calling `createApiKey` in [`src/lib/db/apiKeys.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/apiKeys.ts), which invokes `generateApiKeyWithMachine` to produce a high-entropy secret. The function persists a raw `key`, a SHA-256 `key_hash`, and a 12-character `key_prefix` while defaulting model access to `"all"`.

### What are the default model permissions for a new API key?

By default, a new key receives `modelAccessMode: "all"` and an empty `allowedModels` array, permitting access to every model. Downstream updates can restrict access by populating `allowedModels` or `blockedModels` and changing the access mode.

### How does OmniRoute enforce IP allowlists?

OmniRoute stores allowed IPs as a JSON array in the `ip_allowlist` column of the `api_keys` table. During request handling, [`src/sse/services/auth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/sse/services/auth.ts) parses this list and rejects any client whose IP is not included, effectively acting as a network-level gate.

### How do I rotate or revoke an existing API key?

Rotate a key by calling `regenerateApiKey` (lines 15–33 in [`src/lib/db/apiKeys.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/apiKeys.ts)), which issues a new secret while preserving the machine ID and clearing caches. Revoke a key by calling `revokeApiKey`, which sets `revoked_at` and forces `is_active` to `0`, immediately terminating all future requests.