feat: credential proxy daemon — zero-knowledge HTTP/HTTPS broker (#4656) - #4695
Closed
dsr-restyn wants to merge 5 commits into
Closed
dsr-restyn wants to merge 5 commits into
dsr-restyn wants to merge 5 commits into
Conversation
Open
1 task
dsr-restyn
force-pushed
the
feat/cred-proxy
branch
from
April 3, 2026 18:37
0997e34 to
9757a91
Compare
Adds a credential proxy subsystem that intercepts outbound HTTP/HTTPS from tool subprocesses and substitutes hermes-proxy://<name> placeholders with real credential values. The agent process structurally cannot read stored values — there is no read API. New files (cred_proxy/): - store.py: AES-256-GCM encrypted store, machine-derived key, chmod 600 - ca.py: local CA for HTTPS MITM, per-hostname cert issuance - substitutor.py: hermes-proxy:// placeholder detection + substitution - server.py: asyncio HTTP/HTTPS MITM proxy, Unix socket + TCP port - daemon.py: process lifecycle, PID/port files, stale PID cleanup - cli.py: hermes-cred-proxy start/stop/status/add/list Minimal additions to existing files: - tools/environments/local.py: inject http_proxy/https_proxy/CA bundle into subprocess env when proxy is running (additive only) - hermes_cli/main.py: cred-proxy subcommand group (additive only) - pyproject.toml: cred-proxy extras + entry point 23 tests passing. Closes NousResearch#4656
…yring - server.py: replace 459-line asyncio MITM with mitmproxy DumpMaster + CredentialProxyAddon - store.py: replace AES-256-GCM with keyring (service: hermes-cred-proxy) - ca.py: deleted — mitmproxy manages its own CA - daemon.py: remove Unix socket, start mitmproxy via asyncio.run(run_proxy()) - local.py: update CA cert path to mitmproxy-ca-cert.pem - pyproject.toml: swap cryptography+aiohttp for mitmproxy+keyring - tests: update for keyring in-memory backend, addon unit tests 22 tests passing.
daemon.py and server.py both used Path.home() / '.hermes' directly, breaking multi-profile setups where HERMES_HOME points to a profile subdirectory. Replace with get_hermes_home() from hermes_constants so the proxy state dir and CA dir respect the active profile.
Adds a config option for proxy-brokered credential placeholders (NousResearch#4429). Sandboxed runtimes like NVIDIA OpenShell use credential brokering where env vars contain placeholders and a network proxy rewrites HTTP headers with real tokens on egress. Hermes strips these as if they were real secrets, breaking tools that depend on the placeholder being present. Config: terminal: proxy_credentials: - SLACK_BOT_TOKEN - SLACK_APP_TOKEN Listed vars bypass the provider-secret blocklist via the existing env_passthrough mechanism. Output redaction still applies. Registration is config-driven (not agent-controllable) and runs once, guarded by a module-level flag set after the try block completes. Changes: hermes_cli/config.py | +5 proxy_credentials config key tools/environments/local.py | +26 one-time registration in both _sanitize_subprocess_env and _make_run_env 31 additions, 0 deletions. No existing code modified. Addresses NousResearch#4429 Part of NousResearch#410 (Secure Secrets Management umbrella)
…env blocklist Vars whose value starts with 'hermes-proxy://' are credential proxy placeholders, not real secrets. They must reach the cred-proxy daemon in subprocess env so the proxy can substitute the real value at the HTTP transport layer. Without this, a var like: CLOUDFLARE_API_TOKEN=hermes-proxy://cf_token would be stripped by _sanitize_subprocess_env even though it carries no real credential value. Real secret values on blocked keys are still stripped as before. Tests: 2 new cases in test_env_passthrough.py
dsr-restyn
force-pushed
the
feat/cred-proxy
branch
from
April 3, 2026 20:47
57094f7 to
f720e39
Compare
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
hermes-proxy://<name>placeholders with real credential values at the transport layerArchitecture
Changes
New files (
cred_proxy/)store.py_get()for substitutor only. No read API.server.pyDumpMaster+CredentialProxyAddon— substitutes placeholders in request headers and bodies. ~70 lines.substitutor.pyhermes-proxy://<name>detection and replacement.daemon.pycli.pyModified files
tools/environments/local.py_make_run_env()injectshttp_proxy/https_proxy+ combined CA bundle when proxy is runninghermes_cli/main.pyhermes cred-proxysubcommand grouppyproject.tomlcred-proxyextras + console entry pointDesign decisions
server.pyto ~70 lines.keyringlibrary. No key derivation, no passphrase prompt, no custom crypto.get_hermes_home()so proxy state respectsHERMES_HOME/ active profile.Tests
22 tests across 4 files:
test_store.py— CRUD, sorted listing, overwrite, missing key errorstest_substitutor.py— header/body/query string substitution, mixed known/unknown, empty stringtest_integration.py— mitmproxy addon end-to-end, no-public-get invarianttest_fixes.py— stale PID file cleanupAll tests use an in-memory keyring backend (no system keyring access needed).
Related
Refs #4656, addresses #4429, complements #4432
🤖 Generated with Claude Code