Skip to content

feat(settings): power Loki with your own model — paste a key, get your best model - #921

Merged
catomean merged 7 commits into
mainfrom
feat/bring-your-own-model
Sep 25, 2026
Merged

catomean merged 7 commits into
mainfrom
feat/bring-your-own-model

Conversation

@catomean

Copy link
Copy Markdown
Collaborator

Anyone can now power Loki with the best model they have access to. Settings → AI → "Power Loki with your own model": pick a provider, open its key page from the link, paste, and the key is checked on the spot. If it works, the models it can actually use appear with the strongest one already chosen. One click saves. From then on your Loki chat runs on it.

Built on the shared @bitbaum/ai-kit 1.17.0 (probeByokKey, released for this, ai-kit#76), so every other app gets the same check with a version bump.

What a person sees

  • Ten providers from ai-kit's closed list (OpenRouter, OpenAI, Anthropic, Google Gemini, Groq, Mistral, DeepSeek, xAI, Together, Cerebras), each with a direct "Get a key" link.
  • No "Check" button to find: a pasted key is checked as soon as you stop typing.
  • Failures in the vendor's own words, key masked: *OpenAI didn't accept this key. OpenAI says: "Incorrect API key provided: sk-this-***0000 …". And "couldn't reach them" is never presented as "your key is wrong".
  • Your models, best first: the list is the vendor's own list for your key; the preselected one is the newest in the strongest tier. No model names are hardcoded.
  • Connected state: Loki is thinking with Groq · openai/gpt-oss-120b — your key …pJXr, with Change model (no re-paste), Replace key, Disconnect.
  • At the moment it's needed: when the free daily budget runs out, the refusal now ends with "Or connect your own model with a key from any provider you use, and Loki's daily budget no longer applies to your chats."

What happens on the server

  • The key is sealed (ai-kit/seal, AES-256-GCM, new BYOK_SEAL_SECRET) in user_model_keys (migration 0081, hand-written like 0073–0080 because drizzle's snapshot stops at 0072). No API ever returns it. Saving re-checks it server-side rather than trusting the client.
  • A chat turn on your model runs only your chain:
    • it skips Loki's free-ration gate and spend ledger;
    • it writes nothing to Loki's vendor quota, refusal, usage or AI-health records;
    • if your key fails, you get a sentence naming your vendor and what it said, with a link to fix it — no quiet fallback to the shared pool, which would hide that your key stopped working.
  • Key isolation, by construction (keyForLink): a user's link reads its key only from the per-call env ai-kit returns, and a server link reads only process.env. So a server that happened to set BYOK_API_KEY can't answer users on the operator's account, and a user's key can't ride along to a vendor they didn't choose.
  • /api/settings is denied in the shared demo account (the new sandbox guard caught it): a key saved there would power every visitor on someone's bill.
  • These are API keys only. A Claude or ChatGPT subscription is not stored here; agents sign in through their own login flow.

Also fixed

Settings deep links threw a hydration mismatch. The server rendered Profile and the browser rendered the hash's tab, on every /settings#… link — the new budget-refusal link would have hit it constantly. The tab is now read through useSyncExternalStore with a server snapshot. Verified: a hard reload of /settings#ai opens AI with zero console errors.

Verified, not assumed

  • Real UI, signed in, on a throwaway local copy (fresh Postgres, full schema, one test user):
    • the failure path against the real OpenAI API;
    • the working path against real Groq: 4 models, gpt-oss-120b preselected;
    • a chat turn ran on it: provenance your key: Groq · openai/gpt-oss-120b;
    • afterwards ai_spend, ai_usage and provider_quota all had 0 rows.
  • scripts/test/own-model.ts drives the real walker with a faked fetch: one request, to the user's vendor, with their key and model, and never a server key. Mutation-checked: letting a user link fall back to process.env, and making the walker ignore the user's model, each fail it.
  • pnpm verify green · pnpm run build green · client bundle stays Node-free.

After merge: nothing to do on the box — BYOK_SEAL_SECRET is already set there (generated on the box, never printed).

🤖 Generated with Claude Code

https://claude.ai/code/session_016YJVAanfj8keVSXyZnTemh

@catomean
catomean merged commit 20f4ceb into main Sep 25, 2026
3 checks passed
@catomean
catomean deleted the feat/bring-your-own-model branch September 25, 2026 16:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant