Skip to content

feat(providers): configure provider credentials from the dashboard - #566

Merged
SantiagoDePolonia merged 3 commits into
mainfrom
feat/providers
Jul 21, 2026
Merged

feat(providers): configure provider credentials from the dashboard#566
SantiagoDePolonia merged 3 commits into
mainfrom
feat/providers

Conversation

@SantiagoDePolonia

@SantiagoDePolonia SantiagoDePolonia commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Lets GoModel run and be fully configured without any provider API-key env vars. Providers declared via env vars/config.yaml keep working exactly as before and stay read-only in the dashboard; a new "Providers" page lets you add, edit, disable, and delete dashboard-managed provider credentials, which apply to the running gateway immediately — no restart.

  • New provider_credentials admin store (SQLite/PostgreSQL/MongoDB), mirroring the existing MCP-servers declarative-shadows-store precedence: a provider name declared via env/config.yaml is always read-only (shown with a "Config" badge), and a dashboard-managed row hot-registers/unregisters into the live registry.
  • ModelRegistry.UnregisterProvider (new — didn't exist before) makes edit/disable/delete work live. providers.Init and the registry's refresh path no longer treat "zero providers configured" as a startup failure — that's the whole point of the feature.
  • Admin API: GET/PUT /admin/provider-credentials, GET /admin/provider-credentials/types, DELETE /admin/provider-credentials/:name. API keys and service-account secrets are redacted as *** (positional for the key-rotation list); an upsert echoing *** back preserves the stored value at that position.
  • Dashboard: new "Providers" page — Type-first form with name auto-suggestion ({type}, then {type}-1, {type}-2, ...), repeatable API-key rows, and less-common fields (Base URL, Models, Vertex/service-account, ...) tucked behind an "Advanced settings" disclosure. Standardized icon-button and toggle-switch shapes (rounded-square instead of circular) and disabled-field styling app-wide along the way.
  • Docs: CLAUDE.md, docs/providers/overview.mdx, .env.template, README.md, regenerated Swagger/OpenAPI.

Three real bugs were found and fixed via actually running the built binary end-to-end (not just unit tests with mocks): providers.Init hard-failing on zero providers, the registry's Refresh erroring on zero remaining providers, and Upsert/Delete conflating a provider's own network/credential failure with a save failure (a wrong API key would return a scary error instead of just showing "Unhealthy").

Test plan

  • go build ./... and go vet ./...
  • go test ./internal/... (full suite, including new registry/credentials/admin tests)
  • make test-dashboard (499 JS unit tests)
  • make lint / pre-commit hooks all green
  • Live end-to-end smoke test: built cmd/gomodel, ran with zero provider env vars (boots successfully), exercised create/update/delete against /admin/provider-credentials, verified "***" redaction round-trips correctly, verified env-only declared providers (e.g. OLLAMA_BASE_URL with no config.yaml entry) are correctly protected as read-only
  • Verified dashboard page renders at /admin/dashboard/providers-config

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added an admin dashboard page and REST API to manage provider credentials (create/edit, enable/disable, delete) with immediate routing updates.
    • Providers can now be configured after startup, and the gateway can boot with no provider API keys set.
    • Added Anthropic Messages batch endpoints (including JSONL results).
    • Updated MCP documentation and naming to use slug-based conventions.
  • Documentation
    • Updated guidance on provider credential precedence, managed/read-only behavior, and secret redaction.
    • Documented LOGGING_RETENTION_DAYS and stop_sequence.
  • Bug Fixes
    • Improved startup behavior when no providers are configured.

Lets GoModel run and be fully configured with zero provider API-key env
vars: a new admin-managed provider-credentials store (SQLite/Postgres/
Mongo) plus a "Providers" dashboard page for CRUD, mirroring the existing
MCP-servers declarative-shadows-store precedence (env/config.yaml-declared
providers stay read-only; dashboard-managed ones hot-register into the
live registry immediately, no restart).

- internal/providers: new CredentialsService/CredentialStore, and
  ModelRegistry.UnregisterProvider (previously missing) so add/edit/
  disable/delete apply live. providers.Init and the registry's refresh
  path no longer treat zero configured providers as a startup failure.
- internal/admin: /admin/provider-credentials GET/PUT/DELETE and
  /admin/provider-credentials/types, with positional "***" redaction for
  API keys and service-account secrets. The list merges in declarative
  providers (read-only, secrets never loaded) and hides any store row a
  declared name now shadows. Shared the managed-resource delete flow with
  MCP servers to avoid duplicating the same handler shape.
- Dashboard: new "Providers" page (Type-first form with name
  auto-suggestion, API key rotation rows, collapsed advanced settings for
  less-common fields), standardized icon-button/toggle-switch shapes and
  disabled-field styling app-wide along the way.
- Docs: CLAUDE.md, docs/providers/overview.mdx, .env.template, README,
  and regenerated Swagger/OpenAPI.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@mintlify

mintlify Bot commented Jul 20, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
gomodel 🟢 Ready View Preview Jul 20, 2026, 6:05 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 83d3499a-edd7-4be8-8701-9bc909218e1c

📥 Commits

Reviewing files that changed from the base of the PR and between caa1abb and 137344a.

📒 Files selected for processing (1)
  • internal/admin/dashboard/static/css/dashboard.css

📝 Walkthrough

Walkthrough

GoModel adds persisted provider credentials with runtime registration, admin API endpoints, a dashboard Providers page, multiple storage backends, and documentation for startup without provider keys and related API schema changes.

Changes

Provider credential management

Layer / File(s) Summary
Runtime service and lifecycle
internal/providers/*, internal/app/app.go
Adds credential reconciliation, persistence wiring, live provider registration, disabling and deletion behavior, registry cleanup, empty-provider startup, and lifecycle shutdown handling.
Credential storage backends
internal/providers/credentials_store_*.go, internal/providers/credentials_store.go
Adds SQLite, PostgreSQL, and MongoDB CRUD stores with shared encoding, timestamps, ordering, and not-found handling.
Admin API
internal/admin/handler_provider_credentials.go, internal/admin/routes.go, internal/admin/handler_managed_resource.go
Adds credential listing, type discovery, upsert, delete, managed-provider protection, secret redaction, placeholder preservation, and handler coverage.
Dashboard Providers page
internal/admin/dashboard/static/js/modules/providers-config.js, internal/admin/dashboard/templates/page-providers-config.html, internal/admin/dashboard/static/js/dashboard.js
Adds routing, credential editing, filtering, validation, redacted-key handling, typed deletion confirmation, and runtime API calls.
Dashboard presentation
internal/admin/dashboard/static/css/dashboard.css, internal/admin/dashboard/templates/*, internal/admin/dashboard/static/js/modules/*test.cjs
Wires the page and script into the dashboard, updates control styling, and tests dashboard behavior.
Documentation and API schemas
README.md, docs/providers/overview.mdx, docs/openapi.json, cmd/gomodel/docs/docs.go, .env.template, CLAUDE.md
Documents dashboard provider configuration and updates MCP, Anthropic batch, dashboard configuration, provider credential, and response schemas.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AdminDashboard as Admin dashboard
  participant ProviderCredentialAPI as Provider credential API
  participant CredentialsService
  participant CredentialStore
  participant ModelRegistry

  AdminDashboard->>ProviderCredentialAPI: PUT provider credential
  ProviderCredentialAPI->>CredentialsService: Upsert credential
  CredentialsService->>CredentialStore: Persist credential
  CredentialsService->>ModelRegistry: Register or unregister provider
  ProviderCredentialAPI-->>AdminDashboard: Redacted credential view
Loading

Possibly related PRs

Suggested labels: release:feature

Poem

A rabbit stores keys out of sight,
Providers bloom without restart tonight.
Shadows guard configured names,
The dashboard tends the routing lanes.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.94% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: dashboard-managed provider credentials.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/providers

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov-commenter

codecov-commenter commented Jul 20, 2026

Copy link
Copy Markdown

@greptile-apps

greptile-apps Bot commented Jul 20, 2026

Copy link
Copy Markdown

Confidence Score: 5/5

This PR appears safe to merge with low risk.

The provider credential flow is wired through storage, admin handlers, registry updates, and dashboard code with explicit secret redaction and config/env read-only handling. No verified functional or security issues were found in the reviewed changed paths.

No files require special attention.

T-Rex T-Rex Logs

What T-Rex did

  • The build proof confirms the gomodel target built successfully, with exit code 0.
  • The runtime proof confirms the server started on port 18081 with zero providers and the admin UI enabled.
  • The Providers page UI was rendered, as shown by the live-page screenshot.
  • The add-provider form interaction state was captured in the UI, as shown by the corresponding screenshot.

View all artifacts

T-Rex Ran code and verified through T-Rex

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Operator as Dashboard/Admin API
participant Handler as Admin Handler
participant Service as CredentialsService
participant Store as provider_credentials store
participant Factory as ProviderFactory
participant Registry as ModelRegistry

Operator->>Handler: PUT /admin/provider-credentials
Handler->>Service: Upsert(credential)
Service->>Store: Persist row
Service->>Registry: UnregisterProvider(name)
alt enabled credential
    Service->>Factory: Create(provider config)
    Factory-->>Service: Provider instance
    Service->>Registry: RegisterProviderWithNameAndType
end
Service->>Registry: Refresh model catalog
Handler-->>Operator: Redacted credential view

Operator->>Handler: "DELETE /admin/provider-credentials/{name}"
Handler->>Service: Delete(name)
Service->>Store: Delete row
Service->>Registry: UnregisterProvider(name)
Service->>Registry: Refresh model catalog
Handler-->>Operator: 204 No Content
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Operator as Dashboard/Admin API
participant Handler as Admin Handler
participant Service as CredentialsService
participant Store as provider_credentials store
participant Factory as ProviderFactory
participant Registry as ModelRegistry

Operator->>Handler: PUT /admin/provider-credentials
Handler->>Service: Upsert(credential)
Service->>Store: Persist row
Service->>Registry: UnregisterProvider(name)
alt enabled credential
    Service->>Factory: Create(provider config)
    Factory-->>Service: Provider instance
    Service->>Registry: RegisterProviderWithNameAndType
end
Service->>Registry: Refresh model catalog
Handler-->>Operator: Redacted credential view

Operator->>Handler: "DELETE /admin/provider-credentials/{name}"
Handler->>Service: Delete(name)
Service->>Store: Delete row
Service->>Registry: UnregisterProvider(name)
Service->>Registry: Refresh model catalog
Handler-->>Operator: 204 No Content
Loading

Reviews (1): Last reviewed commit: "feat(providers): configure provider cred..." | Re-trigger Greptile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/providers/credentials_wire.go`:
- Around line 80-95: Update newCredentialsResult so that when
NewCredentialsService returns an error after createCredentialStore succeeds, it
closes the live store before returning; preserve the existing error return and
successful result behavior.

In `@internal/providers/credentials.go`:
- Around line 201-214: Validate provider names in the credential validation flow
around IsManaged and the cred.Name assignment, rejecting `/` and any other
characters reserved as model-selector qualifier delimiters; apply the same
format validation in the admin handler’s UpsertProviderCredential path so both
entry points enforce the identical naming rule.
- Around line 216-287: The Upsert flow removes the existing provider before
validating the replacement, leaving failed edits offline and skipping Refresh.
Split register’s resolve/create work from registry mutation: validate and create
the replacement first, return the “saved but not applied” error without
unregistering or changing the live provider when that fails, then swap the
validated provider into the registry and refresh as before; update register or
introduce a clearly named helper while preserving its configuration behavior,
and add coverage for editing an already-registered credential into an
unresolvable state.
- Around line 96-103: Protect provider-credential mutations with the service’s
mutation lock: update UpsertProviderCredential and DeleteProviderCredential to
acquire h.mutationMu for the full mutation sequence, including store and
registry updates. Ensure the lock is always released while preserving existing
behavior and error handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7bca522d-3569-4d5d-9a16-0e82058c85bf

📥 Commits

Reviewing files that changed from the base of the PR and between 7b8ccdd and 0866d79.

📒 Files selected for processing (37)
  • .env.template
  • CLAUDE.md
  • README.md
  • cmd/gomodel/docs/docs.go
  • docs/openapi.json
  • docs/providers/overview.mdx
  • internal/admin/dashboard/static/css/dashboard.css
  • internal/admin/dashboard/static/js/dashboard.js
  • internal/admin/dashboard/static/js/modules/dashboard-layout.test.cjs
  • internal/admin/dashboard/static/js/modules/providers-config.js
  • internal/admin/dashboard/static/js/modules/providers-config.test.cjs
  • internal/admin/dashboard/templates/index.html
  • internal/admin/dashboard/templates/layout.html
  • internal/admin/dashboard/templates/page-providers-config.html
  • internal/admin/dashboard/templates/sidebar.html
  • internal/admin/handler.go
  • internal/admin/handler_managed_resource.go
  • internal/admin/handler_mcpservers.go
  • internal/admin/handler_provider_credentials.go
  • internal/admin/handler_provider_credentials_test.go
  • internal/admin/routes.go
  • internal/admin/routes_test.go
  • internal/app/app.go
  • internal/providers/credentials.go
  • internal/providers/credentials_store.go
  • internal/providers/credentials_store_mongodb.go
  • internal/providers/credentials_store_postgresql.go
  • internal/providers/credentials_store_sqlite.go
  • internal/providers/credentials_store_sqlite_test.go
  • internal/providers/credentials_test.go
  • internal/providers/credentials_wire.go
  • internal/providers/factory.go
  • internal/providers/init.go
  • internal/providers/init_test.go
  • internal/providers/registry.go
  • internal/providers/registry_init.go
  • internal/providers/registry_unregister_test.go

Comment thread internal/providers/credentials_wire.go
Comment thread internal/providers/credentials.go
Comment thread internal/providers/credentials.go
Comment thread internal/providers/credentials.go
Addresses CodeRabbit review feedback on PR #566:

- CredentialsService.Upsert unregistered the existing provider before
  validating the replacement, so an edit that failed to resolve (e.g.
  stripping the only API key) took a previously-working, actively-serving
  provider offline with no way back except another edit. Now the
  replacement is built and validated first (buildProvider), and only
  swapped into the registry (install) on success -- a bad edit leaves
  whatever was already registered untouched.
- Reject '/' in provider names: model selectors use "name/model" syntax,
  so a name containing '/' would be unreachable or ambiguous. Enforced at
  both the admin handler (clean 400) and the service layer.
- Serialize provider-credential mutations with the admin handler's
  existing mutationMu (already used by guardrails/tagging/workflows) so
  concurrent edits of the same name can't interleave between the
  redaction-merge read and the store write.
- Close the credential store if NewCredentialsService fails after the
  store was already created, avoiding a leaked connection on that error
  path.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@SantiagoDePolonia

Copy link
Copy Markdown
Contributor Author

Reviewed all bot comments critically (not just autofixed). Verified each CodeRabbit finding against the actual code before acting:

Fixed (all in caa1abb):

  • MajorCredentialsService.Upsert unregistered the live provider before validating the replacement. An edit that failed to resolve (e.g. an accidental empty API key on an already-working provider) took it fully offline with no way back except another edit. Confirmed by tracing the code — real bug. Fixed by resolving/constructing the replacement first (buildProvider) and only swapping it into the registry (install) on success; added a regression test (TestCredentialsService_UpsertKeepsThePreviousProviderLiveWhenTheEditIsUnresolvable) that would have caught this.
  • Minor — no validation rejected / in provider names, which would break "name/model" selector routing. Added the check at both the service and handler layer (handler gets a clean 400 instead of a 502-wrapped service error), with tests at both layers.
  • MinorUpsertProviderCredential/DeleteProviderCredential bypassed the admin handler's mutationMu, unlike guardrails/tagging/workflows. Added it — closes a real (if narrow) window where two concurrent edits of the same provider name could interleave between the redaction-merge read and the store write.
  • Minor — the credential store wasn't closed if NewCredentialsService failed after createCredentialStore succeeded. Fixed; note this mirrors a pre-existing (unreported) gap in mcpgateway's equivalent wiring — out of scope here, left as-is.

Deliberately not changed:

  • The "Docstring Coverage" pre-merge check (31.94% vs. an 80% threshold) — this repo's actual style guidance (CLAUDE.md) is the opposite: no comments unless the why is non-obvious. Chasing that metric would work against the project's own conventions, so I left it.
  • Codecov's 0% coverage on credentials_store_postgresql.go/credentials_store_mongodb.go/credentials_wire.go — matches the existing, already-accepted pattern for mcpgateway's Postgres/Mongo stores (only SQLite gets a unit test there either); app-wiring plumbing in app.go/credentials_wire.go was instead verified by actually building and running the binary end-to-end with zero provider env vars.

🤖 Generated with Claude Code

Match the rounded-square icon-button style used everywhere else (was
still circular, missed in the earlier standardization pass), and pull
it in slightly (margin-right: -9px) so it sits flush with the row edge.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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