# How the FreeLLMAPI Model Catalog Sync Works: Signed Catalog Synchronization Explained

> Understand the FreeLLMAPI model catalog sync. Discover how signed catalog synchronization works for secure and verified model updates, even offline.

- Repository: [Tashfeen/freellmapi](https://github.com/tashfeenahmed/freellmapi)
- Tags: internals
- Published: 2026-08-30

---

**FreeLLMAPI synchronizes its local model catalog with an upstream signed catalog service through a cryptographically verified process that runs on a scheduled interval and supports offline re-application of cached catalogs.**

The **FreeLLMAPI model catalog sync** is a security-critical background service that keeps your local deployment aligned with the latest available AI models. Located in [`server/src/services/catalog-sync.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/catalog-sync.ts), this system fetches, verifies, and applies model definitions while preserving user customizations and ensuring zero-trust integrity through Ed25519 signatures.

## Scheduler Initialization and Boot Sequence

When the **FreeLLMAPI** server starts, `startCatalogSync()` establishes the synchronization lifecycle.

The function performs three critical setup tasks:

- Registers a **one-off timer** (`BOOT_DELAY_MS`) for the initial sync after startup
- Schedules a **recurring interval** (`SYNC_INTERVAL_MS`, approximately twice daily)
- Immediately calls `reapplyCachedCatalog()` to restore the last verified catalog from the local SQLite `settings` table

This dual-timer approach ensures rapid availability of models after boot while maintaining freshness through periodic updates.

```ts
// Start the background sync (usually called from server initialization)
import { startCatalogSync } from './services/catalog-sync.js';
import { scheduler } from '../lib/scheduler.js';

startCatalogSync(scheduler);

```

Source: [`server/src/services/catalog-sync.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/catalog-sync.ts) lines 33-45

## Fetching the Catalog with Incremental Updates

The `syncCatalog()` function constructs authenticated requests to `<base-url>/v1/latest` with intelligent delta handling.

Request construction follows these rules:

- **Premium authentication**: When a license key exists, adds `Authorization: Bearer <key>` header
- **Incremental fetch**: Appends `?since=<applied_version>` when a version is already applied (unless `force=true`)
- **Fresh fetch**: Bypasses the `since` parameter when forcing a full refresh

```ts
// Manually trigger a sync, e.g. after a user upgrades their license
import { syncCatalog } from './services/catalog-sync.js';

const result = await syncCatalog(true); // `force=true` ignores the `since` shortcut
console.log(result); 
// {ok: true, action: 'applied', version: '2026.09.01', ...}

```

The `since` parameter enables bandwidth-efficient synchronization by allowing the server to return only changed portions of the catalog when the version hasn't advanced.

Source: lines 71-84

## Cryptographic Signature Verification

Before any catalog data is processed, **FreeLLMAPI** enforces strict signature validation to prevent supply-chain attacks.

The verification flow in `syncCatalog()`:

1. Extracts the `x-catalog-signature` header from the HTTP response
2. Reads the response body as raw bytes
3. Verifies the signature against the **pinned Ed25519 public key** (`PINNED_CATALOG_PUBKEY`) embedded in the binary

If verification fails, the entire response is discarded and the sync aborts. This **signature-first security model** ensures that even a compromised CDN cannot inject malicious model definitions, as the client only trusts content signed by the matching private key held by the upstream service.

Source: lines 94-99

## Payload Validation and Schema Checking

After signature verification, the JSON payload undergoes strict validation through `isCatalog()`.

The validator checks required structural fields:

- `version` — catalog version string
- `tier` — deployment tier identifier
- `models` — array of chat model definitions
- `quirks` — model behavior overrides
- Optional arrays: `embeddings`, `videoModels`, `mediaModels`, `transcriptionModels`

Type checking ensures each field conforms to expected shapes before the sync proceeds. This prevents partially corrupted or malformed catalogs from affecting the database state.

Source: lines 100-150

## Version Safety and Migration Protection

The **FreeLLMAPI model catalog sync** includes a safeguard against destructive rollbacks.

Before applying any catalog, the system compares `catalog.version` against `MIN_CATALOG_VERSION` — a hard-coded constant representing the bundled migration baseline. If the fetched catalog version is **older** than this minimum, the sync is skipped entirely.

This protection ensures that:
- Downgrade attacks via stale catalog delivery are blocked
- Database migrations always move forward
- Offline re-application of cached catalogs never regresses schema state

Source: lines 104-109

## Atomic Catalog Application

When version or tier changes are detected, `applyCatalog()` executes within a **single SQLite transaction** to maintain consistency.

The application process handles multiple model categories:

| Model Type | Target Table | Special Handling |
|------------|-----------|----------------|
| Chat models | `models` | Respects user-owned rows, applies overrides |
| Image/audio | `media_models` | Gated by `MEDIA_PLATFORMS` allow-list |
| Video | `videoModels` | Gated by `VIDEO_PLATFORMS` allow-list |
| Transcription | `transcriptionModels` | Gated by `TRANSCRIPTION_PLATFORMS` allow-list |
| Embeddings | `embeddings` | Optional snapshot section |

Key application behaviors:

- **User row protection**: Rows with `source='user'` are preserved and never overwritten or deleted
- **Tombstone awareness**: Checks `catalog_model_tombstones` for permanently retired models
- **Override handling**: Applies `model_config_overrides` and `fallback_config` rows
- **Profile consistency**: Maintains relationships between user profiles and available models
- **Stale row cleanup**: Deletes catalog-managed rows (`source='catalog'`) that no longer appear in the new snapshot
- **Quirk replacement**: Clears existing quirks and inserts the complete new set wholesale

Source: lines 57-66, 75-129, 140-250, 260-470

## State Persistence and Caching

After successful application, the sync state is persisted to the `settings` table:

- `catalog_applied_version` — the applied catalog version
- `catalog_applied_tier` — the deployment tier
- `catalog_applied_json` — complete raw catalog JSON for offline use

Additional metadata is recorded:

- `lastSyncMs` — timestamp of the sync attempt
- `lastError` — error message if the sync failed

This caching enables the **offline re-application** capability that makes **FreeLLMAPI** resilient to network outages.

Source: lines 114-130

## License Status Refresh

Independently of catalog synchronization, `refreshLicenseStatus()` maintains premium entitlement state.

This function:

- Contacts `/v1/license/check` when a license key is configured
- Updates cached license status in the database
- Logs failures without aborting the catalog sync

The separation of concerns allows model availability to proceed even when license verification is temporarily unavailable.

Source: lines 44-63

## Offline Re-Application via Cached Catalogs

The `reapplyCachedCatalog()` function provides **deterministic recovery** for server restarts.

Execution flow:

1. Reads `catalog_applied_json` from the `settings` table
2. Re-validates the cached payload with `isCatalog()`
3. Executes `applyCatalog()` without any network traffic

This guarantees that the local database reflects the last known-good catalog state even when the server boots without internet connectivity. The re-validation step ensures data integrity against disk corruption or manual tampering.

Source: lines 95-108

## Querying Sync State

The `getSyncState()` function exposes current synchronization status for monitoring and UI purposes.

```ts
// Query the current sync state (useful for UI)
import { getSyncState } from './services/catalog-sync.js';

const state = getSyncState();
console.log(state);
/*
{
  baseUrl: 'https://api.freellmapi.co',
  appliedVersion: '2026.09.01',
  appliedTier: 'live',
  lastSyncMs: 1725112000000,
  lastError: ''
}
*/

```

This enables dashboards to display synchronization health, applied catalog versions, and error conditions to administrators.

## Key Architectural Files

| File | Role |
|------|------|
| [`server/src/services/catalog-sync.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/catalog-sync.ts) | Core implementation: fetching, verifying, applying, and caching |
| [`server/src/services/model-state.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/model-state.ts) | Tombstone handling, overrides, and retired model reinstatement |
| [`server/src/services/media.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/media.ts) | Platform allow-lists (`MEDIA_PLATFORMS`, `VIDEO_PLATFORMS`, `TRANSCRIPTION_PLATFORMS`) |
| [`server/src/db/index.js`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/db/index.js) | SQLite helpers (`getDb`, `getSetting`, `setSetting`) |
| [`shared/types.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/shared/types.ts) | Shared type definitions including `Platform` |

## Summary

- **FreeLLMAPI model catalog sync** is driven by [`catalog-sync.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/catalog-sync.ts) with scheduled and on-demand execution modes
- **Ed25519 signature verification** provides zero-trust integrity before any data is processed
- **Incremental updates** via the `since` parameter reduce bandwidth; `force=true` enables full refresh
- **Migration protection** via `MIN_CATALOG_VERSION` prevents destructive catalog rollbacks
- **Atomic SQLite transactions** ensure consistent application across chat, media, video, transcription, and embedding models
- **Source-of-truth handling** preserves user-owned rows and respects tombstones
- **Offline resilience** through cached catalog re-application on every boot

## Frequently Asked Questions

### How often does FreeLLMAPI sync the model catalog?

The sync runs approximately **twice daily** via `SYNC_INTERVAL_MS`, plus an immediate attempt after `BOOT_DELAY_MS` on server startup. Administrators can trigger manual syncs with `syncCatalog(true)` at any time.

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

The entire response is **discarded immediately**. No database changes occur, and the error is recorded in `lastError`. The system continues operating with the previously applied (or re-applied cached) catalog until a valid signed response is received.

### Can FreeLLMAPI work without internet connectivity?

**Yes**. On every boot, `reapplyCachedCatalog()` validates and re-applies the last known-good catalog from local SQLite storage without network access. Models that were available before disconnection remain functional.

### How does FreeLLMAPI handle user-customized model configurations?

User-owned rows (`source='user'`) are **preserved across all syncs**. They take precedence over catalog definitions on collision and are never deleted by the synchronization process. The `catalog_model_tombstones` table separately tracks permanently retired upstream models.