Skip to content

feat: add OpenAI-compatible local endpoint backend#770

Merged
FuJacob merged 6 commits into
FuJacob:mainfrom
akramj13:codex/add-ollama-settings-page
Jul 5, 2026
Merged

feat: add OpenAI-compatible local endpoint backend#770
FuJacob merged 6 commits into
FuJacob:mainfrom
akramj13:codex/add-ollama-settings-page

Conversation

@akramj13

@akramj13 akramj13 commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds Local Endpoint as a third suggestion engine for OpenAI-compatible servers such as Ollama, LM Studio, and vLLM. The integration keeps generation local-first while providing model discovery, streamed completions, Keychain-backed authentication, privacy warnings, and lifecycle handling that unloads the bundled llama runtime when an external endpoint is active.

Validation

  • xcodegen generate — completed successfully
  • Unsigned xcodebuild ... build-for-testing with repo-local build/DerivedData — passed
  • Focused endpoint, router, settings, persistence, attention, and prompt-policy tests via test-without-building — passed
  • Live Ollama validation at http://127.0.0.1:11434/v1: discovered and selected gemma4:12b-mlx, streamed Chat Completions without an API key, and confirmed the in-process llama runtime shut down
  • plutil -lint CotabbyInfo.plist — passed
  • git diff --check — passed

Linked issues

Fixes #627

Risk / rollout notes

  • Adds persisted endpoint URL, request mode, model identifiers, and per-power-profile endpoint model selections.
  • Optional bearer credentials are stored in Keychain and are removed by Reset All Settings.
  • Adds NSAllowsLocalNetworking; insecure public HTTP endpoints remain rejected.
  • Non-loopback endpoints warn that typed text and enabled context leave the Mac.

Greptile Summary

This PR adds a local OpenAI-compatible endpoint backend. The main changes are:

  • New endpoint configuration, model discovery, and streaming generation support.
  • Settings UI for endpoint URL, API mode, model selection, and credentials.
  • Keychain-backed API key storage and reset handling.
  • Router and lifecycle updates for switching between bundled and endpoint engines.
  • Tests for endpoint settings, routing, persistence, debounce behavior, and API client paths.

Confidence Score: 4/5

This is close, but the endpoint HTTP guard should be fixed before merging.

  • DNS hostnames that look like IPv6 local prefixes can skip the public HTTP rejection.
  • The endpoint-state invalidation changes cover the main URL, mode, model, and credential updates reviewed here.

Cotabby/Models/OpenAICompatibleEndpointModels.swift

Security Review

The endpoint HTTP policy can still classify some DNS hostnames as local IPv6 addresses. This can allow plaintext HTTP to a host that resolves outside the local network.

Important Files Changed

Filename Overview
Cotabby/Models/OpenAICompatibleEndpointModels.swift Adds endpoint URL validation and host classification, but the local IPv6 checks can also match DNS hostnames.
Cotabby/App/Core/CotabbyAppEnvironment.swift Adds endpoint client wiring and invalidates connection state when active endpoint settings change.

Fix All in Codex Fix All in Claude Code

Reviews (3): Last reviewed commit: "fix: invalidate stale endpoint connectio..." | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

@akramj13

akramj13 commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator Author

@greptileai

Comment thread Cotabby/Models/OpenAICompatibleEndpointModels.swift Outdated
Comment thread Cotabby/App/Core/CotabbyAppEnvironment.swift
@akramj13

akramj13 commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator Author

@greptileai

@akramj13 akramj13 marked this pull request as ready for review July 4, 2026 21:40
Comment on lines +143 to +147
// A single-label DNS name is not proof of LAN scope: DNS may resolve it publicly.
if host.hasSuffix(".local") || host.hasPrefix("fc") || host.hasPrefix("fd")
|| (host.hasPrefix("fe") && (host.dropFirst(2).first.map { "89ab".contains($0) } ?? false)) {
return .localNetwork
}

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.

P1 security Hostnames Bypass HTTP Guard

This branch applies IPv6 local-prefix checks before proving the host is an IPv6 literal. A valid DNS name such as http://feast:11434/v1 is classified as local network because it starts with fea, so the public HTTP rejection does not run. If that name resolves outside the local network, typed text and enabled context are sent over plaintext HTTP while the UI treats the endpoint as local.

Suggested change
// A single-label DNS name is not proof of LAN scope: DNS may resolve it publicly.
if host.hasSuffix(".local") || host.hasPrefix("fc") || host.hasPrefix("fd")
|| (host.hasPrefix("fe") && (host.dropFirst(2).first.map { "89ab".contains($0) } ?? false)) {
return .localNetwork
}
// A single-label DNS name is not proof of LAN scope: DNS may resolve it publicly.
if host.hasSuffix(".local") {
return .localNetwork
}
if host.contains(":"), host.hasPrefix("fc") || host.hasPrefix("fd")
|| (host.hasPrefix("fe") && (host.dropFirst(2).first.map { "89ab".contains($0) } ?? false)) {
return .localNetwork
}

Fix in Codex Fix in Claude Code

@FuJacob FuJacob merged commit 126269d into FuJacob:main Jul 5, 2026
5 checks passed
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.

[Feature] Support local OpenAI-compatible endpoint (Ollama / LM Studio / etc.)

2 participants