Skip to content

feat(byok): support multiple keys per provider with round-robin rotation#4963

Merged
waleedlatif1 merged 4 commits into
stagingfrom
feat/byok-multiple-keys
Jun 11, 2026
Merged

feat(byok): support multiple keys per provider with round-robin rotation#4963
waleedlatif1 merged 4 commits into
stagingfrom
feat/byok-multiple-keys

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

  • Workspaces can now store multiple BYOK keys per provider (capped at 10); requests round-robin across the pool in creation order
  • Dropped the (workspace_id, provider_id) unique index, added a plain composite lookup index and a nullable name label column — existing rows need no backfill
  • Rotation lives entirely inside getBYOKKey (in-process cursor, decrypt-failure skips to the next key), so all call sites are untouched
  • POST/DELETE on /api/workspaces/[id]/byok-keys take an optional keyId: with it they update/delete that key, without it they keep the old add / delete-all-for-provider semantics
  • BYOK settings UI gains a per-provider Manage modal (key list, optional labels, per-key update/delete, add up to the cap); the mothership tab keeps the single-key mode unchanged

Type of Change

  • New feature

Testing

Added unit tests for rotation (cycle order, per-provider cursors, decrypt fallback) and route semantics (add vs update-by-keyId, cap, delete one/all, authz) — 24 tests passing. Typecheck and check:api-validation pass.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel

vercel Bot commented Jun 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jun 11, 2026 4:58pm

Request Review

@gitguardian

gitguardian Bot commented Jun 11, 2026

Copy link
Copy Markdown

️✅ There are no secrets present in this pull request anymore.

If these secrets were true positive and are still valid, we highly recommend you to revoke them.
While these secrets were previously flagged, we no longer have a reference to the
specific commits where they were detected. Once a secret has been leaked into a git
repository, you should consider it compromised, even if it was deleted immediately.
Find here more information about risks.


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@cursor

cursor Bot commented Jun 11, 2026

Copy link
Copy Markdown

PR Summary

High Risk
Touches encrypted credential storage, key selection for all provider requests, and a breaking DB uniqueness change; rotation is per-process so load spread varies across instances.

Overview
Workspaces can store up to 10 BYOK keys per provider (with optional labels) instead of one. A migration drops the (workspace_id, provider_id) unique constraint, adds a nullable name column, and adds a composite lookup index.

Runtime: getBYOKKey loads all keys for a provider in creation order and round-robins via an in-process cursor; decrypt failures skip to the next key. Existing call sites stay unchanged.

API: GET returns every key with name; POST accepts optional keyId (update in place) or adds a new key under a transactional cap with advisory locking; DELETE accepts optional keyId (one key vs all for the provider).

UI: Workspace BYOK settings use multi-key mode—per-provider Manage modal, named keys, per-key update/delete—while the enterprise mothership tab keeps single-key behavior on the shared BYOKKeyManager.

Unit tests cover route semantics and rotation behavior.

Reviewed by Cursor Bugbot for commit 3f6e3e7. Configure here.

Comment thread apps/sim/app/api/workspaces/[id]/byok-keys/route.ts Outdated
@greptile-apps

greptile-apps Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR upgrades workspace BYOK from one key per provider to a pool of up to 10 keys per provider, with round-robin rotation handled entirely in-process inside getBYOKKey. The previous (workspace_id, provider_id) unique index is dropped and replaced by an advisory-lock-serialized cap check inside a DB transaction, so concurrent adds can't bypass the limit.

  • Rotation layer (byok.ts): a module-level Map tracks a per-workspaceId:providerId cursor; decrypt failures skip to the next key in the pool without consuming an extra rotation slot.
  • Route changes (route.ts): POST now branches on keyId — update-in-place vs. create; create runs inside db.transaction with pg_advisory_xact_lock matching the workspace-env pattern; DELETE accepts an optional keyId to target a single key.
  • UI (byok-key-manager.tsx, byok-provider-keys-modal.tsx): BYOKKeyManager gains a multiKey discriminated-union prop; configured providers show a "Manage" button that opens a per-provider key list modal.
  • Migration: drops the unique index, adds nullable name column, and replaces the dropped onConflictDoNothing target with an application-level pre-check.

Confidence Score: 5/5

Safe to merge — cap enforcement, rotation logic, and all three route verbs are covered by 24 unit tests, the advisory-lock pattern mirrors existing workspace-env code, and the migration requires no backfill.

All changed paths are well-tested: rotation cycle, per-provider cursor independence, decrypt-failure fallback, and route semantics are each exercised. The concurrent-insert race addressed in previous review iterations is correctly closed with the transaction + advisory lock. The migration correctly replaces the dropped onConflictDoNothing target with an application-level idempotency guard. No unguarded cross-workspace data access or missing validation was found.

No files require special attention.

Important Files Changed

Filename Overview
apps/sim/lib/api-key/byok.ts Adds in-process round-robin rotation across a pool of keys; decrypt failures skip to the next key. The module-level counter is intentionally per-process and does not require DB writes.
apps/sim/app/api/workspaces/[id]/byok-keys/route.ts POST splits into update-by-keyId and create paths; create uses an advisory lock inside a transaction to enforce the 10-key cap atomically. DELETE supports per-key or all-provider deletion with correct 404 handling.
packages/db/migrations/0232_byok_multiple_keys.sql Drops the (workspace_id, provider_id) unique index, adds the nullable name column, and creates a plain composite lookup index. Existing rows need no backfill.
apps/sim/app/workspace/[workspaceId]/settings/components/byok/byok-key-manager.tsx Splits props into single-key and multi-key discriminated union, adds per-provider Manage modal flow and name input field. Delete guard for missing keyId in multi-key mode now logs and early-returns correctly.
apps/sim/app/workspace/[workspaceId]/settings/components/byok/byok-provider-keys-modal.tsx New modal listing all stored keys for a provider with per-key Update/Delete actions and an Add Key footer that disables once the cap is reached.
packages/db/scripts/migrate-block-api-keys-to-byok.ts Removes the dropped onConflictDoNothing target and replaces it with an application-level pre-check using existingBYOKProviders; existing-row query now runs unconditionally since the unique constraint is gone.

Reviews (3): Last reviewed commit: "fix(byok): drop ON CONFLICT on removed u..." | Re-trigger Greptile

Comment thread apps/sim/app/api/workspaces/[id]/byok-keys/route.ts
Comment thread apps/sim/app/api/workspaces/[id]/byok-keys/route.ts Outdated
@waleedlatif1 waleedlatif1 force-pushed the feat/byok-multiple-keys branch from 476d1d9 to a27cc9c Compare June 11, 2026 07:11
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit a27cc9c. Configure here.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 3f6e3e7. Configure here.

@waleedlatif1 waleedlatif1 merged commit 9efb7b4 into staging Jun 11, 2026
9 checks passed
@waleedlatif1 waleedlatif1 deleted the feat/byok-multiple-keys branch June 11, 2026 17:01
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