Skip to content

Bug: Ollama provider discovery is blocked by destination policy in OpenCode 2.7.43 #758

Closed

Description

@5p4w7

Client or integration

Claude Code

Area

CLI

Summary

I'm using OpenCode 2.7.43 with Ollama as the default provider under Ubuntu 24.04 WSL2.

Although the Ollama provider is correctly configured and Ollama is reachable, ocx sync refuses to discover local models because the destination policy blocks loopback/localhost addresses.

As a result:

No Ollama models are added to the catalog.
/v1/models only exposes OpenAI models.
Claude model aliases (for example claude-opus-4-8-rox) are forwarded to the OpenAI backend instead of Ollama.
Claude Code then fails because OpenAI rejects those model names.

Expected behavior:

The built-in Ollama provider should be allowed to discover models from the configured local endpoint.

Reproduction

Reproduction
Environment
OpenCode: 2.7.43

Installed via:

npm install -g @bitkyc08/opencodex
Ubuntu 24.04 WSL2
Ollama running locally

Configuration:

{
"defaultProvider": "ollama",
"providers": {
"ollama": {
"adapter": "openai-chat",
"baseUrl": "http://127.0.0.1:11434/v1"
}
}
}

Verify Ollama is reachable:

curl http://127.0.0.1:11434/v1/models

This correctly returns my installed models.

Run:

ocx sync

Result:

Provider model discovery for "ollama" was blocked by destination policy:
provider URL targets loopback address

Changing the URL to http://localhost:11434/v1 changes the message to:

provider URL targets localhost destination

but the behavior is identical.

Expected Result

ocx sync should successfully discover the models exposed by the configured Ollama endpoint and include them in the OpenCode model catalog.

Actual Result

Model discovery is blocked.

curl http://127.0.0.1:10100/v1/models

returns only OpenAI models.

Claude requests are then routed to OpenAI instead of Ollama.

Example proxy log:

400 openai/claude-opus-4-8-rox

The OpenAI backend responds:

The 'claude-opus-4-8-rox' model is not supported when using Codex with a ChatGPT account.
Additional Information

The provider appears to be configured correctly:

ocx provider show ollama

returns:

Provider: ollama (default)
adapter: openai-chat
baseUrl: http://127.0.0.1:11434/v1

The installed source also registers the built-in Ollama provider with:

allowPrivateNetworkByDefault: true

located in:

src/providers/registry.ts

However, model discovery is still rejected by the destination policy during ocx sync.

Image Image Image Image

Version

2.7.43

Operating system

Windows 11 24H2

Provider and model

No response

Logs or error output

nbpacio@COOLMAN-PC:~$ ocx config show
{
  "port": 10100,
  "providers": {
    "openai": {
      "adapter": "openai-responses",
      "baseUrl": "https://chatgpt.com/backend-api/codex",
      "codexAccountMode": "pool",
      "authMode": "forward"
    },
    "ollama": {
      "adapter": "openai-chat",
      "baseUrl": "http://127.0.0.1:11434/v1"
    }
  },
  "defaultProvider": "ollama",
  "openaiProviderTierVersion": 2,
  "multiAgentGuidanceEnabled": true,
  "codexShimAutoRestore": true,
  "subagentModels": [
    "gpt-5.5",
    "gpt-5.6-sol",
    "gpt-5.6-terra",
    "gpt-5.6-luna",
    "gpt-5.4-mini"
  ],
  "websockets": false,
  "codexAutoStart": true,
  "activeCodexAccountId": "__main__"
}
nbpacio@COOLMAN-PC:~$ ocx sync
[opencodex] Provider model discovery for "ollama" was blocked by destination policy: provider URL targets loopback address [urlClass=provider-models, fallback=configured].
Pointed Codex's built-in openai provider at the opencodex proxy (openai_base_url).
  Codex model catalog: /mnt/c/Users/nbpac/.codex/opencodex-catalog.json
  Codex resume history: untouched (threads keep their native openai tag).
  All models now route through opencodex proxy (like OpenRouter).
  OpenAI models (gpt-5.5, etc.) are passed through to OpenAI.
  Custom models route to their configured providers.
  Fallback reference: /mnt/c/Users/nbpac/.codex/opencodex.config.toml
   Target Codex home: /mnt/c/Users/[USER]/.codex
nbpacio@COOLMAN-PC:~$ ocx provider show ollama
Provider: ollama (default)
  adapter:      openai-chat
  baseUrl:      http://127.0.0.1:11434/v1
nbpacio@COOLMAN-PC:~$ curl http://127.0.0.1:11434/v1/models
{"object":"list","data":[{"id":"hf.co/tvall43/Qwen3.6-14B-A3B-FableVibes-GGUF:Q4_K_M","object":"model","created":1785333239,"owned_by":"tvall43"},{"id":"fervent_mcclintock/Gemma-3-12B-Instruct:Q4_K_M","object":"model","created":1785332957,"owned_by":"fervent_mcclintock"},{"id":"frob/mradermacher-Llama3.3-8B-Thinking-Heretic-Claude-4.5-Opus:q8_0","object":"model","created":1785332291,"owned_by":"frob"},{"id":"deepseek-r1:8b","object":"model","created":1785331986,"owned_by":"library"},{"id":"qcwind/qwen3-8b-instruct-Q4-K-M:latest","object":"model","created":1785330826,"owned_by":"qcwind"}]}
nbpacio@COOLMAN-PC:~$ curl http://127.0.0.1:11434/v1/models
{"object":"list","data":[{"id":"hf.co/tvall43/Qwen3.6-14B-A3B-FableVibes-GGUF:Q4_K_M","object":"model","created":1785333239,"owned_by":"tvall43"},{"id":"fervent_mcclintock/Gemma-3-12B-Instruct:Q4_K_M","object":"model","created":1785332957,"owned_by":"fervent_mcclintock"},{"id":"frob/mradermacher-Llama3.3-8B-Thinking-Heretic-Claude-4.5-Opus:q8_0","object":"model","created":1785332291,"owned_by":"frob"},{"id":"deepseek-r1:8b","object":"model","created":1785331986,"owned_by":"library"},{"id":"qcwind/qwen3-8b-instruct-Q4-K-M:latest","object":"model","created":1785330826,"owned_by":"qcwind"}]}
nbpacio@COOLMAN-PC:~$

