Codumentor logo Codumentor

Phase 3 — BYO API Key

Goal

Let each user supply their own API key per backend, stored in EncryptedKVStore via the user_secrets plugin. Add the settings UI that lets them paste a key, test it, and clear it. Resolution priority at call time: per-user key → instance technical-user key → error.

Preconditions

Phases 1 and 2 shipped. The user_secrets plugin exists in the codebase (see src/codumentor/plugins/user_secrets/plugin.py and src/codumentor/core/secrets/store.py).

Deliverables

  1. auth/byo_key.py resolving per-user keys from EncryptedKVStore.
  2. plugin.yaml updated to declare dependencies: [user_secrets].
  3. Resolution logic in tools/base.py (or wherever phase 1 placed credential resolution) updated to follow the per-user → technical-user → error priority.
  4. x-external-agent-settings web component, placed under placements: ["sidebar-panel"] or wherever existing plugin settings render (verify against PluginSettingsPanel.tsx and the WIDGET_REGISTRY).
  5. FastAPI routes for: list configured backends, set per-user key for a backend, clear per-user key, run an auth probe for a backend. Decide whether to reuse user_secrets' existing CRUD routes or add plugin-scoped routes. Report the decision and rationale in the handoff.
  6. UI surface in the settings panel: one row per backend showing "key set / not set," a "test" button, a paste-key dialog, a clear button.
  7. Tests: unit tests for the resolution priority, an E2E test for the settings panel basic flow.

New files

src/codumentor/plugins/external_agent/auth/byo_key.py
ui/plugins/external_agent/settings.tsx
tests/test_external_agent_auth_byo_key.py
ui/plugins/external_agent/e2e/byo_key.spec.ts  (or ui/e2e/... per repo convention)

Touched:

Implementation steps

  1. Decide CRUD route shape first. Read src/codumentor/plugins/user_secrets/plugin.py. If its routes can be reused with a naming convention like external_agent/<backend>/api_key, prefer that — less surface to maintain. If reuse forces awkward UX (e.g., users seeing raw secret names in a generic UI), expose plugin-scoped routes. Document the decision in the handoff.
  1. auth/byo_key.py: a resolve(user_id, backend) -> Credentials | None function that hits EncryptedKVStore for external_agent/<backend>/api_key. Return None if no per-user key set.
  1. Resolution priority: in the existing credential resolution path (added in phase 1), call byo_key.resolve first; if None, fall back to technical_user.resolve; if that is also None, raise an error that produces a plugin.external_agent.error event with kind auth_required payload (this is a new optional payload field, not a new event kind — error already exists).
  1. Settings UI (settings.tsx): list each backend configured in codumentor.yaml. For each, show the current state ("system key in use" / "your key set" / "not configured"), with controls to set/clear/test. The test button calls the existing auth/probe route with the per-user credentials applied.
  1. Tests: - Unit: priority order, missing key returns None, key written and read back round-trip. - E2E: navigate to settings, paste a key, click test, see success; clear, see system key state.

Acceptance criteria

Out of scope

Handoff

Fill out the template. Be specific in "Contracts established/changed" about: