Skip to content

THU-505: Move model API keys to local-only table#858

Open
raivieiraadriano92 wants to merge 8 commits into
mainfrom
raivieiraadriano92/thu-505-models-secrets-local-only-table
Open

THU-505: Move model API keys to local-only table#858
raivieiraadriano92 wants to merge 8 commits into
mainfrom
raivieiraadriano92/thu-505-models-secrets-local-only-table

Conversation

@raivieiraadriano92
Copy link
Copy Markdown
Collaborator

@raivieiraadriano92 raivieiraadriano92 commented May 12, 2026

Summary

  • Moves api_key out of the synced models table into a new local-only models_secrets table that PowerSync never syncs — API keys never leave the device
  • All model DAL queries LEFT JOIN with models_secrets so consumers still get apiKey transparently
  • Adds warning indicator (amber triangle + tooltip) in model picker and settings list for models missing an API key
  • Adds edit/delete button group to model cards with an edit modal for updating model config and API keys
  • Removes api_key from E2EE encrypted columns and backend schema (with migration)

Test plan

  • Create a model with API key → key stored in models_secrets, not in models
  • Edit model API key via edit modal → models_secrets updated, warning disappears
  • Delete model → models_secrets row deleted
  • Default/system models load correctly with apiKey: null, no warning shown
  • Model picker shows warning icon + tooltip on models without API key, disables selection
  • AI chat works with API-key-based providers (OpenAI, Anthropic, etc.)
  • TypeScript compiles, lint passes, all tests pass

🤖 Generated with Claude Code


Note

Medium Risk
Changes database schema and model CRUD/query logic to split synced vs local-only data, which could affect model selection and provider connectivity if joins/migrations misbehave. UI now disables selection for models missing a required key, which may impact existing user flows.

Overview
API keys are removed from the synced models schema (backend migration + Drizzle schemas) and from the E2EE encrypted column list, and are instead stored in a new local-only models_secrets table that is excluded from PowerSync syncing.

Model DAL queries (getAllModels, getAvailableModels, getModel, getSelectedModelQuery, getSystemModel) now LEFT JOIN models_secrets to continue exposing Model.apiKey transparently, and model mutations (createModel, updateModel, resetModelToDefault, deleteModel) manage secret rows transactionally (including delete-on-model-delete).

The UI surfaces missing-key configuration: the model selector and settings list show an amber warning + tooltip for models without a required key and disable selecting such models; settings models page also replaces the prior detail/new routes with an in-page edit modal and centralized delete confirmation. Tests are updated/added to validate secret storage/join behavior.

Reviewed by Cursor Bugbot for commit 9d366d0. Bugbot is set up for automated code reviews on this repo. Configure here.

@raivieiraadriano92 raivieiraadriano92 self-assigned this May 12, 2026
@raivieiraadriano92 raivieiraadriano92 changed the title Raivieiraadriano92/thu 505 models secrets local only table THU-505: Move model API keys to local-only table May 12, 2026
@github-actions
Copy link
Copy Markdown

Semgrep Security Scan

No security issues found.

@raivieiraadriano92 raivieiraadriano92 marked this pull request as ready for review May 12, 2026 12:36
Comment thread src/settings/models/index.tsx
Comment thread src/dal/models.ts
Comment thread src/dal/models.ts Outdated
Comment thread src/settings/models/index.tsx Outdated
Comment thread src/settings/models/layout.tsx Outdated
@github-actions
Copy link
Copy Markdown

Preview environment deployed 🚀

Service URL
Marketing / blog / docs https://thunderbolt-pr-858.preview.thunderbolt.io
App https://app-pr-858.preview.thunderbolt.io
API https://api-pr-858.preview.thunderbolt.io
Keycloak https://auth-pr-858.preview.thunderbolt.io
PowerSync https://powersync-pr-858.preview.thunderbolt.io

Stack: preview-pr-858 · Commit: 9d366d031feec6b0594f98a764f627fa5e53e2bb

Auto-destroys on PR close/merge. Login via the bundled Keycloak realm — demo@thunderbolt.io / demo by default.

@github-actions
Copy link
Copy Markdown

PR Metrics

Metric Value
Lines changed (prod code) +369 / -645
JS bundle size (gzipped) 🟢 1.02 MB → 1.02 MB (-3.7 KB, -0.4%)
Test coverage 🟡 71.47% → 71.28% (-0.2%)
Performance (preview) Preview not ready — Render deploy may have timed out
Accessibility
Best Practices
SEO

Updated Thu, 14 May 2026 11:17:54 GMT · run #1493

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 9d366d0. Configure here.

</Tooltip>
</TooltipProvider>
)}
{model.provider !== 'thunderbolt' && !model.apiKey && (
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Duplicated needsApiKey logic across two files

Low Severity

The needsApiKey logic (model.provider !== 'thunderbolt' && !model.apiKey) is defined as a named helper in model-selector.tsx but duplicated inline in index.tsx at line 1149. If the condition for determining whether a model needs an API key changes in the future (e.g., excluding custom providers that run local LLMs), both locations must be updated in sync, risking inconsistency.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9d366d0. Configure here.

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.

2 participants