DEV Community

Lynkr
Lynkr

Posted on

How to run Claude Desktop and ChatGPT desktop app for free with llama.cpp and other local LLMs.

Most desktop AI apps still assume the backend is fixed.

Claude Desktop assumes Anthropic. ChatGPT.app assumes OpenAI.

That works until you want to route the same desktop app across local and cloud models, keep one gateway for caching and fallback, and pin stronger or cheaper models without changing frontend UX.

That is the problem we just solved in Lynkr.

Lynkr is an open-source LLM gateway. In the latest code changes, we added native support for two desktop surfaces that normally do not give you much backend control: Claude Desktop and ChatGPT.app (Codex Desktop).

Claude Desktop: using the hidden third-party gateway path

Claude Desktop does not expose a normal bring-your-own-provider flow. The new Lynkr support works by using Claude Desktop's undocumented third-party gateway mode — the same general mechanism Ollama's Claude Desktop launcher relies on.

The new install flow is:

lynkr desktop-token <oauth-token>
Enter fullscreen mode Exit fullscreen mode

Under the hood, Lynkr edits config in ~/Library/Application Support/ and manages:

  • Claude/claude_desktop_config.json
  • Claude-3p/claude_desktop_config.json
  • Claude-3p/configLibrary/_meta.json
  • Claude-3p/configLibrary/<uuid>.json

The installer also writes Claude-3p/configLibrary/.lynkr-backup.json, which means restore is not a blind delete. Lynkr saves the original deployment mode and applied profile, then restores them exactly later.

lynkr desktop-token --restore
Enter fullscreen mode Exit fullscreen mode

That restore command removes the Lynkr profile, resets deployment bookkeeping, and puts Claude Desktop back into its previous mode.

Why Claude Desktop needs a token

Once Desktop is switched into third-party gateway mode (deploymentMode: "3p"), it is no longer talking directly to Anthropic. Lynkr becomes the API layer receiving Desktop traffic.

That is why the install command expects a Claude OAuth token. The current implementation explicitly validates that the token starts with sk-ant-oat.

Lynkr does not mint or refresh that token. It installs the token into Claude Desktop's gateway profile so Desktop can send it to Lynkr, and Lynkr can forward it when a request routes to an Anthropic-backed tier.

Claude Desktop's model picker becomes a tier selector

Instead of treating the picker as cosmetic, Lynkr uses it as a routing control surface.

The docs describe fixed picker entries that map to Lynkr tiers:

  • Auto -> no pin, normal content-based scoring
  • claude-opus-5 -> REASONING
  • claude-sonnet-5 -> COMPLEX
  • claude-sonnet-4-6 -> MEDIUM
  • claude-haiku-4-5-20251001 -> SIMPLE

Because Claude Desktop validates model ids against its own catalog, Lynkr cannot advertise made-up ids like lynkr-medium. Instead, it reuses real Claude-family ids and treats them as tier pins.

So the contract becomes:

  • Auto = gateway decides
  • specific model = user decides

When the user picks anything other than Auto, Lynkr bypasses classifier-based routing for that request and pins the tier directly.

Claude Desktop detection is gated, not global

Lynkr does not force this behavior onto every Anthropic-compatible caller. The Claude Desktop gateway logic only intercepts callers that look like Desktop specifically — for example requests carrying an anthropic-version header or an explicit ?format=anthropic path.

Other Anthropic-format clients still fall through to the normal model list behavior.

ChatGPT.app: one backend for Desktop and CLI

ChatGPT.app required a different strategy.

The key detail is that Codex Desktop and Codex CLI share the same config surface:

~/.codex/config.toml
Enter fullscreen mode Exit fullscreen mode

That means Lynkr can be added once as a provider block and then back both interfaces with the same gateway configuration.

ChatGPT.app exposes a real routing signal: reasoning.effort

The most interesting part of the ChatGPT.app work is that the model picker sends structured routing hints on the wire.

The latest code adds a dedicated mapping module in src/routing/openai-model-slots.js.

That file documents the behavior Lynkr now relies on:

  • Codex sends a real OpenAI model id
  • it also sends reasoning.effort
  • Lynkr maps that pair into a routing tier

The current effort-to-tier mapping is:

  • minimal -> SIMPLE
  • low -> MEDIUM
  • medium -> COMPLEX
  • high -> REASONING

This was confirmed live from a captured request.

One verified path from the implementation:

  • UI label: Light
  • wire value: reasoning.effort = "low"
  • Lynkr routing tier: MEDIUM
  • classifier path: bypassed

If the app already tells the gateway how much reasoning the user wants, the gateway should respect it.

The pin is intentionally conservative

If Lynkr sees an unrecognized model or effort value, it falls back to normal content-based routing instead of guessing.

There is also an important scope note in the code: the model/effort pin only governs the first model call of a turn. If the turn becomes multi-step — tool calls, follow-up steps, more agent iterations — later steps can still be rescored by content.

Why these integrations matter

The reason these integrations matter is not just that Lynkr now supports two more apps.

It is that both apps normally own the backend relationship:

  • Claude Desktop assumes Anthropic
  • ChatGPT.app assumes OpenAI

Once both route through Lynkr, the control plane moves out of the app and into the gateway:

Claude Desktop / ChatGPT.app
-> Lynkr
-> local + cloud models
-> tier routing + token optimization + fallback
Enter fullscreen mode Exit fullscreen mode

At that point:

  • the app owns the UX
  • Lynkr owns routing logic
  • providers become interchangeable

That is the real story in these latest Lynkr changes.

Top comments (0)