# How to Debug Connection Cooldowns and Model Lockout States in OmniRoute

> Debug OmniRoute connection cooldowns and model lockout states by inspecting the Domain Lockout Policy. Learn how to check lockouts, record failures, and clear them for runtime inspection.

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

---

**To debug connection cooldowns and model lockout states in OmniRoute, inspect the Domain Lockout Policy at [`src/domain/lockoutPolicy.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/domain/lockoutPolicy.ts), which tracks failed attempts in a hybrid in-memory/SQLite cache and exposes `checkLockout`, `recordFailedAttempt`, and `clearAllLockouts` for runtime inspection.**

OmniRoute implements connection-rate throttling and model lockout protection through a centralized **Domain Lockout Policy**. When troubleshooting why a client receives 429 or 403 errors, you need to understand how the system tracks failed authentication attempts and manages cooldown windows. This guide shows you how to debug connection cooldowns and model lockout states in OmniRoute by examining the state lifecycle, inspecting the hybrid storage layer, and using the exposed debugging utilities.

## Understanding the Domain Lockout Architecture

OmniRoute isolates lockout logic inside [`src/domain/lockoutPolicy.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/domain/lockoutPolicy.ts). This module tracks failed authentication or request attempts per identifier—such as IP address, API key, or username—and determines when to enforce a cooldown.

The system uses a **two-tier storage strategy**:

