FreeLLMAPI Model Catalog Sync Mechanism: How It Fetches, Verifies, and Applies Updates

FreeLLMAPI synchronizes its local SQLite database with a cryptographically signed remote catalog every 12 hours, verifying Ed25519 signatures, gating updates by minimum version, and applying changes atomically while preserving user-disabled model states and caching verified catalogs for offline resilience.

FreeLLMAPI maintains an up-to-date model catalog through a robust synchronization mechanism implemented in server/src/services/catalog-sync.ts. This system ensures that self-hosted instances receive the latest model definitions, capability quirks, and pricing tiers without manual intervention. The mechanism combines cryptographic verification, conditional HTTP requests, and atomic database transactions to safely update the local catalog while maintaining zero-downtime operation.

Bootstrap and Periodic Polling

The sync lifecycle begins when startCatalogSync() is invoked at server startup (source lines 81‑87). This function implements a two-phase initialization:

  • Boot delay: A BOOT_DELAY_MS of 10 seconds allows the rest of the server to initialize before the first network call.
  • Recurring schedule: After the initial delay, syncCatalog() executes immediately and then schedules itself to repeat every SYNC_INTERVAL_MS (12 hours) (source lines 43‑44, 94‑95).

Each execution also refreshes the license status, ensuring that premium tier changes are detected promptly.

Fetching and Authenticating the Catalog

The syncCatalog() function constructs a request to <base-url>/v1/latest with tier-aware authentication:

  • Premium tier detection: If a license key exists, the function adds an Authorization: Bearer <key> header, which causes the service to return the live tier catalog; otherwise, a monthly snapshot is served (source lines 28‑31).
  • Conditional requests: When the server already knows the last applied version, it appends a since query parameter, allowing the server to return 304 Not Modified if the catalog has not changed, saving bandwidth and processing time (source lines 31‑33).

Cryptographic Verification and Version Gating

Before any update is applied, the mechanism performs strict security validation:

Ed25519 Signature Verification

The HTTP response must include an x-catalog-signature header. The raw response body is verified against an Ed25519 public key that is hard-coded in the binary (or overridden via the CATALOG_PUBKEY environment variable) (source lines 60‑63, 42‑45). If verification fails, the entire update is discarded to prevent tampering.

Minimum Version Enforcement

Even verified catalogs are rejected if their version field is older than MIN_CATALOG_VERSION (set to 2026.06.07 at the time of writing). This prevents stale monthly snapshots from rolling back database migrations that are baked into the binary (source lines 39‑42, 52‑58).

Applying Updates Atomically

When a newer valid catalog is detected, applyCatalog() executes inside a single SQLite transaction to ensure consistency (source lines 55‑56, 90‑91). This process handles several distinct data types:

Model Upserts and User Preferences

For each model entry in the catalog, the system either updates an existing row or inserts a new one. Importantly:

  • Catalog-disabled models: If the catalog sets enabled to false, the model is disabled regardless of local state.
  • User-disabled models: A model that was manually disabled locally remains disabled even if the catalog later re-enables it, preserving user autonomy (source lines 44‑53).

Media Model Routing

Entries with a modality of image or audio are routed to a separate media_models table, while unknown platforms are counted and skipped to ensure forward compatibility (source lines 18‑30, 95‑102).

Custom Provider Handling

Models belonging to a custom platform or to providers not compiled into the current binary are ignored. This allows the catalog to include bleeding-edge providers without breaking older server versions (source lines 22‑27).

Cleanup and Quirks

The routine performs garbage collection on stale data:

  • Orphaned models: Models that disappeared from the catalog are deleted, including their associated fallback_config rows (source lines 68‑81, 84‑94).
  • Quirks replacement: All existing quirks are wiped and replaced wholesale with the catalog’s quirks array, ensuring behavior changes propagate exactly as specified (source lines 96‑110).

The function returns a counts summary object detailing updated, inserted, and removed rows, skipped platforms, and quirks applied, which is logged for observability.

Offline Resilience and State Management

FreeLLMAPI ensures continuity during network outages through aggressive caching:

Cached Catalog Re-application

On every boot, reapplyCachedCatalog() reads the cached JSON from the settings table, validates it again, and re-applies it without a network call. This prevents database drift when the server restarts while offline (source lines 58‑77).

Persistent State Tracking

After a successful apply, the service records:

  • catalog_applied_version, catalog_applied_tier, and the raw verified JSON (catalog_applied_json) in the settings table (source lines 62‑70).
  • catalog_last_sync_ms timestamp and clears any previous error messages.

Error Handling

Any failure—whether HTTP errors, missing signatures, verification failures, or malformed payloads—is caught and stored in catalog_last_error. The sync returns { ok: false, action: 'error' } to signal the failure without crashing the server (source lines 84‑89).

Implementation Examples

Start the background sync (typically invoked in the server entry point):

import { startCatalogSync } from './server/src/services/catalog-sync.js';

// Begins the 10s boot-delay, then polls twice daily
startCatalogSync();

Force a one-off catalog refresh (e.g., after premium key activation):

import { syncCatalog } from './server/src/services/catalog-sync.js';

// Bypasses the `since` short-circuit to force a full fetch
await syncCatalog(/* force */ true);

Manually re-apply the cached catalog (useful after database restoration):

import { reapplyCachedCatalog } from './server/src/services/catalog-sync.js';

const result = reapplyCachedCatalog();
console.log('Re-apply outcome:', result);

Inspect the current sync state:

import { getSyncState } from './server/src/services/catalog-sync.js';

console.log(getSyncState());
/* {
   baseUrl: 'https://api.freellmapi.co',
   appliedVersion: '2026.06.07',
   appliedTier: 'monthly',
   lastSyncMs: 1687718400000,
   lastError: ''
} */

Summary

  • FreeLLMAPI pulls the model catalog from <base-url>/v1/latest every 12 hours via startCatalogSync() in server/src/services/catalog-sync.ts.
  • All updates require Ed25519 signature verification via the x-catalog-signature header and must satisfy the MIN_CATALOG_VERSION gate.
  • Changes are applied atomically within a SQLite transaction, with user-disabled models preserved, media models routed to separate tables, and custom providers ignored.
  • The verified catalog JSON is cached in the settings table, enabling reapplyCachedCatalog() to restore state during offline restarts.
  • Comprehensive error handling stores failure messages in catalog_last_error without interrupting server operation.

Frequently Asked Questions

How does FreeLLMAPI handle updates when the server is offline?

FreeLLMAPI stores the last successfully verified catalog JSON in the settings table as catalog_applied_json. On every boot, reapplyCachedCatalog() validates and re-applies this cached version without making a network request, ensuring the database remains synchronized with the last known state even without internet connectivity.

What prevents a malicious catalog from downgrading the system?

The sync mechanism implements version gating using MIN_CATALOG_VERSION (currently 2026.06.07). Even if a catalog passes Ed25519 signature verification, it is rejected if its version field is older than this constant, preventing stale snapshots from rolling forward-compatible database migrations.

How does the sync mechanism respect user preferences for disabled models?

During the atomic apply phase in applyCatalog(), the system checks local state before enabling models. If a user has manually disabled a model, that flag persists in the database even if the incoming catalog marks it as enabled, ensuring that user autonomy overrides remote configuration.

What is the difference between live and monthly catalog tiers?

The tier depends on authentication: requests with a valid Authorization: Bearer header receive the live tier (updated continuously), while unauthenticated requests receive a monthly snapshot. This allows premium users to access immediate updates while free-tier instances receive stable, batched updates.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →