Auto-sync
The gateway rebuilds its model registry from every provider every 6 hours.
CleverRouter's model registry is not hand-maintained. The gateway rebuilds it from every configured provider's catalog every 6 hours. New models appear without an SDK release; retired models are flagged automatically.
At a glance
| Job | model-sync.ts in the gateway |
| Cadence | Every 6 hours |
| Crash policy | Per-provider failures don't kill the tick |
| Deprecated marker | deprecated_at = now(), returns 410 Gone |
What the cron job does
Every 6 hours, the gateway runs model-sync.ts:
- Call
listModels()on each configured provider (Scaleway, Tensorix, Bedrock). - UPSERT into the
modelsandmodel_providerstables. - Track
discoveredAt,lastSeenAt,autoSynced,pricingKnown,lifecycle(active/legacy/preview). - Models present last sync but missing in the current one (with
autoSynced=true) are flaggeddeprecated_at = now()andactive=false.
The job is crash-resistant: a single provider failure doesn't kill
the tick, a running flag prevents overlap, SIGTERM triggers a
clean shutdown.
Discovery vs whitelisting
| Behaviour | Auto-synced model | Manually seeded model |
|---|---|---|
| Appears after first sync | yes | seeded at deploy |
| Marked deprecated on miss | yes | no |
| Touched by re-sync | yes | no (preserved) |
| Pricing | from whitelist if present | from whitelist |
Pricing lives in a whitelist in packages/db/src/seeds/pricing.ts. A
model with no whitelist match has pricingKnown=false — the call
still runs, but cost tracking shows €0 until pricing is added.
Deprecation flow
When a provider removes a model:
- The next sync sees
autoSynced=truefor that ID but no longer finds it upstream. - The model is flagged
deprecated_at = now(),active=false. - Subsequent calls return
410 Gonewitherror.code: model_deprecated. - The SDK throws
CRModelDeprecatedErrorfor typed handling.
Manually seeded models are never auto-deprecated — that lets us maintain "alias" entries pointing to current model IDs.
Forcing a sync
Platform admins can trigger an out-of-band sync at
/app/platform/health ("Re-sync models" button). Internally
that hits POST /admin/sync on the gateway, authorised via the
X-Admin-Token header.
Defensive coding
const all = await cr.listModels({ kind: 'chat' });
const stillActive = all.filter((m) => !(m as any).deprecated_at);Or just handle CRModelDeprecatedError and pick a successor on the
spot:
import { CRModelDeprecatedError } from '@cleverrouter/sdk';
try {
await cr.chat({ model, messages });
} catch (err) {
if (err instanceof CRModelDeprecatedError) {
// model id was retired upstream. Pick something else.
} else {
throw err;
}
}Sync interval
6 hours is the default. Shorter intervals increase load on provider APIs without product benefit — model catalogs change on the order of days, not minutes.