- **In-memory cache**: A JavaScript `Map` named `lockoutCache` provides fast lookups for active lockout states.
- **SQLite persistence**: The module falls back to [`src/lib/db/domainState.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/domainState.ts) for durable storage, ensuring lockout states survive process restarts.

When debugging, you can interact with either layer directly using the exported functions from the lockout policy module.

## Debugging the Lockout State Lifecycle

The lockout mechanism follows a predictable lifecycle. Understanding these six phases helps you identify why a request is being rejected.

### State Loading and Cache Behavior

When `checkLockout` is invoked, it first attempts to retrieve a cached lockout record from the in-memory `lockoutCache`. If the cache misses, the function loads the persisted record from SQLite via `loadLockoutState` ([lines 41‑49](https://github.com/diegosouzapw/OmniRoute/blob/main/src/domain/lockoutPolicy.ts#L41-L49)).

```typescript
import { checkLockout } from '@/domain/lockoutPolicy';

const identifier = '192.168.1.42'; // IP, API key, or username
const result = checkLockout(identifier);

if (result.locked) {
  console.log(
    `Locked out – ${result.remainingMs! / 1000}s remaining (attempts: ${result.attempts})`
  );
} else {
  console.log('No lockout – safe to proceed');
}

```

### Expiration Handling and Cleanup

If a record exists and `lockedUntil` is in the future, the function returns `locked: true` together with the remaining cooldown duration in `remainingMs` ([lines 86‑92](https://github.com/diegosouzapw/OmniRoute/blob/main/src/domain/lockoutPolicy.ts#L86-L92)).

When the lockout window has elapsed, the module automatically clears `lockedUntil` and the attempt list, persisting the refreshed state back to the database ([lines 95‑99](https://github.com/diegosouzapw/OmniRoute/blob/main/src/domain/lockoutPolicy.ts#L95-L99)).

### Attempt Counting and Pruning

Each failed attempt is recorded via `recordFailedAttempt`, which stores timestamped entries in `state.attempts`. The policy prunes attempts older than `attemptWindowMs` (default 5 minutes) before evaluating the threshold.

### Lockout Trigger Conditions

When the number of recent attempts reaches `maxAttempts` (default 5), the service calculates the lockout expiry and sets `lockedUntil = Date.now() + lockoutDurationMs` (default 15 minutes). It then returns a lockout response ([lines 133‑138](https://github.com/diegosouzapw/OmniRoute/blob/main/src/domain/lockoutPolicy.ts#L133-L138)).

### Policy Engine Integration

The [`src/domain/policyEngine.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/domain/policyEngine.ts) orchestrator invokes `checkLockout` early in the request pipeline ([line 52](https://github.com/diegosouzapw/OmniRoute/blob/main/src/domain/policyEngine.ts#L52-L58)). If the client is locked, the engine aborts the request with a 429 or 403 error before reaching the model layer.

## Practical Debugging Techniques

Use these runtime utilities to inspect and manipulate lockout states during development.

### Checking Current Lockout Status

To verify whether a specific identifier is currently locked out, call `checkLockout` directly from your debugging console or test script:

```typescript
import { checkLockout } from '@/domain/lockoutPolicy';

const identifier = '192.168.1.42';
const result = checkLockout(identifier);

if (result.locked) {
  console.log(
    `Locked out – ${result.remainingMs! / 1000}s remaining (attempts: ${result.attempts})`
  );
} else {
  console.log('No lockout – safe to proceed');
}

```

*(See `checkLockout` definition at lines 80‑92.)*

### Resetting All Lockouts

During development or integration testing, you may need to clear all active lockouts without waiting for expiration. The `clearAllLockouts` function removes every entry from both the in-memory cache and the SQLite database:

```typescript
import { clearAllLockouts } from '@/domain/lockoutPolicy';

// Remove every entry from the in-memory cache and DB
clearAllLockouts();
console.log('All lockouts cleared – next request will be allowed');

```

*(See `clearAllLockouts` near the end of [`src/domain/lockoutPolicy.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/domain/lockoutPolicy.ts).)*

### Simulating Failed Attempts

To test the lockout trigger threshold manually, use `recordFailedAttempt` to programmatically register failures:

```typescript
import { recordFailedAttempt, checkLockout } from '@/domain/lockoutPolicy';

const id = 'my-api-key';
for (let i = 0; i < 6; i++) {
  recordFailedAttempt(id);
}
const status = checkLockout(id);
console.log(status); // { locked: true, remainingMs: … }

```

*(See `recordFailedAttempt` implementation in [`src/domain/lockoutPolicy.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/domain/lockoutPolicy.ts).)*

## Inspecting the SQLite Persistence Layer

Because lockout state is durably stored in SQLite via [`src/lib/db/domainState.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/domainState.ts), you can inspect the raw database for debugging. The module provides three low-level helpers:

- **`loadLockoutState(identifier)`**: Retrieves the persisted record.
- **`saveLockoutState(identifier, state)`**: Writes the current state to disk.
- **`deleteLockoutState(identifier)`**: Removes the record entirely.

You can query the underlying database directly using SQLite CLI tools to verify that `lockedUntil` timestamps and attempt arrays match your expectations.

## Summary

- **OmniRoute** centralizes lockout logic in [`src/domain/lockoutPolicy.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/domain/lockoutPolicy.ts), using a hybrid in-memory/SQLite cache.
- **State lifecycle**: Check cache → load from DB → validate `lockedUntil` → prune old attempts → count recent failures → trigger lockout if threshold exceeded.
- **Debugging utilities**: Use `checkLockout` to inspect status, `clearAllLockouts` to wipe all states, and `recordFailedAttempt` to simulate failures.
- **Integration point**: The [`src/domain/policyEngine.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/domain/policyEngine.ts) invokes `checkLockout` at line 52, returning 429/403 for locked clients.
- **Persistence**: Raw state is accessible via [`src/lib/db/domainState.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/domainState.ts) for direct database inspection.

## Frequently Asked Questions

### How do I check if a specific IP is currently locked out in OmniRoute?

Call `checkLockout(identifier)` from the [`src/domain/lockoutPolicy.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/domain/lockoutPolicy.ts) module, passing the IP address as the identifier. The function returns an object with `locked: boolean` and `remainingMs: number` indicating the cooldown time left.

### Where is the lockout state stored in OmniRoute?

OmniRoute uses a two-tier system: an in-memory `Map` called `lockoutCache` for fast access, and SQLite for persistence. The SQLite operations are handled in [`src/lib/db/domainState.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/domainState.ts) via `loadLockoutState` and `saveLockoutState`.

### How do I clear all active lockouts without restarting the server?

Import `clearAllLockouts` from [`src/domain/lockoutPolicy.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/domain/lockoutPolicy.ts) and invoke it. This function clears both the in-memory cache and the SQLite database, immediately allowing all previously locked identifiers to make requests.

### What are the default thresholds for triggering a lockout?

By default, OmniRoute allows 5 failed attempts within a 5-minute window (`attemptWindowMs`). When the sixth attempt occurs, the system imposes a 15-minute lockout (`lockoutDurationMs`). These values are configurable in the lockout policy configuration.