How the Catalog Sync Updates Available Models in FreeLLMAPI
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 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, 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
enabledflag, 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. 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:
(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:
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):
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:
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
startCatalogSyncinserver/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
enabledflag and metadata for standard models. - Runtime availability depends on both the catalog's
enabledflag and the presence of an active API key, calculated bybuildModelListing()inserver/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.
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 →