openclaw - ✅(Solved) Fix [Feature]: Server-side model selector filtering with auth-gated visibility [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#59811Fetched 2026-04-08 02:40:16
View on GitHub
Comments
0
Participants
1
Timeline
8
Reactions
0
Participants
Timeline (top)
mentioned ×3subscribed ×3cross-referenced ×1referenced ×1

Add a server-side config option controlUi.modelSelector.filter that restricts the Control UI model selector dropdown to only show models the user can actually use, instead of all 600+ models from every known provider.

Root Cause

  1. Client-side filtering in the Control UI — Weaker because WebSocket messages can be intercepted/replayed, bypassing the filter entirely. Server-side enforcement is required.
  2. Regex/glob pattern matching on model names — Introduces ReDoS risk and is harder to reason about. A strict enum of three modes is simpler and safer.
  3. Using only the existing agents.defaults.models allowlist — This serves a different purpose (agent-level model restriction) and doesn't account for credential state.

Fix Action

Fix / Workaround

  1. Server-side filtering only — The models.list RPC handler filters the catalog before sending to the UI. No client-side filtering (WebSocket can be bypassed).
  2. Credential pre-flight on selectionsessions.patch validates credentials exist before accepting a model switch.
  3. Multi-source credential check — Static auth profiles, environment variables, OAuth token expiry, plugin-provided credentials.
  4. Strict enum validation — Config rejects invalid filter values at startup. No regex/glob patterns (ReDoS prevention).

PR fix notes

PR #59693: feat(gateway,control-ui): server-side model selector filtering with auth-gated visibility

Description (problem / solution / changelog)

Summary

Problem: The Control UI model selector dropdown shows all 600+ models from every known provider, regardless of whether the user has credentials configured. This creates an unusable UX and security concerns (auth probing, rate limit exhaustion).

Why it matters: OpenClaw users with 2-3 configured providers must scroll through hundreds of irrelevant models. Selecting an unauthenticated model can trigger failed auth attempts against providers.

What changed: Added a gateway.controlUi.modelSelector.filter config with three server-side filter modes, a credential pre-flight check on model selection, and a UI affordance showing filter status.

Scope boundary: Gateway models.list handler, sessions.patch validation, config schema, and Control UI model selector. No changes to CLI model picker, TUI, or native apps.

Change type

  • Feature

Scope

  • Gateway
  • UI/DX
  • Auth

Linked issues

Closes #48483 — Dashboard ignores models.mode="replace", shows all 600+ models Closes #50498 — Feature: Allow hiding/filtering built-in model catalog entries Refs #59811 — Feature proposal with security analysis

Implementation

Config

```json { "gateway": { "controlUi": { "modelSelector": { "filter": "authenticated" } } } } ```

Filter modes

ModeBehavior
`"all"`Show every model (default, backward-compatible)
`"authenticated"`Only models with valid credentials (auth profiles + env vars + custom API keys)
`"configured"`Only models referenced in agent configs

Architecture

``` models.list RPC handler → reads filter: params.filter ?? config ?? "all" → filterModelCatalog() applies server-side filter → response includes _meta { totalCount, filteredCount, filterMode } → UI renders pre-filtered list + status affordance

sessions.patch handler → when filter is "authenticated" or "configured" → checkProviderAuth() validates credentials exist → rejects with INVALID_REQUEST if no credentials ```

Security impact

  • Does this touch auth, credentials, tokens, or secrets? — Yes, reads credential state to filter
  • Does this change API surface or protocol? — Yes, adds optional filter param and _meta to models.list
  • Does this affect access control? — Yes, restricts model visibility by credential state
  • Does this handle user-supplied file paths?
  • Does this execute or eval dynamic strings?

Security audit (7 findings, all PASS)

#FindingSeverityStatus
1Server-side filtering onlyPASS
2Auth probing preventionHIGHPASS
3No RegExp/injectionMEDIUMPASS
4No client-side bypassHIGHPASS
5Multi-source credential checkPASS
6Backward compatibilityPASS
7Auth state handlingMEDIUMPASS (expiry deferred)

Test plan

  • 47 new tests across 4 test files
  • Config validation: 13 tests (accepts valid values, rejects invalid)
  • filterModelCatalog: 21 tests (all modes, edge cases, caching)
  • sessions-patch pre-flight: 7 tests (auth/reject scenarios)
  • models.list handler: 6 tests (param/config priority, _meta)
  • All existing tests pass (75+ across touched files)

User-visible behavior changes

  • New config option `gateway.controlUi.modelSelector.filter`
  • When set to "authenticated": model dropdown shows only models with credentials
  • When set to "configured": model dropdown shows only agent-configured models
  • Subtle status text appears below dropdown: "Showing authenticated models only"
  • Default ("all") preserves current behavior exactly

Compatibility / migration

  • Fully backward compatible — default is "all" (current behavior)
  • No breaking changes to existing configs
  • New config key is optional with graceful fallback

Generated with Claude Code

Changed files

  • src/agents/model-selection.filter.test.ts (added, +410/-0)
  • src/agents/model-selection.ts (modified, +145/-0)
  • src/config/schema.base.generated.ts (modified, +32/-0)
  • src/config/schema.help.ts (modified, +4/-0)
  • src/config/schema.labels.ts (modified, +2/-0)
  • src/config/types.gateway.ts (modified, +14/-0)
  • src/config/zod-schema.model-selector-filter.test.ts (added, +86/-0)
  • src/config/zod-schema.ts (modified, +12/-0)
  • src/gateway/protocol/schema/agents-models-skills.ts (modified, +26/-1)
  • src/gateway/server-methods/models.filter.test.ts (added, +198/-0)
  • src/gateway/server-methods/models.ts (modified, +28/-3)
  • src/gateway/sessions-patch.model-filter-preflight.test.ts (added, +180/-0)
  • src/gateway/sessions-patch.ts (modified, +16/-0)
  • ui/src/ui/app-chat.test.ts (modified, +1/-0)
  • ui/src/ui/app-chat.ts (modified, +7/-3)
  • ui/src/ui/app-render.helpers.ts (modified, +33/-1)
  • ui/src/ui/app-view-state.ts (modified, +2/-0)
  • ui/src/ui/app.ts (modified, +2/-0)
  • ui/src/ui/chat-model-select-state.test.ts (modified, +3/-0)
  • ui/src/ui/chat-model-select-state.ts (modified, +4/-2)
  • ui/src/ui/controllers/models.ts (modified, +28/-2)
  • ui/src/ui/types.ts (modified, +7/-0)
  • ui/src/ui/views/chat.test.ts (modified, +1/-0)
RAW_BUFFERClick to expand / collapse

Summary

Add a server-side config option controlUi.modelSelector.filter that restricts the Control UI model selector dropdown to only show models the user can actually use, instead of all 600+ models from every known provider.

Problem to solve

The Control UI model selector dropdown currently shows all 600+ models from every known provider, regardless of whether the user has credentials configured. This creates two problems:

  1. Unusability — Finding the right model in a list of 600+ entries is painful, especially for operators who only use 1-2 providers.
  2. Security concerns — Selecting an unauthenticated model triggers auth probing on providers the operator hasn't configured, and can cause rate limit exhaustion on misconfigured endpoints.

Related issues: #48483, #50498 Related PRs: #48535, #42797

Proposed solution

Add a controlUi.modelSelector.filter config with three modes:

ModeBehavior
"all"Show every model (current behavior, backward-compatible default)
"authenticated"Only models with valid, non-expired credentials
"configured"Only models referenced in agent configs

Key design decisions:

  1. Server-side filtering only — The models.list RPC handler filters the catalog before sending to the UI. No client-side filtering (WebSocket can be bypassed).
  2. Credential pre-flight on selectionsessions.patch validates credentials exist before accepting a model switch.
  3. Multi-source credential check — Static auth profiles, environment variables, OAuth token expiry, plugin-provided credentials.
  4. Strict enum validation — Config rejects invalid filter values at startup. No regex/glob patterns (ReDoS prevention).

Implementation touchpoints:

  • Gateway: Extend ModelsListParamsSchema in src/gateway/protocol/schema/agents-models-skills.ts, add filter logic in models.list handler in src/gateway/server-methods/models.ts, extend buildAllowedModelSet() in src/agents/model-selection.ts, add config type in src/config/types.agent-defaults.ts
  • Control UI: Add "Showing X of Y models" affordance in model selector
  • Tests: Unit, integration, and contract tests covering all filter modes

Alternatives considered

  1. Client-side filtering in the Control UI — Weaker because WebSocket messages can be intercepted/replayed, bypassing the filter entirely. Server-side enforcement is required.
  2. Regex/glob pattern matching on model names — Introduces ReDoS risk and is harder to reason about. A strict enum of three modes is simpler and safer.
  3. Using only the existing agents.defaults.models allowlist — This serves a different purpose (agent-level model restriction) and doesn't account for credential state.

Impact

  • Affected: All Control UI operators, especially multi-provider deployments
  • Severity: Medium-High (usability blocker for large deployments; HIGH security finding for auth probing)
  • Frequency: Every model selection interaction
  • Consequence: Operators waste time scrolling through irrelevant models; unauthenticated model selections trigger unnecessary auth failures and potential rate limiting

Evidence/examples

Security audit findings (7 items):

#FindingSeverity
1Model registry enumeration exposes provider infoLOW
2Auth probing via unauthenticated model selectionHIGH
3Config injection / ReDoS if regex patterns addedMEDIUM
4Client-side filter bypass via WebSocketHIGH
5Default "all" violates least privilegeMEDIUM
6Env-var credentials invisible to filterLOW
7Auth state staleness (expired OAuth tokens)MEDIUM

Related open PRs touching adjacent code:

  • #48535 by @adpena
  • #42797 by @yzxlr

Additional information

Questions for maintainers:

  1. Should the default be "all" (backward compat) or "authenticated" (least privilege)?
  2. Should this integrate with the existing agents.defaults.models allowlist or be a separate config path?
  3. Is there appetite to merge this given the open PRs #48535 and #42797 that touch adjacent code?

Happy to coordinate with @adpena and @yzxlr to avoid conflicts. Full security analysis and implementation plan available upon request.

Note: Per CONTRIBUTING.md, new features require a Discussion first. Since Discussions are not enabled on this repo, this issue serves as the feature proposal.


Contributor: @La-Cappuccino | AI-assisted analysis

extent analysis

TL;DR

Implement the proposed controlUi.modelSelector.filter config option to restrict the Control UI model selector dropdown to only show models the user can actually use.

Guidance

  1. Add the controlUi.modelSelector.filter config: Implement the proposed config with three modes: "all", "authenticated", and "configured", to filter models based on user credentials and configuration.
  2. Update the models.list RPC handler: Modify the handler to filter the model catalog before sending it to the UI, based on the selected filter mode.
  3. Implement credential pre-flight on selection: Validate credentials exist before accepting a model switch in the sessions.patch method.
  4. Perform multi-source credential check: Verify credentials using static auth profiles, environment variables, OAuth token expiry, and plugin-provided credentials.
  5. Test the implementation: Write unit, integration, and contract tests to cover all filter modes and ensure the feature works as expected.

Example

// Example of updated ModelsListParamsSchema
interface ModelsListParamsSchema {
  filter: 'all' | 'authenticated' | 'configured';
}

// Example of updated models.list handler
async function modelsListHandler(params: ModelsListParamsSchema) {
  const models = await getModelCatalog();
  const filteredModels = filterModels(models, params.filter);
  return filteredModels;
}

// Example of filterModels function
function filterModels(models, filterMode) {
  switch (filterMode) {
    case 'all':
      return models;
    case 'authenticated':
      return models.filter((model) => hasValidCredentials(model));
    case 'configured':
      return models.filter((model) => isModelConfigured(model));
    default:
      throw new Error(`Invalid filter mode: ${filterMode}`);
  }
}

Notes

The proposed solution requires careful consideration of the default filter mode, with options being "all" for backward compatibility or "authenticated" for least privilege. Additionally, the integration with the existing agents.defaults.models allowlist needs to be determined.

Recommendation

Apply the proposed workaround by implementing the controlUi.modelSelector.filter config option, as it addresses both usability and security concerns by restricting the model selector dropdown to only show models the user can actually use.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix [Feature]: Server-side model selector filtering with auth-gated visibility [1 pull requests, 1 participants]