Skip to content

Commit 277a4b6

Browse files
clawsweeper[bot]dutifulbobosolmaz
authored
fix(ollama): allow Orb host local auth (#84999)
Summary: - The PR adds Docker/OrbStack host aliases to Ollama local-auth classification, keeps those aliases out of loopback-only discovery suppression, adds regression tests, and updates the changelog. - Reproducibility: yes. The linked report gives a concrete v2026.5.19 config and error, and current main source shows host.orb.internal is not classified as local for ollama-local marker auth. Automerge notes: - PR branch already contained follow-up commit before automerge: fix(ollama): allow Orb host local auth Validation: - ClawSweeper review passed for head cb82dcf. - Required merge gates passed before the squash merge. Prepared head SHA: cb82dcf Review: #84999 (comment) Co-authored-by: Bob <dutifulbob@gmail.com> Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com> Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com> Approved-by: osolmaz Co-authored-by: osolmaz <2453968+osolmaz@users.noreply.github.com>
1 parent 229323d commit 277a4b6

6 files changed

Lines changed: 83 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Docs: https://docs.openclaw.ai
1515

1616
### Fixes
1717

18+
- Providers/Ollama: treat Docker/OrbStack host aliases as local Ollama endpoints so `ollama-local` marker auth works when OpenClaw runs inside a VM/container and Ollama runs on the host. Fixes #84875.
1819
- Agents: cap heartbeat model bleed context hints by the stored session window when runtime model metadata is unavailable, so overflow recovery advice does not suggest a larger window than the active session actually has.
1920
- Control UI/Web Push: use `https://openclaw.ai` as the generated default VAPID subject instead of the old localhost mailbox so iOS PWA push setup uses an Apple-acceptable subject when `OPENCLAW_VAPID_SUBJECT` is unset. Fixes #83134. (#83317) Thanks @IWhatsskill.
2021
- Agents/Pi: keep embedded session transcript writes from tripping false takeover detection after packaged npm onboarding agent turns.

extensions/ollama/index.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,32 @@ describe("ollama plugin", () => {
572572
expect(buildOllamaProviderMock).not.toHaveBeenCalled();
573573
});
574574

575+
it.each(["docker.orb.internal", "host.docker.internal", "host.orb.internal"])(
576+
"skips implicit localhost discovery when a custom host-backed Ollama provider is configured for %s",
577+
async (hostname) => {
578+
const provider = registerProvider();
579+
580+
const result = await provider.catalog.run({
581+
config: {
582+
models: {
583+
providers: {
584+
"ollama-orb": {
585+
api: "ollama",
586+
baseUrl: `http://${hostname}:11434`,
587+
models: [{ id: "qwen3.5:27b", name: "Qwen 3.5 27B" }],
588+
},
589+
},
590+
},
591+
},
592+
env: { NODE_ENV: "development", OLLAMA_API_KEY: "ollama-live" },
593+
resolveProviderApiKey: () => ({ apiKey: "ollama-live" }),
594+
} as never);
595+
596+
expect(result).toBeNull();
597+
expect(buildOllamaProviderMock).not.toHaveBeenCalled();
598+
},
599+
);
600+
575601
it("treats custom 127/8 Ollama providers as loopback for implicit discovery", async () => {
576602
const provider = registerProvider();
577603
buildOllamaProviderMock.mockResolvedValueOnce({

extensions/ollama/src/discovery-shared.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ describe("isLocalOllamaBaseUrl", () => {
1515
"http://192.168.1.100:11434",
1616
"http://gpu-node-1:11434",
1717
"http://mac-studio.local:11434",
18+
"http://docker.orb.internal:11434",
19+
"http://host.docker.internal:11434",
20+
"http://host.orb.internal:11434",
1821
"http://[fd00::1]:11434",
1922
"http://[fe90::1]:11434",
2023
])("classifies %s as local", (baseUrl) => {

extensions/ollama/src/discovery-shared.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,17 @@ function shouldSkipAmbientOllamaDiscovery(env: NodeJS.ProcessEnv): boolean {
7070
return Boolean(env.VITEST) || env.NODE_ENV === "test";
7171
}
7272

73-
const LOCAL_OLLAMA_HOSTNAMES = new Set(["localhost", "127.0.0.1", "0.0.0.0", "::1", "::"]);
73+
const LOCAL_OLLAMA_HOSTNAMES = new Set([
74+
"localhost",
75+
"127.0.0.1",
76+
"0.0.0.0",
77+
"::1",
78+
"::",
79+
"docker.orb.internal",
80+
"host.docker.internal",
81+
"host.orb.internal",
82+
]);
83+
const LOOPBACK_OLLAMA_HOSTNAMES = new Set(["localhost", "127.0.0.1", "0.0.0.0", "::1", "::"]);
7484

7585
function isIpv4Loopback(host: string): boolean {
7686
if (!/^\d+\.\d+\.\d+\.\d+$/.test(host)) {
@@ -137,7 +147,7 @@ function isLoopbackOllamaBaseUrl(baseUrl: string | undefined | null): boolean {
137147
if (host.startsWith("[") && host.endsWith("]")) {
138148
host = host.slice(1, -1);
139149
}
140-
return LOCAL_OLLAMA_HOSTNAMES.has(host) || isIpv4Loopback(host);
150+
return LOOPBACK_OLLAMA_HOSTNAMES.has(host) || isIpv4Loopback(host);
141151
}
142152

143153
function hasExplicitRemoteOllamaApiProvider(

src/agents/model-auth.test.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,6 +1111,44 @@ describe("resolveApiKeyForProvider – synthetic local auth for custom providers
11111111
});
11121112
});
11131113

1114+
it.each(["docker.orb.internal", "host.docker.internal", "host.orb.internal"])(
1115+
"accepts ollama-local marker auth for host-backed alias %s",
1116+
async (hostname) => {
1117+
const auth = await resolveApiKeyForProvider({
1118+
provider: "ollama",
1119+
cfg: {
1120+
models: {
1121+
providers: {
1122+
ollama: {
1123+
baseUrl: `http://${hostname}:11434`,
1124+
api: "ollama",
1125+
apiKey: "ollama-local",
1126+
models: [
1127+
{
1128+
id: "qwen3.5:27b",
1129+
name: "Qwen 3.5 27B",
1130+
reasoning: false,
1131+
input: ["text"],
1132+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
1133+
contextWindow: 262144,
1134+
maxTokens: 8192,
1135+
},
1136+
],
1137+
},
1138+
},
1139+
},
1140+
},
1141+
store: { version: 1, profiles: {} },
1142+
});
1143+
1144+
expectAuthFields(auth, {
1145+
apiKey: "ollama-local",
1146+
source: "models.json (local marker)",
1147+
mode: "api-key",
1148+
});
1149+
},
1150+
);
1151+
11141152
it("does not accept non-secret local markers for remote custom providers", async () => {
11151153
await expect(
11161154
resolveApiKeyForProvider({

src/agents/model-auth.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,9 @@ function isLocalBaseUrl(baseUrl: string): boolean {
267267
host === "::1" ||
268268
host === "::ffff:7f00:1" ||
269269
host === "::ffff:127.0.0.1" ||
270+
host === "docker.orb.internal" ||
271+
host === "host.docker.internal" ||
272+
host === "host.orb.internal" ||
270273
host.endsWith(".local") ||
271274
isPrivateIpv4Host(host)
272275
);

0 commit comments

Comments
 (0)