v0.4.0 — multi-tenant admission control, fleet standard, release hardening
Repositions the project as multi-tenant admission control for a shared Ollama rather than a fleet pool manager, brings the repo to the fleet Baseline standard, and corrects four README claims the code did not implement.
Changed
- Positioning. The README led with "smart pool manager for Ollama", competing on fleet routing against tools that do it better. It now leads with authenticated multi-tenant policy — priority bound to a credential and enforced server-side — which is the thing no comparable tool does: Olla has no inbound client auth at all, ollamaMQ keys priority off an unauthenticated
X-User-IDheader, and LiteLLM'spriorityis caller-declared, beta, and excludes embeddings. - Host state unified;
hosts.pyandHostManagerdeleted. Two structures tracked the same facts with different refresh rules andproxy.pyselected through one while failing over through the other.HostManager._health_loopre-probed a host onlyif not host.healthy, so a host healthy at startup was never polled again and its model list was frozen for the process lifetime.RoutingTablepolled correctly but was built only whenrouting.strategy != "round_robin"— andround_robinis the default, so the default deployment had no polling table at all. The routing table is now built unconditionally. - BREAKING (metric):
oqp_host_models_loaded→oqp_host_models_installed. The old name said "loaded" (resident in VRAM,/api/ps) and reported models merely installed on disk (/api/tags). No alias is kept. HostRoutingState.loaded_models→installed_models, for the same reason.pick()now prefers reachable hosts on theround_robinpath too, and returns a candidate rather thanNonewhen no host is reachable — a stalereachable=Falseshould not black-hole the proxy into 503s.- Management endpoints check authorization before validating input.
?tier=bogusfrom an unauthenticated caller returned 400 enumerating the valid tiers; it now returns 401.
Added
key_env:andkey_file:for API keys. Exactly one ofkey/key_env/key_fileper entry. A literal inconfig.ymlwas previously the only option and not by design:_apply_env_overridesskips any path with a numeric component, soOQP_AUTH__KEYS__0__KEYwas silently ignored — the list index trips it.key_filestrips trailing whitespace and warns when the file is readable beyond its owner. Resolved keys carryrepr=Falseand never appear in an error message.- Metadata fast path.
/api/tags,/api/version,/api/ps,/api/showand/bypass the priority queue and worker semaphore (they still authenticate). Previously a UI polling/api/tagsqueued behind a multi-minute generation. queue.max_queued_mb(default 512) — global cap on bytes waiting across all tiers. Depth limits bound the number of waiting requests; each holds its whole body until dispatch.- Secret scanning as a CI gate — both halves: a push/PR gate and a scheduled full-history scan with
fetch-depth: 0..gitleaks.tomlhad been present for months with nothing running it. - Release hardening. Every published architecture is now built, scanned with Trivy and smoke-tested (the container is actually started and must serve
/health) before anything is pushed. Publishes multi-arch with provenance, SBOM and a signed build-provenance attestation. - CodeQL (
pythonandactions), OSSF Scorecard, CODEOWNERS, Dependabot, and a committeduv.lock. - Ruff now carries the eight fleet-mandated rule families and lints
tests/as well assrc/.
Deprecated
ollama.health_check_intervaldrove the deletedHostManagerloop and is no longer read;ollama.hosts[].model_sync_intervalis now the only poll interval, and it polls every host rather than only unhealthy ones. Setting it logs a warning at startup.
Documented
- Low-tier expiry: at
max_waita queued request is not promoted — it fails with503 {"error": "request expired in queue"}and noRetry-After. - The auth-off caveat: with
auth.enabled: false(the shipped default and quick-start path),client_idcomes from a caller-supplied header and the priority ceiling is not applied, so any client can claim any identity and any priority. - Model-aware routing routes on installed, not loaded, models. Where every host has the same models pulled,
model_awaredegenerates to weighted round-robin. - The proxy does not rate-limit.
RateLimitConfigthrottles failed authentication attempts per IP; there is no per-client request rate limit.
Conformance against the fleet Baseline: 12 pass / 6 fail → 24 pass / 0 fail / 0 skip. Tests 150 → 227; coverage 63% → 75.79% with the floor raised to 75 and CI measuring it for the first time.
Security audit: 2 findings, 1 resolved and 1 accepted. See CHANGELOG.md for the full entry.
Deploy: update ghcr.io/tadmstr/ollama-queue-proxy to 0.4.0. Existing configs load unchanged — verified against a live 11-key deployment. Note the breaking metric rename above if you scrape /metrics.