kveton adds encrypted BYOK storage for Anthropic and Gemini provider keys

CaseMark's mikeoss-casedotdev fork now lets users save their own Anthropic or Gemini keys alongside (or instead of) the Case.dev model gateway. Keys are encrypted at rest using the same helpers already in place for Case credentials, validated against a live `/v1/models` call on save, and stored with verification metadata. The model catalog then reads from whatever credentials are configured to decide which providers are selectable.

integrationsecurity

The core new file is backend/src/lib/providerCredentials.ts (385 lines). It handles the full credential lifecycle: encryption using encryptCaseApiKey/decryptCaseApiKey, a live model-list validation call on save, a provider_api_credentials Postgres table (migration 009), and status tracking with last4, verified_at, last_checked_at, and a capabilities jsonb blob ({ llm, model_count }).

Removing a key soft-deletes the row rather than deleting it - the review pass (2032ca81) changed clearProviderApiKey from a .delete() to an .update() that zeroes the key material, sets status: "invalid", and stamps revoked_at. This preserves the audit row without leaving a decryptable key behind.

The server fallback (honoring ANTHROPIC_API_KEY/GEMINI_API_KEY from .env) defaults to false in production. The initial commit had this enabled by default, but the review pass added a NODE_ENV === "production" hard block regardless of the env var, so staging and dev still work from .env while production requires a user-saved key.

The Account > Models page was reorganized into four sections (Case.dev, Model Providers, Model Defaults, Storage). Provider availability checks that were duplicated across ChatInput, TRChatPanel, TabularReviewView, and useSelectedModel got consolidated into a modelAvailability.ts helper.

So what Worth a look if you want users to bring their own Anthropic or Gemini keys without your service holding them in plaintext. The encryption helper reuse, the live `/v1/models` validation pattern, and the migration shape are all reasonably self-contained. Check `serverProviderFallbackAllowed()` before copying: in production it unconditionally returns false, which is the right posture, but you'll need to understand the fallback logic if your local dev setup differs from theirs.

View this fork on GitHub →

Spotted something wrong? Or know the PR text has fresher detail than the writeup above?

Commits in this thread

4 commits from CaseMark/mikeoss-casedotdev, oldest first. Source extracted verbatim from the harvested git log.

SHA Subject Author Date
c0ed073a Add BYOK model provider settings kveton 2026-05-04 ↗ GitHub
2032ca81 Address BYOK review feedback kveton 2026-05-04 ↗ GitHub
b3098e8b Tighten BYOK review fixes kveton 2026-05-04 ↗ GitHub
54f5b8b8 Polish BYOK provider feedback kveton 2026-05-04 ↗ GitHub

Capture this thread into my fork

Download a single Markdown prompt that tells Claude how to port every commit above into your working tree — adapting paths and structure to match your repo. Run it via claude -p < capture-thread-288.md from inside the repo you want the changes in.

⬇ Download capture-thread-288.md