Skip to content

fix(models): restore authenticated Actual discovery on macOS - #86492

Merged
teknium1 merged 3 commits into
mainfrom
salv-83554
Aug 14, 2026
Merged

fix(models): restore authenticated Actual discovery on macOS#86492
teknium1 merged 3 commits into
mainfrom
salv-83554

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Summary

Actual model catalog discovery works again on macOS: the plugin's /v1/models fetch now routes through Hermes' credential-safe urllib opener, and Hermes-owned urllib openers get an explicit CA context (env bundles first, certifi fallback on macOS only) so Homebrew-Python cert stores no longer kill the request before it's sent.

Root cause: ActualProfile.fetch_models called raw urllib.request.urlopen — bypassing the cross-origin credential-stripping guard entirely — and urllib's default trust store on affected macOS environments failed verification pre-send, making an authenticated catalog look like a missing-key failure (Desktop showed zero models).

Salvaged from #83554 by @somewheresy — commits preserved for both authors (@pierrenode's redirect-guard routing + Justin Bennington's certifi CA context). Incorporates the #80247 author commit.

Changes

  • plugins/model-providers/actual/__init__.py: fetch_models uses open_credentialed_url instead of raw urlopen
  • hermes_cli/urllib_security.py: _resolved_https_context() — HERMES_CA_BUNDLE/SSL_CERT_FILE/REQUESTS_CA_BUNDLE/CURL_CA_BUNDLE precedence, macOS-only certifi fallback, never disables verification; only applies when Hermes builds the opener (installed app openers untouched)
  • tests/: real two-server redirect e2e proving Bearer never crosses origins, CA selection, no-credential no-probe behavior

Validation

Before After
macOS Homebrew Python catalog fetch CERTIFICATE_VERIFY_FAILED pre-send certifi-verified request
Bearer on cross-origin redirect forwarded (raw urlopen) stripped by guard
Linux, no env override stdlib default unchanged (None context)
targeted tests 31 passed (actual_provider + urllib_security)

E2E (real imports): env-CA precedence honored, bad bundle path warns and falls through, darwin gets certifi context, linux default unchanged. Author verified live on macOS: 3 models returned + full AIAgent round-trip through provider actual.

Note: overlaps _secure_opener_from_installed_policy with #86491 (per-request ssl_context seam) — semantically compatible; whichever merges second gets a trivial rebase.

Infographic

Actual macOS discovery

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

૮ >ﻌ< ა ci review

ran on 2021341 — chore: map contributor email

⚠️ Warnings

OSV vulnerability scan · View job

5 known vulnerabilities found in pinned dependencies.

How to fix:

Review the findings in the Security tab. Update the affected dependencies if a patched version is available.


debug info

CI timings

CI timings · View report · View job

Wall time 3m7s vs 3m30s (-11.0%). 12 job(s) slower, 12 faster,

  • Python tests / Run tests slice 4/12: -35.0s
  • Python tests / Run tests slice 12/12: -26.0s
  • Python tests / Run tests slice 11/12: -25.0s
  • Python tests / Run tests slice 7/12: -18.0s
  • Python tests / Run tests slice 8/12: +17.0s

pierrenode and others added 3 commits August 14, 2026 16:38
…direct guard

ActualProfile.fetch_models() overrides ProviderProfile's default
implementation with its own Actual-specific base_url resolution
(ACTUAL_BASE_URL env var, hosted-vs-local normalization), but called raw
urllib.request.urlopen(req, timeout=timeout) directly instead of the base
class's open_credentialed_url(). Every other provider either uses the
base class default or forwards to it via super() and gets
SafeCredentialRedirectHandler for free — Actual is the only provider that
attaches a Bearer token to its own Request object and opens it with the
stdlib's default redirect handling, which forwards every header,
including Authorization, across a cross-origin redirect.

Actual's own feature surface makes the trigger realistic: ACTUAL_BASE_URL
is a first-class, documented way to point this provider at a self-hosted
or local-offline endpoint (see the local-loopback no-auth path already
handled elsewhere in this provider), so a misconfigured or compromised
endpoint 302-ing to another host leaks ACTUAL_API_KEY to it.

Fix: import and call the same open_credentialed_url() the base class
uses, keeping Actual's own URL-resolution logic unchanged.

Adds an end-to-end regression test using two real local HTTP servers (no
mocking of the security module itself) — one redirects, the other
records the Authorization header it receives — mirroring
test_urllib_security.py's own redirect tests. Also repoints the existing
fetch_models test's mock from urllib.request.urlopen to
hermes_cli.urllib_security.open_credentialed_url, since fetch_models no
longer calls the former. Mutation-verified: the new redirect test fails
on pre-fix code with the Authorization header observed at the redirect
target.
@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/cli CLI entry point, hermes_cli/, setup wizard comp/plugins Plugin system and bundled plugins area/auth Authentication, OAuth, credential pools labels Aug 14, 2026
@teknium1
teknium1 merged commit 832eb56 into main Aug 14, 2026
47 checks passed
@teknium1
teknium1 deleted the salv-83554 branch August 14, 2026 23:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/auth Authentication, OAuth, credential pools comp/cli CLI entry point, hermes_cli/, setup wizard comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants