{"id":49537354,"url":"https://github.com/grakz/proxy_settings","last_synced_at":"2026-05-02T12:01:16.558Z","repository":{"id":355187969,"uuid":"1227127567","full_name":"grakz/proxy_settings","owner":"grakz","description":"A small toolkit that makes command-line developer tools (Git, npm, pip, Node.js, and anything that honors HTTPS_PROXY) work on Windows in enterprise environments sitting behind a corporate proxy.","archived":false,"fork":false,"pushed_at":"2026-05-02T11:07:52.000Z","size":132,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-02T11:09:08.822Z","etag":null,"topics":["cmd","enterprise","node","npm","proxy","proxy-configuration","windows"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/grakz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-02T08:40:41.000Z","updated_at":"2026-05-02T11:02:10.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/grakz/proxy_settings","commit_stats":null,"previous_names":["grakz/proxy_settings"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/grakz/proxy_settings","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grakz%2Fproxy_settings","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grakz%2Fproxy_settings/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grakz%2Fproxy_settings/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grakz%2Fproxy_settings/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/grakz","download_url":"https://codeload.github.com/grakz/proxy_settings/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grakz%2Fproxy_settings/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32533344,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-02T11:28:32.350Z","status":"ssl_error","status_checked_at":"2026-05-02T11:27:30.140Z","response_time":132,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cmd","enterprise","node","npm","proxy","proxy-configuration","windows"],"created_at":"2026-05-02T12:01:08.062Z","updated_at":"2026-05-02T12:01:16.550Z","avatar_url":"https://github.com/grakz.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# proxy_settings\n\nA small toolkit that makes command-line developer tools (Git, npm, pip, Node.js, and anything that honors `HTTPS_PROXY`) work on Windows in enterprise environments sitting behind a corporate proxy.\n\n## Quick start\n\nIf you just want it to work and don't need details, this is everything you need.\n\n### Option A — prebuilt binary (no Python needed)\n\nDownload from the [latest GitHub Release](../../releases/latest):\n\n- **`proxy_settings-windows-x64.exe`** — for the vast majority of Windows machines (Intel / AMD).\n- **`proxy_settings-windows-arm64.exe`** — for Windows on ARM (Surface Pro X, Snapdragon laptops, etc.).\n\nDrop it anywhere on your PATH (or just into your home directory), rename to `proxy_settings.exe` if you like, and use it exactly like the Python script — every flag in this README applies:\n\n```\nproxy_settings.exe\n```\n\nThe binary is self-contained: it bundles its own Python interpreter, pywin32, and cryptography. Wherever the README below says `python configure_proxy.py`, you can substitute `proxy_settings.exe`. Skip to step 2.\n\n### Option B — from source\n\n**1. Install dependencies and run once:**\n\n```\npip install pywin32 cryptography certifi\npython configure_proxy.py\n```\n\nThen **restart your terminal** (so the env vars `configure_proxy.py` set via `setx` are visible to new processes). That's it.\n\n**2. If npm/pnpm/yarn later fail with tarball integrity errors** (or any download arrives as HTML instead of bytes), your corporate proxy is using McAfee Web Gateway \"progress pages\" to wrap large downloads. Re-run once with `--mitm` for the offending hosts:\n\n```\npython configure_proxy.py --mitm \"registry.npmjs.org,registry.yarnpkg.com,binaries.prisma.sh\"\n```\n\nThe flag is persisted, so future runs need no arguments. Restart any pnpm/node processes after.\n\n**3. After a reboot**, re-run the same one-liner (no args needed; it picks up your persisted config):\n\n```\npython configure_proxy.py\n```\n\n**To start it automatically on login** (no admin rights required), drop a shortcut into your per-user Startup folder:\n\n1. Press `Win+R`, type `shell:startup`, press Enter. This opens `%APPDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup`.\n2. Create a file there called `proxy_settings.bat` containing one of:\n\n   ```bat\n   @echo off\n   start \"\" \"C:\\full\\path\\to\\proxy_settings.exe\"\n   ```\n\n   …or, if running from source:\n\n   ```bat\n   @echo off\n   start \"\" pythonw \"C:\\full\\path\\to\\configure_proxy.py\"\n   ```\n\n   The daemon detaches from the parent, so the script exits immediately and the proxy keeps running in the background. `pythonw` (in the source variant) runs without a console window.\n\nTo undo everything: `python configure_proxy.py --unset` (or `proxy_settings.exe --unset`).\n\n---\n\n## Contents\n\n- [Why this exists](#why-this-exists)\n- [Files](#files)\n- [Requirements](#requirements)\n- [`configure_proxy.py`](#configure_proxypy)\n  - [Options](#options)\n  - [Detection chain](#detection-chain-what-runs-in-what-order)\n  - [Corporate CA discovery strategy](#corporate-ca-discovery-strategy)\n- [`auth_proxy.py`](#auth_proxypy)\n  - [Subcommands](#subcommands-mutually-exclusive)\n  - [Connection options](#connection-options)\n  - [MITM (TLS interception) options](#mitm-tls-interception-options)\n  - [Diagnostics options](#diagnostics-options)\n  - [How the auth dance works](#how-the-auth-dance-works)\n  - [McAfee progress-page state machine](#mcafee-progress-page-state-machine)\n- [`mitm_handler.py`](#mitm_handlerpy)\n- [Common workflows](#common-workflows)\n- [Troubleshooting](#troubleshooting)\n- [Building the standalone .exe](#building-the-standalone-exe)\n- [License](#license)\n\n---\n\n## Why this exists\n\nIt handles the four things that typically break CLI tools on a managed corporate machine:\n\n1. **Proxy discovery** — the proxy may be set in env vars, the Windows registry (static or `AutoConfigURL`), or only discoverable via DNS WPAD; the proxy URL itself may come from a JavaScript PAC file.\n2. **TLS inspection** — corporate proxies (Zscaler, Netskope, BlueCoat, McAfee, Palo Alto, etc.) re-sign TLS with a private root CA pushed into the Windows trust store. Tools that ship their own CA bundle (Git for Windows, Node.js, pip/certifi) don't trust it and fail with cert errors.\n3. **Proxy authentication** — many corporate proxies require NTLM or Kerberos (Negotiate) auth. Most CLI tools either don't support it at all or need careful per-tool configuration.\n4. **McAfee Web Gateway \"progress pages\"** — when downloading large files (npm tarballs, Prisma binaries, etc.) MWG replaces the real bytes with a \"Please Wait\" HTML page that polls and eventually shows a \"Click here to get the file\" link. Headless tools see HTML where they expect a tarball and fail with integrity errors.\n\nThe toolkit consists of three Python files. **Only two are meant to be invoked directly.**\n\n## Files\n\n| File | Run directly? | Role |\n|---|---|---|\n| `configure_proxy.py` | **Yes** — main entry point | Detects the proxy + corporate CA, writes settings into Git/npm/pip/Node, optionally starts the local auth daemon. |\n| `auth_proxy.py` | **Yes** — usually managed by `configure_proxy.py`, can be run on its own | Local NTLM/Negotiate-handling proxy daemon, optionally with TLS interception for the McAfee workaround. |\n| `mitm_handler.py` | **No** — internal module | Local CA, on-the-fly leaf cert signing, McAfee progress-page state machine. Imported by `auth_proxy.py`. |\n\nState and config live under `~/.config/configure_proxy/`:\n\n- `config.json` — persisted CLI flags from the last successful `configure_proxy.py` run\n- `state.json` — last-detected proxy state\n- `ca-bundle.pem` — combined system + corporate + local-MITM CA bundle pointed at by Git/npm/pip/Node\n- `auth_proxy_ca.pem` / `auth_proxy_ca.key` — local CA generated for MITM mode\n- `auth_proxy.pid` / `auth_proxy.log` — daemon control files\n\n## Requirements\n\n- Python 3.9+ (Python 3.13+ unlocks the most reliable cert-chain capture path).\n- `pywin32` — required by `auth_proxy.py` for SSPI (NTLM/Kerberos via the logged-in Windows credentials, no password storage).\n- `cryptography` — required by `mitm_handler.py` (i.e. only needed if you use `--mitm`).\n- `certifi` — recommended; gives a stable baseline trust store to diff the corporate CA against.\n\n```\npip install pywin32 cryptography certifi\n```\n\nThe toolkit is Windows-first (registry detection, SSPI, `setx`). The detection and CA-bundling code on `configure_proxy.py` will run on Linux/macOS, but `auth_proxy.py` daemon mode and SSPI auth are Windows-only.\n\n## `configure_proxy.py`\n\nTop-level orchestrator. Persists arguments across runs, so most users only ever run it with no flags after the first time.\n\n```\npython configure_proxy.py [options]\n```\n\nWhen run with no flags, it will:\n\n1. Detect the proxy (env vars → registry static → registry `AutoConfigURL` → DNS WPAD → PAC).\n2. Probe whether the proxy demands authentication.\n3. If it does, start `auth_proxy.py` as a background daemon on `127.0.0.1:3128` and point Git, npm, pip and the `HTTPS_PROXY` / `HTTP_PROXY` / `NO_PROXY` env vars at it.\n4. Find the corporate TLS-inspection root CA (preferring the Windows `ROOT` store, falling back to a TLS probe through the proxy), build a combined PEM bundle, and configure Git (`http.sslCAInfo`), npm (`cafile`), pip (`[global] cert`) and Node (`NODE_EXTRA_CA_CERTS`) to use it.\n5. Persist the flags you used to `~/.config/configure_proxy/config.json` so the next run is a no-arg `python configure_proxy.py`.\n\n### Options\n\n#### Proxy detection / override\n\n- `--proxy URL` — Skip detection and use this proxy URL.\n- `--pac-url URL` — Skip PAC discovery and fetch from this URL instead.\n- `--probe-url URL` — URL used both as the input to PAC evaluation and as the destination of the TLS probe used to capture the corporate cert chain. Default: `https://github.com`.\n\n#### Corporate CA discovery\n\n- `--no-ca` — Skip CA discovery entirely. Use this if SSL inspection is not happening on your network.\n- `--ca-bundle-path PATH` — Where to write the combined PEM bundle. Default: `~/.config/configure_proxy/ca-bundle.pem`.\n- `--ca-import PATH` — Path to a manually-exported corporate cert (`.cer`/`.pem`/`.crt`). May be passed multiple times. Use this when auto-detection fails; export the cert from *Internet Options → Content → Certificates → Trusted Root* as Base-64 `.cer`.\n- `--ca-pick SUBSTRING` — When the Windows `ROOT` store contains multiple plausible candidates, auto-select the one whose subject contains this substring (case-insensitive). Useful for non-interactive or scripted runs.\n\n#### Local auth proxy\n\n- `--auth-proxy {auto,always,never}` — Whether to run `auth_proxy.py` as a local daemon in front of the corporate proxy.\n  - `auto` (default): probe the upstream and start the daemon only if it returns 407.\n  - `always`: start unconditionally.\n  - `never`: skip the daemon and configure tools to talk to the corporate proxy directly. Pick this only if the proxy doesn't require auth or if you have another mechanism (e.g. `cntlm`).\n- `--auth-proxy-port N` — Port for the local daemon. Default: `3128`.\n- `--mitm HOSTS` — Comma-separated list of hosts (or `*`) for which the local daemon should terminate TLS to handle McAfee progress pages. Common targets: `registry.npmjs.org,registry.yarnpkg.com,binaries.prisma.sh`. Suffix matching applies, so `npmjs.org` covers `registry.npmjs.org`. Pass `--mitm \"\"` to clear a previously-saved value.\n- `--auth-proxy-debug` — Run the daemon with verbose per-request logging.\n\n#### Environment variables\n\n- `--no-env-proxy` — Skip `setx`-ing `HTTPS_PROXY` / `HTTP_PROXY` / `NO_PROXY`. By default these *are* set so that anything reading them (curl, wget, requests, Prisma, many CLIs) uses the proxy automatically.\n\n#### Run mode\n\n- `--dry-run` — Show what would be done without touching anything.\n- `--unset` — Reverse everything: remove Git/npm/pip CA + proxy settings, delete env vars (Windows: via `reg delete`), stop the daemon, delete the persisted config.\n- `--show-config` / `--reset-config` — Inspect or wipe the persisted config and exit.\n- `--no-save` — Don't persist this run's flags.\n- `-v` / `--verbose` — Show DNS lookups, TLS probe details, certificate diffs.\n\n### Detection chain (what runs in what order)\n\n```\nHTTPS_PROXY / HTTP_PROXY env vars (loopback addresses ignored — those are us from a previous run)\n    ↓ none\nWindows registry: ProxyEnable + ProxyServer\n    ↓ none\nWindows registry: AutoConfigURL (PAC URL set by GPO)\n    ↓ none\nDNS WPAD: walk DNS-suffix list, look up wpad.\u003cdomain\u003e, fetch wpad.dat\n    ↓ none\ngive up\n```\n\nPAC files (JavaScript) are evaluated with Node.js if present on `PATH`; otherwise a small built-in Python evaluator handles the common subset (`shExpMatch`, `dnsDomainIs`, `isInNet`, `isPlainHostName`, `dnsResolve`, `myIpAddress`, `isResolvable`, `dnsDomainLevels`, plus `||` / `\u0026\u0026` / `!`).\n\n### Corporate CA discovery strategy\n\nIn order:\n\n1. **`--ca-import`** — anything passed explicitly wins.\n2. **Windows `ROOT` store diff** — list certs in the Windows `ROOT` store that aren't in `certifi`, filter out Microsoft OS roots and non-self-signed entries, score the rest by name (Zscaler/BlueCoat/Netskope/etc. → high score) and recency. If multiple candidates remain, do a TLS probe through the proxy and pick the one whose Subject Key Identifier matches the leaf's Authority Key Identifier. Falls back to interactive prompt if the probe is inconclusive.\n3. **TLS probe** — connect through the proxy to `--probe-url` (default `https://github.com`) with verification disabled, capture the chain (Python ≥ 3.13's `get_unverified_chain` → `openssl s_client -proxy …` → leaf-only fallback), keep certs not in `certifi`. This path can fail on NTLM proxies if `auth_proxy.py` isn't already in front.\n\nThe combined bundle = `certifi` baseline + corporate CA(s) + the local `auth_proxy` MITM CA (if it exists). The MITM CA is also re-appended idempotently on subsequent runs, so creating it after the first run doesn't require rebuilding everything.\n\n## `auth_proxy.py`\n\nA local HTTP/HTTPS proxy that listens on `127.0.0.1` (default port `3128`), accepts plain unauthenticated requests from local tools, and forwards them upstream to the corporate proxy — performing NTLM or Negotiate (Kerberos with NTLM fallback) authentication on the way using Windows SSPI. This means tools never see the auth dance and your password is never stored anywhere; SSPI uses the credentials of the logged-in Windows user.\n\nUsually managed by `configure_proxy.py`, but you can drive it directly:\n\n```\npython auth_proxy.py --start --upstream http://corp.proxy:8080 [options]\npython auth_proxy.py --status\npython auth_proxy.py --stop\n```\n\n### Subcommands (mutually exclusive)\n\n- `--start` — Detached background daemon. Writes the PID to `~/.config/configure_proxy/auth_proxy.pid` and logs to `auth_proxy.log` next to it. On Windows uses `DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP`; on POSIX uses `fork()` + `setsid()`. Verifies the daemon is accepting connections before returning.\n- `--stop` — Kill the running daemon (TerminateProcess on Windows, SIGTERM on POSIX) and remove the PID file.\n- `--status` — Exit 0 if the daemon is running, 1 otherwise.\n- `--serve` — Run in the foreground (used internally by `--start`; useful for debugging).\n- `--diagnose URL` — Fetch `URL` through the running daemon and report bytes received, sha512, and any truncation. Pair with `--expect-sha512 HASH` (raw hex or `sha512-…` base64) to verify against an expected hash. Use this when chasing pnpm tarball-integrity errors.\n\nIf no subcommand is given, the script runs in the foreground (same as `--serve`).\n\n### Connection options\n\n- `--upstream URL` — Corporate proxy URL. Required for `--start`, `--serve`, and the default foreground mode.\n- `--port N` — Listening port. Default: `3128`.\n- `--bind ADDR` — Bind address. Default: `127.0.0.1`.\n\n### MITM (TLS interception) options\n\n- `--mitm HOSTS` — Comma-separated list of hosts (suffix-matched) or `*` for all. The daemon terminates TLS for these hosts using a leaf cert signed by its local CA, inspects responses, and follows McAfee Web Gateway progress-page redirects/links so the client receives real file bytes. Requires `cryptography`.\n- `--mitm-print-ca` — Print the path to `auth_proxy_ca.pem` and exit. The cert at that path must be in the CA bundle clients use (`configure_proxy.py` does this automatically; otherwise import it manually).\n- `--mitm-check` — Diagnostic: confirm the MITM CA exists, that `NODE_EXTRA_CA_CERTS` is set in the current process, and that the bundle file actually contains the CA. Run this when MITM-enabled requests hang during the client TLS handshake or when pnpm/npm doesn't send a request after CONNECT (almost always: client doesn't trust the local CA, often because the shell was started before `setx` updated `NODE_EXTRA_CA_CERTS`).\n\n### Diagnostics options\n\n- `--debug` — Verbose per-request logging: every request line, headers in both directions, every TLS milestone, every MWG poll. Off by default.\n- `--expect-sha512 HASH` — Used with `--diagnose`.\n\n### How the auth dance works\n\nFor HTTPS (CONNECT):\n\n```\nclient → auth_proxy:    CONNECT host:443\nauth_proxy → corp:      CONNECT host:443                          (round 1)\ncorp → auth_proxy:      407 Proxy-Authenticate: Negotiate, NTLM\nauth_proxy → corp:      CONNECT host:443 + SSPI Type1 token       (round 2)\ncorp → auth_proxy:      407 Proxy-Authenticate: Negotiate \u003cType2\u003e\nauth_proxy → corp:      CONNECT host:443 + SSPI Type3 response    (round 3)\ncorp → auth_proxy:      200 Connection established\nauth_proxy → client:    200 Connection established\n        ↕ raw byte splicing in both directions for the TLS stream ↕\n```\n\nThe full handshake stays on a single TCP connection (NTLM is connection-bound). For plain HTTP, the same dance happens on the request itself with `Proxy-Authorization` headers.\n\nFor MITM hosts, instead of splicing raw bytes, `auth_proxy` terminates TLS with a per-host leaf cert (signed by its local CA, generated and cached lazily), opens a separate TLS connection upstream verified against the system trust store, and proxies HTTP requests one at a time — intervening when responses look like McAfee progress pages.\n\n### McAfee progress-page state machine\n\nImplemented in `mitm_handler.py`. When MWG intercepts a download it replaces the response with one of:\n\n- **WAITING page** — HTML with a JS progress meter; the JS polls `?a=1\u0026\u003cts\u003e` every 3s.\n- **POLL response** — small `text/plain` body like `1234567;7000000;30;0;0` (`downloaded;total;percent;ready;scan_seconds`).\n- **READY page** — HTML with `\u003ca href=\"…\u0026dl\"\u003eClick here to get the file\u003c/a\u003e`.\n\nThe handler detects which state the upstream is in (either via a 307 to `/mwg-internal/…/progress` or by classifying the body), polls for ready (up to 10 minutes), follows the `\u0026dl` link, and returns the real bytes to the client with synthesized headers (correct `Content-Length`, `Content-Type` preserved). It also rewrites chunked responses to use `Content-Length` framing because the body has already been buffered, which prevents pnpm/undici from RST-ing on a \"chunked\" stream that doesn't actually have chunk markers.\n\n## `mitm_handler.py`\n\nInternal module. Not meant to be invoked directly. Imported by `auth_proxy.py` when `--mitm` is in use. Provides:\n\n- `CertAuthority` — loads or creates the local CA at `~/.config/configure_proxy/auth_proxy_ca.pem`, signs per-host leaf certs on the fly with both `host` and `*.host` SANs, caches them in memory and on disk under `~/.config/configure_proxy/leaves/`.\n- `mitm_handle_connect()` — TLS-terminates the client side, opens an upstream TLS connection (verified against the system trust store), and proxies HTTP one request at a time.\n- `classify_mcafee_response()` / `_handle_mcafee_progress()` — the McAfee state machine described above.\n- `compute_ca_fingerprint()` / `check_ca_trust_status()` — diagnostics used by `auth_proxy.py --mitm-check`.\n\n## Common workflows\n\n**First-time setup on a managed machine**\n\n```\npip install pywin32 cryptography certifi\npython configure_proxy.py\n# restart terminal so setx-ed env vars are visible\n```\n\n**npm/pnpm fails with tarball integrity errors**\n\nThe corporate proxy is replacing tarballs with McAfee progress pages. Re-run with MITM enabled for the affected hosts:\n\n```\npython configure_proxy.py --mitm \"registry.npmjs.org,registry.yarnpkg.com,binaries.prisma.sh\"\n# restart any pnpm/node processes\n```\n\nTo verify in isolation:\n\n```\npython auth_proxy.py --diagnose https://registry.npmjs.org/some/pkg/-/pkg-1.2.3.tgz \\\n                    --expect-sha512 sha512-AbCdEf...\n```\n\n**Auto-detection picked the wrong corporate CA**\n\n```\n# list candidates and pick by substring\npython configure_proxy.py --ca-pick \"Zscaler\"\n# or import an exported .cer manually\npython configure_proxy.py --ca-import C:\\Users\\me\\corp-root.cer\n```\n\n**Tear everything down**\n\n```\npython configure_proxy.py --unset\n```\n\nRemoves Git/npm/pip CA + proxy settings, removes the env vars, stops the daemon, deletes the persisted config. The CA bundle file and the local CA on disk are left in place.\n\n**Inspect what's persisted**\n\n```\npython configure_proxy.py --show-config\n```\n\n## Troubleshooting\n\n- **`auth_proxy listening … forwarding to …` but tools still get 407** — `HTTPS_PROXY` is probably still pointing at the corporate proxy, not at `127.0.0.1:3128`. `setx` only affects new processes; restart the shell.\n- **MITM enabled, client hangs after TLS handshake** — client doesn't trust the local CA. Run `python auth_proxy.py --mitm-check`. Common cause: the shell was started before `NODE_EXTRA_CA_CERTS` was set; restart it.\n- **`upstream wants Basic auth`** — `auth_proxy.py` deliberately refuses to handle Basic. Embed credentials in the upstream URL (`http://user:pass@proxy:8080`) if you really need this — but normally Basic on a corporate proxy is a misconfiguration, not the intended path.\n- **`could not capture cert chain: proxy CONNECT failed`** during CA discovery — the proxy is rejecting the unauthenticated probe. Either pass `--auth-proxy always` so the probe goes through the local daemon, or use `--ca-import` to supply the cert manually.\n- **PAC evaluation fails** — install Node.js (`node` on `PATH`) so PAC files can be evaluated by a real JS engine instead of the built-in Python subset.\n- **Daemon log** — `~/.config/configure_proxy/auth_proxy.log`. Re-run with `--auth-proxy-debug` (or restart the daemon directly with `--debug`) for per-request detail.\n\n## Building the standalone .exe\n\nThe Windows binaries in [GitHub Releases](../../releases/latest) are built from this repo by [`build/build.sh`](build/build.sh) using PyInstaller `--onefile` plus UPX. PyInstaller produces a binary for whatever architecture the build host is, so the same script run on an x64 machine yields the x64 release asset, and run on a Windows ARM machine yields the ARM64 asset.\n\nTo build it yourself:\n\n```bash\n# in Git Bash on a Windows machine with Python 3.10+ on PATH\n./build/build.sh\n# output: build/dist/proxy_settings.exe (native architecture of the host)\n```\n\nThe script:\n\n1. Creates a venv under `build/.venv/` and installs `pyinstaller`, `pywin32`, `cryptography`, and `certifi` into it.\n2. Runs PyInstaller with [`build/proxy_settings_entry.py`](build/proxy_settings_entry.py) as the entry point — a tiny dispatcher that routes into either `configure_proxy.main()` or, when the first arg is the sentinel `__auth_proxy__`, `auth_proxy.main()`. This is how a single bundled exe doubles as both the configurator and the auth daemon.\n3. Excludes stdlib modules the project doesn't import (`tkinter`, `unittest`, `pydoc`, `asyncio`, `multiprocessing`, …) to trim a few MB.\n4. If `upx` is on PATH, hands it to PyInstaller for compression. Without UPX the binary is roughly 30–50% larger but functionally identical. Install with `choco install upx -y` or `scoop install upx`.\n\nReleases are produced automatically by [`.github/workflows/release.yml`](.github/workflows/release.yml): push a tag matching `v*` (e.g. `git tag v1.0.0 \u0026\u0026 git push --tags`) and the workflow runs a matrix build on `windows-latest` (x64) and `windows-11-arm` (ARM64), attaching `proxy_settings-windows-x64.exe` and `proxy_settings-windows-arm64.exe` to a GitHub Release named after the tag. Manual runs via the Actions tab produce both binaries as downloadable workflow artifacts without creating a release.\n\n### A note on the ARM64 build\n\npyca/cryptography stopped publishing Windows ARM64 wheels after version 46.0.0 ([issue #14168](https://github.com/pyca/cryptography/issues/14168)) and shows no sign of bringing them back. To avoid being permanently pinned to an ageing release on ARM64, the workflow's ARM64 matrix entry installs OpenSSL via vcpkg and compiles `cryptography` from source against it (recipe borrowed from [snowflakedb/universal-driver#984](https://github.com/snowflakedb/universal-driver/pull/984)). The `OPENSSL_*` env vars and the `BUILD_CRYPTOGRAPHY_FROM_SOURCE=1` flag in [`build/build.sh`](build/build.sh) wire this together. The vcpkg install tree is cached so that, after the first run, ARM64 builds avoid the 10–15 minute OpenSSL compile.\n\nThe x64 build is unaffected — it always uses the official wheel.\n\n## License\n\nMIT — see [LICENSE](LICENSE). Copyright © 2026 Grakz.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgrakz%2Fproxy_settings","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgrakz%2Fproxy_settings","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgrakz%2Fproxy_settings/lists"}