# How the Catalog Sync Updates Available Models in FreeLLMAPI

> Learn how FreeLLMAPI's catalog sync automatically updates model availability every 12 hours, ensuring you always have access to the latest enabled models while respecting your settings.

- Repository: [Tashfeen/freellmapi](https://github.com/tashfeenahmed/freellmapi)
- Tags: how-to-guide
- Published: 2026-06-28

---

**FreeLLMAPI automatically synchronizes its local model catalog with a remote, signed catalog every 12 hours, updating the `enabled` status in the database while preserving local overrides, then derives final availability by checking for active API keys.**

The catalog sync in FreeLLMAPI ensures your local instance always has the latest model metadata from the upstream registry. This process runs automatically in the background and can be triggered on-demand, directly controlling which models appear in `GET /v1/models` responses based on cryptographic verification and your configured API keys.

## The Three-Stage Catalog Sync Process

The update flow implemented in [`server/src/services/catalog-sync.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/catalog-sync.ts) consists of three distinct stages: fetching and verifying the remote catalog, applying changes to the local SQLite database, and deriving runtime availability for the routing layer.

### Fetching and Verifying the Remote Catalog

The sync process begins with `startCatalogSync`, which registers the sync task with a scheduler. According to the source code in [`server/src/services/catalog-sync.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/catalog-sync.ts), this function schedules an initial boot-delay run followed by repeated executions every `SYNC_INTERVAL_MS = 12 h` (twice daily).

The `syncCatalog()` function constructs the request URL (`/v1/latest`) and adds a *Bearer* token when a premium license key is present. The request includes a configurable timeout (`FETCH_TIMEOUT_MS`). Upon receiving the response, the system validates the `x-catalog-signature` header against the response body using a hard-coded Ed25519 public key from `catalogPublicKey()`. If cryptographic verification fails, the catalog is discarded entirely to prevent tampering.

### Applying the Catalog to the Local Database

Once verified, the `applyCatalog()` function processes the incoming `CatalogModel` entries. This logic determines whether each model belongs to the chat pool (`models` table) or the media pool (`media_models` table) based on the `modality` field (`MEDIA_MODALITIES`).

For each model, the system performs an **upsert** operation:
- **Updates** existing rows while preserving the local `enabled` flag, unless the catalog explicitly disables the model
- **Inserts** new rows when the model is not yet present in the database

The sync respects local modifications by skipping entries where `platform === 'custom'` or where a local tombstone override exists. After processing all entries, the function deletes models that vanished from the catalog, removes tombstoned rows, and refreshes user-provided overrides via `applyAllModelOverrides`.

### Deriving Model Availability for Routing

The final availability calculation occurs in [`server/src/services/model-listing.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/model-listing.ts). The `buildModelListing()` function executes a SQL expression that checks whether a model is both enabled by the catalog sync and has a valid API key configured.

The availability logic uses the following expression:

```sql
(CASE WHEN m.enabled = 1 AND EXISTS (
     SELECT 1 FROM api_keys k
     WHERE k.platform = m.platform
       AND k.enabled = 1
       AND (m.key_id IS NULL OR k.id = m.key_id)
) THEN 1 ELSE 0 END)

```

This expression runs within a CTE (`availableExpr`) for every model row. The resulting `available` flag is **1** only when the model is enabled in the database and an active API key for its platform exists. This flag drives the `GET /v1/models` endpoint and determines the "auto" context-window ceiling.

## Code Examples

To start the background sync when the server boots:

```typescript
import { startCatalogSync } from './services/catalog-sync.js';
import { Scheduler } from './lib/scheduler.js';

const scheduler = new Scheduler();
startCatalogSync(scheduler);

```

To manually trigger a catalog refresh (for example, after adding a license key):

```typescript
import { syncCatalog } from './services/catalog-sync.js';

async function refreshCatalog() {
  const result = await syncCatalog(true); // force = true bypasses the `since` short-circuit
  console.log('Catalog refresh result:', result);
}

```

To retrieve the current list of available models as used by the OpenAI/Anthropic proxy routes:

```typescript
import { buildModelListing } from './services/model-listing.js';

function listModels() {
  const { models, autoContextWindow } = buildModelListing();
  console.log('Available models:', models);
  console.log('Auto context window:', autoContextWindow);
}

```

## Summary

- **Automatic synchronization** occurs every 12 hours via `startCatalogSync` in [`server/src/services/catalog-sync.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/catalog-sync.ts), with support for premium license authentication.
- **Cryptographic verification** using Ed25519 signatures ensures the remote catalog has not been tampered with before local application.
- **Database updates** preserve local overrides and custom platform configurations while synchronizing the `enabled` flag and metadata for standard models.
- **Runtime availability** depends on both the catalog's `enabled` flag and the presence of an active API key, calculated by `buildModelListing()` in [`server/src/services/model-listing.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/model-listing.ts).

## Frequently Asked Questions

### How often does the catalog sync run in FreeLLMAPI?

The sync runs automatically every 12 hours (`SYNC_INTERVAL_MS = 12 h`) after an initial boot-delay, as scheduled by `startCatalogSync`. You can also trigger it manually by calling `syncCatalog(true)` to force an immediate refresh.

### What happens if the catalog signature verification fails?

If the `x-catalog-signature` header does not match the Ed25519 verification against the hard-coded public key, the entire catalog is discarded. The system preserves the existing local catalog rather than applying potentially corrupted or malicious data.

### How does FreeLLMAPI handle custom models during the sync?

Models marked with `platform === 'custom'` are never modified by the catalog sync. Additionally, any model with a local tombstone override or user-specific configuration is preserved, ensuring that custom configurations survive remote updates.

### Why does a model show as enabled in the database but unavailable in the API?

The `enabled` column set by the catalog sync only indicates the upstream availability. The final availability exposed by `GET /v1/models` requires both `enabled = 1` and an active API key for that platform. If no enabled API key exists (or the specific `key_id` is inactive), the model remains unavailable for routing.