Screenshots and supporting files

No response

Redacted configuration

Checks

  • I searched existing issues and documentation.
  • I removed secrets, tokens, account details, request credentials, and personal data.

Activity

  1. github-actions commented on Jul 30, 2026

    @github-actions
    Contributor

    Automated translation bookkeeping — detected language: unknown.

  2. Wibias commented on Jul 30, 2026

    @Wibias
    Contributor

    [shipping-github] Research review

    Claim: Built-in Ollama discovery is blocked by destination policy despite allowPrivateNetworkByDefault: true.
    Checked against: dev@be177ea5

    Field Finding
    Still an issue on latest development? yes
    Exact issue Discovery providerOutboundGet → resolvePublicAddresses honors only provider.allowPrivateNetwork; registry allowPrivateNetworkByDefault is not applied on that path, so loopback Ollama /models is blocked
    Fixed on development? no
    Open PR covering this? none
    Duplicate of? same family as closed #292 (allowPrivateNetwork ignored for discovery); this report is the built-in Ollama default gap
    Security relevance possible — fix touches SSRF/destination-policy wiring; must keep metadata/link-local denied
    Priority high — breaks default Ollama local setup / model catalog on sync
    Verdict Needs fix on development

    Evidence: Registry marks ollama with allowPrivateNetworkByDefault: true (src/providers/registry.ts). providerDestinationConfigError consults the registry, but src/lib/provider-outbound.ts passes allowPrivateNetwork: provider.allowPrivateNetwork into resolvePublicAddresses, which only admits private when that flag is true. Saved configs typically omit the flag. Workaround: set allowPrivateNetwork: true on the provider.
    Gaps: Did not run live ocx sync against a local Ollama in this session.

  3. Wibias commented on Jul 30, 2026

    @Wibias
    Contributor

    [shipping-github] Security review

    Checked against: dev@be177ea5
    Scope: Provider model-discovery destination policy vs registry allowPrivateNetworkByDefault (Ollama/local providers)

    Severity Area Finding Next
    low (current tip) SSRF / destination policy Current discovery outbound path fails closed for loopback/private URLs unless provider.allowPrivateNetwork === true. That is over-restrictive for built-ins that declare allowPrivateNetworkByDefault, not an open SSRF hole. Fix availability by honouring registry defaults on the discovery transport only for those registry ids.
    medium (fix constraint) SSRF / destination policy A careless fix that sets allowPrivateNetwork: true for arbitrary providers, or that admits metadata/link-local/unspecified destinations, would re-open discovery-time SSRF. Preserve deny for metadata/link-local/unspecified; require explicit opt-in for non-registry/custom providers; cover with regression tests.

    Summary: No exploitable bypass found on tip. Treat the patch as security-sensitive destination-policy wiring.
    Residual: Custom providers pointing at private ranges still need explicit user opt-in after the fix.

  4. Wibias commented on Jul 30, 2026

    @Wibias
    Contributor

    [shipping-github] Opened PR #775 to address this.

  5. lidge-jun commented on Jul 31, 2026

    @lidge-jun
    Owner

    Fixed on codex/260731-pr-merge-round (3f7a5045).

    The cause was one provider being judged by two boundaries that disagreed. Ollama's registry entry carries allowPrivateNetworkByDefault — a local model server is what it is — and config validation honoured that. Outbound discovery did not: it read only the operator's allowPrivateNetwork flag (src/lib/provider-outbound.ts), so a stock Ollama provider validated cleanly and was then refused at the fetch.

    Both boundaries now use the same permission helper. Nothing new is granted: a provider with neither a registry default nor an explicit flag is denied exactly as before, and only the three entries that are local by definition (Ollama, vLLM, LM Studio) carry the default.

    The SSRF exclusions are untouched, and they sit in front of this permission rather than inside it — metadata, link-local and unspecified destinations are rejected before any private-network question is asked. The regression tests assert that for Ollama specifically, since it is the provider that gained access; if those exclusions were ever folded into the same check, 169.254.169.254 is where it would show, and an SSRF there leaks cloud instance credentials.

    Worth recording how the tests got there, because the first version was useless. They initially drove the injected-executor branch, and reverting the fix did not fail them — that branch delegates to a helper which already consulted the registry, so they never touched the defect. Real discovery has no injected executor and goes through the pinned transport. They now inject resolveAddresses there and assert the permission value it actually receives. Reverting the fix fails 1 of 6; restoring passes 6/6.

    This touches a destination-security boundary, so it wants a maintainer's review before it reaches dev, even though it only re-uses a permission the config layer already grants. PR #775 proposed the same architecture and reached the same conclusion independently.

    The branch merges into dev and ships once the optimization work in flight there completes.

  6. added a commit that references this issue on Jul 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions