Skip to content

feat: add a switch to disable use of non-published models - #3017

Closed
androw wants to merge 1 commit into
cheaper-inference:mainfrom
androw:nonpublic
Closed

androw wants to merge 1 commit into
cheaper-inference:mainfrom
androw:nonpublic

Conversation

@androw

@androw androw commented May 31, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Add a switch in the API key settings
  • This enables a aditionnal policy that only allows models: that are known to Omniroute (discover or custom) and not hidden.

Related Issues

Validation

  • npm run lint
  • npm run test:unit
  • npm run test:coverage
  • Coverage is still >= 60% for statements, lines, functions, and branches
  • SonarQube PR analysis is green or any remaining issues are explicitly documented below

Tests Added Or Updated

  • No check added or updated

Coverage Notes

  • If this PR changes src/, open-sse/, electron/, or bin/, explain which tests cover the change.
  • If coverage moved down in any touched file, explain why and what follow-up task will recover it.

Coverage before:

=============================== Coverage summary ===============================
Statements   : 78.65% ( 208601/265208 )
Branches     : 74.31% ( 33269/44765 )
Functions    : 80.3% ( 7040/8767 )
Lines        : 78.65% ( 208601/265208 )
================================================================================

Coverage after:

=============================== Coverage summary ===============================
Statements   : 78.65% ( 208632/265255 )
Branches     : 74.3% ( 33279/44786 )
Functions    : 80.31% ( 7042/8768 )
Lines        : 78.65% ( 208632/265255 )
================================================================================

Reviewer Notes

  • Maybe it's better to invert everything, to call this option 'Allow unpublished models'
  • With current master, 'auto/*' goes through the check and are refused as not in models list.
  • I'm not sure about combo management with this change.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces a new feature to restrict API keys to discovered and public models via a 'disableNonPublicModels' toggle. The changes span the UI, API routes, database schemas, migrations, and policy enforcement logic. The review feedback highlights critical issues in the model access check within 'src/lib/db/apiKeys.ts', specifically regarding the blocking of virtual auto-routing combos ('auto/*'), potential false negatives for model IDs without slashes, the use of 'console.error' for standard logging, and inconsistent indentation.

Comment thread src/lib/db/apiKeys.ts Outdated
Comment on lines +1297 to +1320
// Check disableNonPublicModels flag
if (disableNonPublicModels) {
const { resolveModelAlias } = await import("@omniroute/open-sse/services/modelDeprecation.ts");
const { getSyncedAvailableModelsByConnection, getCustomModels, getModelIsHidden } = await import("@/lib/db/models");

const resolvedModelId = resolveModelAlias(modelId);
const effectiveModelId = resolvedModelId || modelId;

console.error(`[Model Access] Checking non-public model access for key ${metadata.id} and model ${modelId} (resolved to ${effectiveModelId})`);

const providerId = effectiveModelId.split("/")[0];
const shortModelId = effectiveModelId.split("/").slice(1).join("/");
const syncedModelsByConnection = await getSyncedAvailableModelsByConnection(providerId);
const customModels = await getCustomModels(providerId);

// Combine synced and custom models
const allDiscoveredModels = Object.values(syncedModelsByConnection).flat().concat(customModels);
const discovered = allDiscoveredModels.some((m) => m.id === shortModelId);
if (!discovered) return false;

const isPublic = !getModelIsHidden(providerId, shortModelId);
console.error(`[Model Access] Model ${effectiveModelId} is public: ${isPublic}`);
if (!isPublic) return false;
}

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.

high

Issues Identified:

  1. Virtual Combos (auto/*): As noted in your reviewer notes, virtual auto-routing combos (like auto/coding) are currently blocked because they are not real models. We should bypass this check for any model ID starting with auto/.
  2. Robustness (No-Slash Model IDs): If effectiveModelId does not contain a slash (e.g., custom models or local models requested directly), split('/') will result in shortModelId = "" and cause a false negative (denying access). We should handle the no-slash case gracefully.
  3. Log Pollution: console.error is used for standard tracing/info logs. This pollutes error monitoring channels and should be changed to console.log.
  4. Indentation & Style: The added block has inconsistent indentation (4 spaces instead of the file's standard 2 spaces), and the comment on line 1297 has 0 indentation.
  // Check disableNonPublicModels flag
  if (disableNonPublicModels && !modelId.startsWith("auto/")) {
    const { resolveModelAlias } = await import("@omniroute/open-sse/services/modelDeprecation.ts");
    const { getSyncedAvailableModelsByConnection, getCustomModels, getModelIsHidden } = await import("@/lib/db/models");
    
    const resolvedModelId = resolveModelAlias(modelId);
    const effectiveModelId = resolvedModelId || modelId;

    console.log("[Model Access] Checking non-public model access for key " + metadata.id + " and model " + modelId + " (resolved to " + effectiveModelId + ")");
    
    const slashIndex = effectiveModelId.indexOf("/");
    const providerId = slashIndex !== -1 ? effectiveModelId.slice(0, slashIndex) : "";
    const shortModelId = slashIndex !== -1 ? effectiveModelId.slice(slashIndex + 1) : effectiveModelId;
    
    const syncedModelsByConnection = await getSyncedAvailableModelsByConnection(providerId);
    const customModels = await getCustomModels(providerId);
    
    // Combine synced and custom models
    const allDiscoveredModels = Object.values(syncedModelsByConnection).flat().concat(customModels);
    const discovered = allDiscoveredModels.some((m) => m.id === shortModelId);
    if (!discovered) return false;
    
    const isPublic = !getModelIsHidden(providerId, shortModelId);
    console.log("[Model Access] Model " + effectiveModelId + " is public: " + isPublic);
    if (!isPublic) return false;
  }

@androw androw May 31, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I've fixed 3 and 4.
Combo aren't supposed to call this code from what I understand.
Accepting help for 2.

@androw

androw commented May 31, 2026

Copy link
Copy Markdown
Contributor Author

@diegosouzapw When you have time, I'm accepting any advice before continuing work here

@diegosouzapw

Copy link
Copy Markdown
Collaborator

Obrigado pela contribuição! Infelizmente esta PR está com conflitos estruturais severos na interface (ApiManagerPageClient.tsx) devido a alterações recentes na branch principal. Para que possamos integrá-la, por favor faça um rebase atualizando sua branch com a main atual e resolva os conflitos.

@androw

androw commented Jun 1, 2026

Copy link
Copy Markdown
Contributor Author

I already rebased against main. I can wait for 3.8.8 changes to be emerged in main.

diegosouzapw added a commit that referenced this pull request Jun 1, 2026
…els (#3041)

Integrates two community contributions into release/v3.8.8 with security hardening and conflict resolution.

- **Plugins framework** (#2913 — thanks @oyi77): hooks + registry unification, plugin SDK (`definePlugin`), worker-thread sandbox, per-plugin hook rate limiting, SHA-256 integrity verification, semver-gated upgrade, and execution analytics. Plugin routes are loopback-only (`isLocalOnlyPath`); `child_process` exec is opt-in via `OMNIROUTE_PLUGINS_ALLOW_EXEC` (default off).
- **API key option: disable non-published models** (#3017 — thanks @androw): a per-key flag restricting the key to discovered public models (combos / `auto/*` / `qtSd/*` routing still allowed).

Hardening applied during integration: migration renumber (089/090/091), `/api/plugins` LOCAL_ONLY route-guard classification (closes the plugin-RCE vector), atomic install/upgrade with path containment, `O_EXCL` tmp-file creation (TOCTOU), rate-limit-map eviction, `validatePluginConfig` on configure, `buildErrorBody` on all plugin error paths. 246/246 tests; typecheck / cycles / docs-sync clean.

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>
Co-authored-by: Nicolas Lorin <androw95220@gmail.com>
@diegosouzapw

Copy link
Copy Markdown
Collaborator

Hi @androw — thank you for this! 🙏

Your "disable non-published models" per-API-key option has been integrated into release/v3.8.8 via #3041 (merged as 20c31493a). I reconciled it with the release's allowedQuotas / quota-sharing work so the flag restricts the key to discovered public models while still allowing combos / auto/* / qtSd/* routing, added migration 089_add_disable_non_public_to_api_keys.sql, and wired it through the policy path with tests.

Because it shipped through the combined integration branch rather than a direct merge of this PR, I'm closing this one — but you're credited in the CHANGELOG (#3017 — thanks @androw) and as a co-author on the squash commit. It'll be part of the upcoming v3.8.8 release. Thanks for the contribution! 🚀

@diegosouzapw diegosouzapw mentioned this pull request Jun 2, 2026
@androw
androw deleted the nonpublic branch June 3, 2026 06:43
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
…els (cheaper-inference#3041)

Integrates two community contributions into release/v3.8.8 with security hardening and conflict resolution.

- **Plugins framework** (cheaper-inference#2913 — thanks @oyi77): hooks + registry unification, plugin SDK (`definePlugin`), worker-thread sandbox, per-plugin hook rate limiting, SHA-256 integrity verification, semver-gated upgrade, and execution analytics. Plugin routes are loopback-only (`isLocalOnlyPath`); `child_process` exec is opt-in via `OMNIROUTE_PLUGINS_ALLOW_EXEC` (default off).
- **API key option: disable non-published models** (cheaper-inference#3017 — thanks @androw): a per-key flag restricting the key to discovered public models (combos / `auto/*` / `qtSd/*` routing still allowed).

Hardening applied during integration: migration renumber (089/090/091), `/api/plugins` LOCAL_ONLY route-guard classification (closes the plugin-RCE vector), atomic install/upgrade with path containment, `O_EXCL` tmp-file creation (TOCTOU), rate-limit-map eviction, `validatePluginConfig` on configure, `buildErrorBody` on all plugin error paths. 246/246 tests; typecheck / cycles / docs-sync clean.

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>
Co-authored-by: Nicolas Lorin <androw95220@gmail.com>
Poid-ZA pushed a commit to Poid-ZA/OmniRoute that referenced this pull request Aug 5, 2026
…els (cheaper-inference#3041)

Integrates two community contributions into release/v3.8.8 with security hardening and conflict resolution.

- **Plugins framework** (cheaper-inference#2913 — thanks @oyi77): hooks + registry unification, plugin SDK (`definePlugin`), worker-thread sandbox, per-plugin hook rate limiting, SHA-256 integrity verification, semver-gated upgrade, and execution analytics. Plugin routes are loopback-only (`isLocalOnlyPath`); `child_process` exec is opt-in via `OMNIROUTE_PLUGINS_ALLOW_EXEC` (default off).
- **API key option: disable non-published models** (cheaper-inference#3017 — thanks @androw): a per-key flag restricting the key to discovered public models (combos / `auto/*` / `qtSd/*` routing still allowed).

Hardening applied during integration: migration renumber (089/090/091), `/api/plugins` LOCAL_ONLY route-guard classification (closes the plugin-RCE vector), atomic install/upgrade with path containment, `O_EXCL` tmp-file creation (TOCTOU), rate-limit-map eviction, `validatePluginConfig` on configure, `buildErrorBody` on all plugin error paths. 246/246 tests; typecheck / cycles / docs-sync clean.

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>
Co-authored-by: Nicolas Lorin <androw95220@gmail.com>
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…els (cheaper-inference#3041)

Integrates two community contributions into release/v3.8.8 with security hardening and conflict resolution.

- **Plugins framework** (cheaper-inference#2913 — thanks @oyi77): hooks + registry unification, plugin SDK (`definePlugin`), worker-thread sandbox, per-plugin hook rate limiting, SHA-256 integrity verification, semver-gated upgrade, and execution analytics. Plugin routes are loopback-only (`isLocalOnlyPath`); `child_process` exec is opt-in via `OMNIROUTE_PLUGINS_ALLOW_EXEC` (default off).
- **API key option: disable non-published models** (cheaper-inference#3017 — thanks @androw): a per-key flag restricting the key to discovered public models (combos / `auto/*` / `qtSd/*` routing still allowed).

Hardening applied during integration: migration renumber (089/090/091), `/api/plugins` LOCAL_ONLY route-guard classification (closes the plugin-RCE vector), atomic install/upgrade with path containment, `O_EXCL` tmp-file creation (TOCTOU), rate-limit-map eviction, `validatePluginConfig` on configure, `buildErrorBody` on all plugin error paths. 246/246 tests; typecheck / cycles / docs-sync clean.

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>
Co-authored-by: Nicolas Lorin <androw95220@gmail.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