Repository navigation
feat: BYOK provider-key storage for organizations - #78
Merged
Merged
Conversation
An organization can now store its own encrypted Claude/OpenAI key
(PUT/GET/DELETE /orgs/{org_id}/provider-keys), gated on manage_org --
an org-wide infrastructure setting, not a self-service literature-API
action. Built on the organization_config table, which has existed since
the multi-tenant schema migration with no service or route ever using
it. One provider/key slot per organization, mirroring GlobalConfig's
own platform-wide shape; setting a new provider replaces the previous
one. Credentials are write-only (has_key flags only, never echoed back)
and refused with 500 rather than stored in plaintext when
CONFIG_ENCRYPTION_KEY isn't configured, reusing the same crypto module
config_service.py already established.
Deliberately scoped to storage only: actually routing a
/v1/literature/answers call through the stored provider (token
accounting, llm.tokens.* events), and exposing this through the public
gateway's own /v1/provider-keys/{provider} path, are follow-up work --
this is reachable today the same way Studio's own LLM settings page
would be, through the org-admin endpoint directly.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
First slice of design-audit gap #4 ("No organisation-scoped BYOK provider-key service exists"). Model routing through the stored key (Claude/OpenAI client calls, token accounting,
llm.tokens.*events) and exposing this through the public gateway's own/v1/provider-keys/{provider}path are explicitly follow-up work -- this PR is storage only.PUT/GET/DELETE /orgs/{org_id}/provider-keys(/{provider}), gated onmanage_org-- an org-wide infrastructure setting, the same administrative charactermanage_orgalready gates elsewhere (GET/PUT /orgs/{org_id}itself), not a self-service literature-API action.organization_config, a table that has existed since the multi-tenant schema migration with no service or route ever reading or writing it -- this is its first real consumer, no migration needed.GlobalConfig's own platform-wide shape inconfig_service.py) -- setting a new provider replaces whichever one was previously configured.has_key, never the key;crypto.encrypt()(the same moduleconfig_service.pyalready uses) raises a loud 500 rather than silently storing anything in plaintext whenCONFIG_ENCRYPTION_KEYisn't configured./v1surface.Test plan
pytest tests/test_organization_provider_keys.py tests/test_route_authorization_coverage.py tests/test_config.py tests/test_apikeys.py tests/test_me_api_keys.py-- 59 passed, including permission gating, provider-name validation, the no-plaintext-fallback guarantee, a real encrypt/decrypt round-trip, single-slot replacement, and the updated org-scoped-route-count tripwire (44 -> 47 for the three new routes)🤖 Generated with Claude Code