ProxyShield local agent for selective AI egress interception and masking.
Project description
ProxyShield Agent (V1)
ProxyShield Agent is the Windows-first background service that selectively intercepts outbound HTTPS traffic destined for AI tools (Copilot, ChatGPT/OpenAI, Claude) and masks sensitive keywords pulled from the ProxyShield desktop app keychain. It runs a local explicit proxy, publishes a PAC file, and exposes a small local API for health/config/logs.
Capabilities
- Explicit proxy on
127.0.0.1:3128with CONNECT+TLS MITM for AI endpoints only. - PAC server on
127.0.0.1:3129/proxy.pacthat routes only AI domains through the proxy; everything else isDIRECT. - Masking of URLs, headers, JSON bodies, and x-www-form-urlencoded bodies using keywords stored by the desktop app (
service=saibre, accounts likeentry:<uuid>in the OS keychain). - Local API (
/health,/config,/keywords,/logs/recent,/pac) served over HTTP for debugging and control, includingPOST /keywordsfor desktop keyword sync. - Policy API on
127.0.0.1:3130for keyword policy status/updates, protected by anX-ProxyShield-Tokenheader (token stored atC:\ProgramData\ProxyShield\config\ui_token.dpapi, override withPROXYSHIELD_UI_TOKENfor dev). - Windows helpers to install/uninstall the ProxyShield Root CA, set/clear system PAC AutoConfigURL, and manage proxy-related environment variables.
proxyshield-agent doctorto validate PAC, env vars, CA install, and connectivity to the main AI endpoints.- CLI helpers to install/uninstall the OS auto-start service (
proxyshield-agent install-service,uninstall-service,enable-autostart,disable-autostart). - macOS installer attempts to expose bundled agent as
ProxyShieldAgentvia/usr/local/bin/ProxyShieldAgentsymlink.
OpenClaw integration direction
- The agent no longer exposes a dedicated OpenClaw HTTP scan endpoint.
- OpenClaw support is being reworked around an in-process wrapper architecture rather than an endpoint layered onto the agent API.
- The standard local control endpoints remain available for agent operations:
/health,/config,/keywords,/logs/recent, and/pac.
Layout
proxyshield_agent/
pyproject.toml
proxyshield_agent/
main.py
service.py
proxy_runtime.py
masking.py
keyword_source.py
pac.py
windows/
trust_store.py
system_proxy.py
env_vars.py
tests/
test_masking.py
test_pac.py
Development
- Install deps (from repo root):
python -m venv .venv source .venv/bin/activate # PowerShell: .venv\Scripts\Activate.ps1 pip install -r requirements.txt
- Install the CLI entrypoint into the venv:
pip install -e proxyshield_agent # from repo root # or, if you're already in the proxyshield_agent directory: pip install -e .
- Run unit tests:
pytest proxyshield_agent/tests - Run macOS-specific tests (marked with
macos) locally on macOS:pytest proxyshield_agent/tests -m macos
- Run macOS-specific tests in CI on a macOS runner:
pytest proxyshield_agent/tests -m macos
On non-macOS CI runners, skip macOS-only tests:pytest proxyshield_agent/tests -m "not macos"
- Run the agent API (dev) with uvicorn:
uvicorn proxyshield_agent.api:app --reload --port 8787
- Run the full agent in dev mode (foreground):
python -m proxyshield_agent.main enable --dev
If proxyshield-agent still reports "command not found", confirm the venv is active and run:
python -m proxyshield_agent enable --dev
If the console script exists but errors with ModuleNotFoundError: No module named 'proxyshield_agent',
reinstall the package in the active venv:
python -m pip install -e . # run from proxyshield_agent/ directory
python -m pip show proxyshield-agent
On Windows, also ensure you're running the command from the repo root or the
proxyshield_agent/ project directory (not the inner proxyshield_agent/ package
folder), and force-reinstall the entrypoint if needed:
python -m pip install -e .\proxyshield_agent --force-reinstall
Get-Command proxyshield-agent
Service management (CLI)
The agent ships cross-platform commands to register and manage auto-start behavior:
- Install the service/LaunchAgent (autostart enabled by default):
proxyshield-agent install-service - Disable autostart:
proxyshield-agent disable-autostart - Re-enable autostart if needed:
proxyshield-agent enable-autostart - Remove the service/LaunchAgent:
proxyshield-agent uninstall-service
On Windows, the installer uses sc.exe and configures the service to restart on failure. On macOS, the CLI installs the LaunchAgent plist (based on scripts/com.saibre.proxyshield.agent.plist) with RunAtLoad and KeepAlive enabled.
The onboarding scripts (scripts/windows_onboard.ps1 and scripts/macos_onboard.sh) also register autostart automatically, so the CLI is optional if you're following those flows.
Packaging
- Build a wheel:
pip wheel .from theproxyshield_agentdirectory. - The Windows installer (PowerShell/Inno Setup) should place binaries under Program Files, generate/install the CA in
%ProgramData%\ProxyShield\ca, set PAC AutoConfigURL, and register the service for autostart. See/docs/V1-onboarding.mdfor scripts and steps. On macOS, data lives under~/Library/Application Support/ProxyShield.
macOS packaging (PyInstaller)
We package the macOS agent as a standalone PyInstaller binary that the desktop app bundles inside the app bundle and launches directly (for example at ProxyShield.app/Contents/Resources/ProxyShieldAgent). This keeps the agent version locked to the desktop build and avoids requiring a separate pip/pipx install.
Build steps (macOS):
- From the repo root:
python -m venv .venv source .venv/bin/activate pip install -r requirements.txt pip install pyinstaller
- (Optional) Preseed assets to bundle into the binary:
- Add a
config.jsonand/or aca/directory underproxyshield_agent/packaging/seed/. - These will be copied into
~/Library/Application Support/ProxyShieldon first run if missing.
- Add a
- Build the agent binary:
cd proxyshield_agent pyinstaller packaging/ProxyShieldAgent.spec
- The output binary will be at
proxyshield_agent/dist/ProxyShieldAgent. Bundle this file into the desktop app.
Runtime behavior:
- When frozen on macOS, the agent sets its working directory to
~/Library/Application Support/ProxyShieldand continues to write config/logs/CA assets there. - The agent stores config at
~/Library/Application Support/ProxyShield/config.json, CA assets under~/Library/Application Support/ProxyShield/ca/, and logs at~/Library/Application Support/ProxyShield/logs/agent.log.
Notes
- Only AI domains are routed through the proxy; general browsing remains direct because the PAC returns
DIRECTfor everything else. - If an app breaks, add its domains to
bypass_domainsin%ProgramData%\ProxyShield\config.json(Windows) or~/Library/Application Support/ProxyShield/config.json(macOS), or use the "Launch Unprotected" wrapper that clears proxy env vars for that process.
Quick validation without diagnose
Use these checks when the agent appears to run but traffic is not being intercepted.
-
Confirm LaunchAgent is loaded (macOS):
launchctl print "gui/$(id -u)/com.saibre.proxyshield.agent" | head -n 40
-
Confirm PAC endpoint is reachable:
curl -sv "http://127.0.0.1:3129/proxy.pac" | head -n 20
-
Confirm macOS network service uses the PAC URL:
networksetup -listallnetworkservices networksetup -getautoproxyurl "Wi-Fi"
Enabled: YesandURL: http://127.0.0.1:3129/proxy.pacshould be present on the active service. -
Confirm proxy listener is accepting connections:
nc -vz 127.0.0.1 3128
-
Force-test a proxied request path manually:
curl -x http://127.0.0.1:3128 -I https://api.openai.com
If this fails, the local explicit proxy path is not healthy yet.
-
Verify masking engine sees your stored keywords:
proxyshield-agent keyword-list proxyshield-agent mask-test --sample "my-secret-value"
mask-testoutput includestierandenabled_detectorsso you can confirm whether entropy/API-key heuristics are active (Pro+) or only keyword masking is enabled (Free). -
Tail agent logs while sending a test request:
tail -f "${HOME}/Library/Application Support/ProxyShield/logs/agent.log"
Then trigger traffic to an AI domain. If nothing appears, traffic is likely not entering the local proxy.
macOS policy + token storage
- Machine policy keywords are stored as an encrypted payload at
/Library/Application Support/ProxyShield/policy/machine.dpapi. - User overlay keywords are stored as an encrypted payload at
~/Library/Application Support/ProxyShield/policy/user.dpapifor the active policy user. - To keep policy visible to system-account processes (for example LaunchDaemons running as root), the agent also writes a synchronized encrypted user copy at
/Library/Application Support/ProxyShield/policy/users/<uid>/user.dpapi. - All policy files above are encrypted at rest using the macOS DPAPI helper (
proxyshield_agent.macos.dpapi) and never persisted as plaintext JSON. - The policy API UI token on macOS is stored at
/Library/Application Support/ProxyShield/config/ui_token.enc(override path withPROXYSHIELD_UI_TOKEN_PATH, or bypass file storage in dev withPROXYSHIELD_UI_TOKEN). - Legacy keychain-based UI tokens are migrated automatically on first read into
ui_token.encand then removed from keychain.
Resetting or migrating policy/UI token state (macOS)
- Reset policy UI token (forces token regeneration on next agent start):
sudo rm -f "/Library/Application Support/ProxyShield/config/ui_token.enc"
- Migrate user policy from home-only storage to a system-visible copy (safe to run repeatedly):
sudo python3 -m proxyshield_agent.main policy status-dump >/dev/null
Loading policy triggers sync from~/Library/.../policy/user.dpapito/Library/.../policy/users/<uid>/user.dpapiwhen needed.
Troubleshooting (permissions + policy visibility)
- If onboarding fails with
Operation not permittedorPermission denied, re-run onboarding with an admin account and ensure/Library/Application Support/ProxyShieldis root-writable:sudo mkdir -p "/Library/Application Support/ProxyShield" sudo chown -R root:wheel "/Library/Application Support/ProxyShield" sudo chmod -R u+rwX,g+rX "/Library/Application Support/ProxyShield"
- If policy appears empty when the agent runs as a system account, check both user and system policy copies exist and are non-empty:
ls -l "${HOME}/Library/Application Support/ProxyShield/policy/user.dpapi" sudo ls -l "/Library/Application Support/ProxyShield/policy/users/$(id -u)/user.dpapi"
- Verify effective policy source/visibility:
proxyshield-agent policy status-dump
Confirmsource_usedcontains expected entries (machine,user) and counts are non-zero when policy is present.
Configuration flow (UI -> Agent)
- The desktop UI sends a full config payload to
POST /configon the local agent API. - The agent validates
allowed_domainsandbypass_domainsas lists of strings, trims whitespace, and removes duplicates before persisting to%ProgramData%\ProxyShield\config.json(Windows) or~/Library/Application Support/ProxyShield/config.json(macOS). - The in-memory config used by
GET /configand PAC generation is updated immediately after a successfulPOST /config. - The proxy runtime keeps a lightweight reference to the latest config; masking and PAC rules will reflect the new lists without restarting the agent.
Masking configuration (PHI patterns)
Enterprise tenants can inject additional regex patterns for PHI MRNs and member IDs via the masking config object. The patterns are compiled into regex detections and mapped to the PHI redaction labels.
Example:
{
"masking": {
"tier": "enterprise",
"mrn_patterns": ["\\bMRN\\d{6,10}\\b", "\\b\\d{8}\\b"],
"member_id_patterns": ["\\bID-[A-Z0-9]{6,12}\\b"]
}
}
Notes:
- The patterns are interpreted as Python regular expressions.
- They are appended to the existing regex detector policy, so the regex detector must be enabled for the active tier.
Integrations (1Password)
- Integrations are enabled through the
integrationsconfig object and only run when keywords are loaded. - The 1Password provider relies on the
opCLI and either a service account token or a desktop session token. - Use
GET /integrationsorGET /integrations/statusto see registered integrations and their last known status.
Example configuration:
{
"integrations": {
"onepassword": {
"enabled": true,
"settings": {
"vaults": "My Vault,another-vault-id",
"account": "my",
"token_ref": "prod"
}
}
}
}
Authentication options:
- Service account: set
OP_SERVICE_ACCOUNT_TOKENor store it under the keychain entryproxyshield.onepasswordwithtoken_ref. - Desktop session: set
OP_SESSIONorOP_SESSION_<account>; the agent will store the token in the OS keychain for reuse.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file saibre-0.1.2.tar.gz.
File metadata
- Download URL: saibre-0.1.2.tar.gz
- Upload date:
- Size: 104.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
65c97b303911853ad18e51f2eb76f893ff223db9fff6ec93b566132bd64b8fd4
|
|
| MD5 |
0216089e93cf58323ef49206dbf0d992
|
|
| BLAKE2b-256 |
4a122a2bd9d61ca195e622cc863bdb966075ccf37ebdf4f9dce3905df72345ac
|
Provenance
The following attestation bundles were made for saibre-0.1.2.tar.gz:
Publisher:
saibre-pypi-release.yml on rogerbalcells/saibre
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
saibre-0.1.2.tar.gz -
Subject digest:
65c97b303911853ad18e51f2eb76f893ff223db9fff6ec93b566132bd64b8fd4 - Sigstore transparency entry: 1304428956
- Sigstore integration time:
-
Permalink:
rogerbalcells/saibre@3ee15670c68f9f22733e5345461c397aa72a3db2 -
Branch / Tag:
refs/tags/saibre-v0.1.2 - Owner: https://github.com/rogerbalcells
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
saibre-pypi-release.yml@3ee15670c68f9f22733e5345461c397aa72a3db2 -
Trigger Event:
push
-
Statement type:
File details
Details for the file saibre-0.1.2-py3-none-any.whl.
File metadata
- Download URL: saibre-0.1.2-py3-none-any.whl
- Upload date:
- Size: 118.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
166c9a15f05f9d8f7ea16b70d2fd869a345058c9cda7d08dfdc0314a76aaf12b
|
|
| MD5 |
54250f6a6075ff4a89111ab838ce77ed
|
|
| BLAKE2b-256 |
14b4aa61f97a840e3e26f20332491356d16d2a16c50ee3c934f33a0da42010b0
|
Provenance
The following attestation bundles were made for saibre-0.1.2-py3-none-any.whl:
Publisher:
saibre-pypi-release.yml on rogerbalcells/saibre
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
saibre-0.1.2-py3-none-any.whl -
Subject digest:
166c9a15f05f9d8f7ea16b70d2fd869a345058c9cda7d08dfdc0314a76aaf12b - Sigstore transparency entry: 1304429065
- Sigstore integration time:
-
Permalink:
rogerbalcells/saibre@3ee15670c68f9f22733e5345461c397aa72a3db2 -
Branch / Tag:
refs/tags/saibre-v0.1.2 - Owner: https://github.com/rogerbalcells
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
saibre-pypi-release.yml@3ee15670c68f9f22733e5345461c397aa72a3db2 -
Trigger Event:
push
-
Statement type: