# How the Self-Updating Model Catalog Sync Mechanism Works in FreeLLMAPI

> Discover how FreeLLMAPI's self-updating model catalog sync mechanism works. It fetches, verifies, and reconciles remote feeds with local data twice daily.

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

---

**FreeLLMAPI maintains its model catalog through an automated sync process that fetches a signed, version-gated remote feed twice daily, verifies it with Ed25519 signatures, and atomically reconciles it with a local SQLite database while preserving user overrides.**

The self-updating model catalog sync mechanism in FreeLLMAPI ensures that local provider configurations stay current without manual intervention. This system operates through a cryptographically verified, scheduled pipeline that balances network efficiency with offline resilience. Understanding this architecture is essential for operators who need to troubleshoot sync failures or customize model behavior.

## Architecture Overview

The mechanism operates through three coordinated phases defined in [`server/src/services/catalog-sync.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/catalog-sync.ts):

- **Boot Rehydration** – On startup, the server restores the last verified catalog from disk so the SQLite database remains functional even without network connectivity.
- **Scheduled Synchronization** – A background scheduler initiates sync cycles twice daily, fetching remote updates, verifying signatures, and atomically applying changes.
- **State Persistence** – The system tracks applied versions, tiers, timestamps, and error states in the `settings` table, enabling observability and crash recovery.

This pipeline is initialized in [`server/src/index.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/index.ts) at lines 77‑78, where `startCatalogSync` is invoked after the HTTP server begins accepting connections.

## The Sync Lifecycle

### Boot Sequence and Cache Rehydration

When the server starts, `startCatalogSync` immediately calls `reapplyCachedCatalog()` to restore the previous state. This function retrieves the cached JSON from the `settings` table under keys like `SETTING_APPLIED_JSON`, validates it again using `isCatalog()`, and executes `applyCatalog()` to ensure the database reflects the last known good configuration.

A deliberate `BOOT_DELAY_MS` of 10 seconds prevents the first live sync from interfering with other initialization routines. After this interval, the scheduler triggers the initial remote fetch.

### Fetching and Signature Verification

The `syncCatalog()` function constructs a request to `/v1/latest`, injecting an `Authorization: Bearer <key>` header when a premium license is configured. To optimize bandwidth, it appends a `since=<applied>` query parameter, allowing the server to return **304 Not Modified** responses when the catalog version remains unchanged.

Upon receiving a payload, the system extracts the `x-catalog-signature` header and verifies the Ed25519 signature against the hard-coded `PINNED_CATALOG_PUBKEY` or an environment override. This cryptographic validation ensures that only authentic, untampered catalogs are processed.

### Validation and Version Gating

Before application, `isCatalog()` performs structural validation against the JSON schema, checking required fields, array shapes, and nested object types. The system then compares the catalog's `version` property against `MIN_CATALOG_VERSION` (the baseline bundled with the binary). If the remote version is older, the sync aborts to prevent regression of database migrations.

### Atomic Database Application

The `applyCatalog()` function executes within a single SQLite transaction, ensuring consistency during updates. This process:

- Inserts or updates chat models, media models, embeddings, transcription, and video models
- Preserves user-added rows marked with `source='user'` and respects tombstone records managed by [`server/src/services/model-state.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/model-state.ts)
- Applies per-model overrides via `applyModelOverrides` (referenced in [`server/src/services/model-weight-overrides.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/model-weight-overrides.ts))
- Prunes entries that have disappeared from the upstream catalog
- Replaces quirks definitions wholesale

After successful application, the system persists the new `version`, `tier`, and raw JSON to the `settings` table under `SETTING_APPLIED_VERSION`, `SETTING_APPLIED_TIER`, and `SETTING_APPLIED_JSON`, along with timestamps for observability.

## Scheduler Configuration and License Integration

The scheduler runs every `SYNC_INTERVAL_MS` (12 hours) as configured in [`catalog-sync.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/catalog-sync.ts) at lines 445‑447. Independently, `refreshLicenseStatus()` probes `/v1/license/check` during each cycle to validate premium entitlement, ensuring that license state and catalog tier remain synchronized.

## Manual Sync and Debugging

Developers can trigger on-demand synchronization programmatically:

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

async function forceRefresh() {
  const result = await syncCatalog(true);
  console.log('Catalog sync result:', result);
  console.log('Current sync state:', getSyncState());
}

forceRefresh().catch(console.error);

```

Passing `true` to `syncCatalog()` bypasses the `since` parameter optimization, forcing a full download regardless of the cached version. This is useful for debugging or immediate updates.

## Graceful Shutdown and Error Handling

The `stopCatalogSync()` function clears the boot timer and interval handles, preventing stray network calls during process termination. Error states are captured in the `settings` table, making sync failures observable through the `getSyncState()` API.

## Summary

- The self-updating model catalog sync mechanism relies on **Ed25519 signature verification** to ensure catalog authenticity before any database modifications occur.
- The system operates on a **twice-daily schedule** with a 10-second boot delay, while supporting immediate manual triggers via `syncCatalog(true)`.
- All database applications are **atomic transactions** that preserve user overrides, tombstones, and platform-specific rules defined in files like [`server/src/services/model-state.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/model-state.ts).
- **Offline resilience** is maintained through cached catalog rehydration during server startup, ensuring continuous operation without network connectivity.
- Version gating prevents rollback to older catalogs than `MIN_CATALOG_VERSION`, protecting database schema integrity.

## Frequently Asked Questions

### How does FreeLLMAPI handle catalog updates when the server is offline?

During boot, `reapplyCachedCatalog()` loads the last verified catalog from the SQLite `settings` table using `SETTING_APPLIED_JSON`. This allows the server to function with stale but valid model metadata until network connectivity returns and the next scheduled sync succeeds.

### What prevents malicious or corrupted catalogs from modifying the database?

Every catalog payload must carry a valid `x-catalog-signature` header that cryptographically verifies against the `PINNED_CATALOG_PUBKEY` using Ed25519. Additionally, `isCatalog()` validates JSON structure, and version gating ensures the catalog is not older than the binary's baseline.

### Can I force an immediate catalog update outside the 12-hour schedule?

Yes. Import `syncCatalog` from [`server/src/services/catalog-sync.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/catalog-sync.ts) and invoke it with `syncCatalog(true)` to bypass the `since` parameter optimization. This forces a fresh download and application of the latest catalog regardless of the cached version.

### Where does the system store the current catalog version and tier?

The applied version, tier, and raw JSON are stored in the SQLite `settings` table under the keys `SETTING_APPLIED_VERSION`, `SETTING_APPLIED_TIER`, and `SETTING_APPLIED_JSON`. These values persist across restarts and enable the boot-time cache rehydration mechanism.