OmniRoute API Key Lifecycle: Generation, Model Permissions, and IP Access Control
The OmniRoute API key lifecycle is managed entirely through 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, 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 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 (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.
// 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 (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 (lines 526–531), which adds:
ip_allowlist— a TEXT column holding a JSON array of permitted IPs.revoked_atandexpires_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 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, 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 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.
// 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) 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.
// 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). Once revoked, the key instantly fails validation and cannot be used for future requests.
// 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, from creation to revocation. - New keys are generated with
createApiKeyand default tomodelAccessMode: "all", allowing unrestricted model access until explicitly limited. - The SQLite schema in
src/lib/db/core.tsis extended bymigrationRunner.tsto supportip_allowlist,revoked_at, and usage-limit columns. - Runtime validation in
src/sse/services/auth.tsenforces IP restrictions by comparing the client address against the JSON allowlist. - Secrets are rotated safely via
regenerateApiKey, which updates hashes, clears caches, and emitsapiKey.regenerateaudit events. - Revocation immediately disables a key by setting
revoked_atandis_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, 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 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), 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.
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 →