Skip to content

feat: internal reveal endpoint for BYOK provider keys - #80

Merged
man4ish merged 1 commit into
mainfrom
feature/m16-provider-key-reveal-endpoint
Oct 4, 2026
Merged

man4ish merged 1 commit into
mainfrom
feature/m16-provider-key-reveal-endpoint

Conversation

@man4ish

@man4ish man4ish commented Oct 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

Part of M16 (design audit gap #4: closing the full gap in one coordinated milestone -- paired PRs in omnibioai-rag and omnibioai-api-gateway do the actual Claude/OpenAI routing and token metering).

  • POST /internal/organizations/{org_id}/provider-keys/{provider}/reveal decrypts a stored BYOK key for one outbound provider API call -- shared-secret gated (PROVIDER_KEY_REVEAL_SECRET), the same service-to-service shape POST /auth/api-keys/exchange already established for the gateway/API-key flow.
  • Deliberately no org-membership/manage_org dependency on this endpoint: the question it answers is "does this organization have a key for this provider," not "may this particular caller manage it" -- any authenticated member can trigger a BYOK-routed call using the org's own already-configured key, the same way any member can spend the org's quota today. manage_org still gates setting/clearing the key (unchanged from M14).
  • Deliberately not audit-logged on each reveal -- the same per-use (not per-lifecycle-change) frequency exchange_api_key already leaves unaudited, for the identical reason.
  • Called by omnibioai-api-gateway immediately before forwarding a BYOK-routed /v1/literature/answers call to omnibioai-rag; never persisted or logged by that caller.

Security note worth a careful look: this is the first endpoint in this service that ever returns a decrypted credential in plaintext. Gated the same way the existing exchange endpoint is (shared secret, fail-closed when unset), but flagging explicitly since it's a new class of exposure, not an incremental extension of an existing one.

Test plan

  • pytest tests/test_organization_provider_keys.py -- 22 passed (14 existing + 8 new: disabled-without-secret, wrong/missing secret, correct decrypt round-trip, no-membership-required, 404 for unconfigured/mismatched provider, unsupported provider name, and a loud 500 rather than a silent failure when encryption isn't configured)
  • pytest tests/test_route_authorization_coverage.py -- confirms the new route correctly sits outside the org-scoped-route tripwire (path doesn't start with /orgs, by design)
  • Full repo suite -- 1496 passed, 2 skipped (the MFA concurrency-timing test flagged as flaky in the last two milestones passed clean this run)

🤖 Generated with Claude Code

POST /internal/organizations/{org_id}/provider-keys/{provider}/reveal
decrypts a stored BYOK key for one outbound provider API call --
shared-secret gated (PROVIDER_KEY_REVEAL_SECRET), the same
service-to-service shape POST /auth/api-keys/exchange already
established. Deliberately no org-membership check: the question this
answers is "does this organization have a key for this provider", not
"may this caller manage it" (manage_org still gates setting/clearing
the key). Called by omnibioai-api-gateway immediately before it
forwards a BYOK-routed /v1/literature/answers call to omnibioai-rag;
never persisted or logged by that caller.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@man4ish
man4ish merged commit 9fab572 into main Oct 4, 2026
1 check passed
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