components/model/ollama: requests to ollama.com fail on hosts without ~/.ollama/id_ed25519 — forced request signing has no opt-out
- Dominant language
- Go
- Stars
- 13k
- Forks
- 1.1k
- Avg merge
- 4h 6m
- Merged PRs (30d)
- 41
Description
## Summary
`components/model/ollama` (v0.1.9, dep `eino-contrib/ollama` v0.1.0) unconditionally
signs every request with the local Ollama CLI's private key (`~/.ollama/id_ed25519`)
when the base URL hostname is `ollama.com`. On hosts where the Ollama app was never
installed (servers, CI, containers), the key is absent, so **every API call fails
before the request is sent** — even when the app supplies its own `Authorization`
header via the injected `HTTPClient` (the documented way to use an API key).
## Environment
- `github.com/cloudwego/eino-ext/components/model/ollama` v0.1.9
- `github.com/eino-contrib/ollama` v0.1.0 (only published version)
- Go 1.25, linux/amd64
- No `~/.ollama/id_ed25519` on the host (headless server)
## Repro
```go
package main
import (
"context"
"fmt"
"net/http"
"net/url"
"github.com/eino-contrib/ollama/api"
)
func main() {
base, _ := url.Parse("https://ollama.com")
client := api.NewClient(base, &http.Client{})
_, err := client.List(context.Background())
fmt.Println("err:", err)
// err: open /home/user/.ollama/id_ed25519: no such file or directory
}
```
Output on a keyless host:
```
{"level":"INFO","msg":"Failed to load private key: open /home/user/.ollama/id_ed25519: no such file or directory"}
err: open /home/user/.ollama/id_ed25519: no such file or directory
```
The same snippet against `https://ollama.com.` (trailing dot) succeeds — confirming
the hardcoded hostname comparison is the trigger.
## Root cause
`api/client.go`, in both `do()` (line 119) and `stream()` (line 184):
```go
if envconfig.UseAuth() || c.base.Hostname() == "ollama.com" {
token, err = getAuthorizationToken(ctx, chal) // → auth.Sign()
if err != nil {
return err // request aborted before it is sent
}
...
}
```
- The hostname branch is unconditional. `OLLAMA_AUTH` (`envconfig/config.go:198`)
participates via `||`, so it can only *add* the signing requirement, never remove it.
- `auth.Sign` (`auth/auth.go:59`) reads `~/.ollama/id_ed25519` and returns an error
when missing, aborting the request. It also emits `slog.Info("Failed to load
private key: ...")` to the **host application's default logger** — INFO-level
noise for a hard failure.
- The signed token is set as `Authorization` at `client.go:143` **before**
`c.http.Do()`, so a caller-supplied `http.RoundTripper` that sets its own
`Authorization: Bearer ` would overwrite it anyway — the signing is
redundant in that setup, yet still mandatory.
This makes `ChatModel` with `BaseURL: "https://ollama.com"` + API-key auth via
`HTTPClient` unusable on any host without the Ollama CLI installed — the primary
use case for a model-component library running on servers.
## Suggested fixes (any one)
1. Skip signing when the caller's `HTTPClient`/transport will set its own
`Authorization` (or expose an explicit `SkipAuth`/`NoSign` option on `Client`).
2. Honor `OLLAMA_AUTH=false` as an opt-out by changing `||` to a plain check of
`UseAuth()` and treating the `ollama.com` hostname as the *default* (on) rather
than *forced*.
3. Fall back to an unsigned request when the key is missing instead of aborting.
## Workaround
Use the FQDN form with a trailing dot: `https://ollama.com.` — valid DNS,
`url.Parse` does not normalize the hostname, so the comparison misses while
DNS/TLS treat it identically. (Also `https://OLLAMA.COM` works, but a future
case-insensitive comparison would silently break it.)
Contributor guide
Research direction
Start in api/client.go at do() and stream(), then read envconfig/config.go and auth/auth.go to trace the forced signing and missing-key error. Reproduce the keyless https://ollama.com request with a caller-supplied Authorization header. Done means the documented API-key use works without ~/.ollama/id_ed25519 while existing Ollama authentication behavior remains covered.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, ollama
- Domain
- api, authentication, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 50/100