Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ make docker # Multi-stage Docker image; data persisted at /data/.agent-vau

## Core concepts (mental model)

- **Single ingress into the broker — transparent MITM** (on by default, port 14322, disable with `--mitm-port 0`): TLS-encrypted HTTPS_PROXY-compatible ingress backed by [internal/mitm](internal/mitm/) + [internal/ca](internal/ca/) (software CA, root key encrypted with the master key). The listener itself is TLS-wrapped (cert signed by the MITM CA) so the CONNECT handshake carrying the session token is encrypted. Clients use `HTTPS_PROXY=https://...`. Credential injection lives in `brokercore`. HTTP/1.1 at the ingress, with transparent WebSocket upgrade support (HTTP/2 not yet). Bind failures are non-fatal — the core HTTP server keeps running.
- **Single ingress into the broker — transparent MITM** (on by default, port 14322, disable with `--mitm-port 0`): TLS-encrypted HTTPS_PROXY/HTTP_PROXY-compatible ingress backed by [internal/mitm](internal/mitm/) + [internal/ca](internal/ca/) (software CA, root key encrypted with the master key). The listener is TLS-wrapped (cert signed by the MITM CA) and accepts both `CONNECT host:port` (HTTPS upstreams) and absolute-form forward-proxy requests (`POST http://host/path HTTP/1.1`, RFC 7230 §5.3.2) for plain-HTTP upstreams on the same port. Clients use `HTTPS_PROXY=https://...` and `HTTP_PROXY=https://...` — both point at the same TLS-wrapped proxy URL. Credential injection lives in `brokercore`. HTTP/1.1 at the ingress, with transparent WebSocket upgrade support (HTTP/2 not yet). Bind failures are non-fatal — the core HTTP server keeps running.
- **Proposals = GitHub-PR-style change requests.** Agents cannot edit services or credentials directly; they create proposals, a human approves in CLI or browser, and apply merges atomically. Per-vault sequential IDs. 7-day TTL.
- **Two independent permission axes**:
- Instance role: `owner` vs `member` (applies to both users and agents).
- Vault role: `proxy` < `member` < `admin`. Proxy can use the proxy and raise proposals; member can manage credentials/services; admin can invite humans.
- **KEK/DEK key wrapping**: A random DEK (Data Encryption Key) encrypts credentials and the CA key at rest (AES-256-GCM). If a master password is set, Argon2id derives a KEK (Key Encryption Key) that wraps the DEK; changing the password re-wraps the DEK without re-encrypting credentials. If no password is set (passwordless mode), the DEK is stored in plaintext — suitable for PaaS deploys where volume security is the trust boundary. Login uses email+password. The first user to register becomes the instance owner and is auto-granted vault admin on `default`.
- **Agent skills are the agent-facing contract.** [cmd/skill_cli.md](cmd/skill_cli.md) and [cmd/skill_http.md](cmd/skill_http.md) are embedded into the binary, installed by `vault run`, and served publicly at `/v1/skills/{cli,http}`. They are the authoritative reference for what agents can do.
- **Two isolation modes for `vault run`** (selected via `--isolation` or `AGENT_VAULT_ISOLATION`): `host` (default, cooperative — fork+exec on the host with `HTTPS_PROXY` envvars) and `container` (non-cooperative — Docker container with iptables egress locked to the Agent Vault proxy). Container mode lives in [internal/isolation/](internal/isolation/) with an embedded Dockerfile + init-firewall.sh + entrypoint.sh, built on first use and cached by content hash.
- **Two isolation modes for `vault run`** (selected via `--isolation` or `AGENT_VAULT_ISOLATION`): `host` (default, cooperative — fork+exec on the host with `HTTPS_PROXY`/`HTTP_PROXY` envvars) and `container` (non-cooperative — Docker container with iptables egress locked to the Agent Vault proxy). Container mode lives in [internal/isolation/](internal/isolation/) with an embedded Dockerfile + init-firewall.sh + entrypoint.sh, built on first use and cached by content hash.
Comment on lines +25 to +32
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Doc cross-sync miss: this PR adds HTTP_PROXY alongside HTTPS_PROXY in vault run and updates ~8 pages to match, but ~14 other docs still describe HTTPS_PROXY-only behavior — quickstarts (claude-code, codex, cursor, hermes-agent, opencode, nanoclaw, openclaw, custom-agent), docs/index.mdx:24, docs/agents/overview.mdx, docs/learn/security.mdx:118 (heading), docs/learn/services.mdx:187, docs/guides/connect-custom-agent.mdx, and docs/self-hosting/{local,docker,fly-io}.mdx. The agents/protocol.mdx body table on line 90 was correctly updated, but the section heading "Route requests through HTTPS_PROXY" (and the frontmatter description) wasn't. Nothing functionally breaks — this is purely the cross-doc-sync rule the PR's own CLAUDE.md line ~32/50 codifies. Mechanical fix: the same wording flip the PR already applied in the eight updated pages, repeated across the listed paths.

Extended reasoning...

What is wrong. This PR's own CLAUDE.md change (the file's own conventions section, line ~32/50) explicitly says: "Whenever a new feature lands, scan docs/ for pages that need a matching update — quickstart, guides, self-hosting, learn, and reference all mirror runtime behavior and drift fast." The PR does this for eight pages (CLAUDE.md, README.md, docs/agents/protocol.mdx body table, docs/guides/{connect-coding-agent,container-isolation}.mdx, docs/installation.mdx, docs/reference/cli.mdx, docs/self-hosting/environment-variables.mdx) and the two embedded skill files. It misses ~14 others that still describe HTTPS_PROXY-only behavior, contradicting the new runtime behavior the PR ships.

Verified by grep against the working tree. Each of the following lines was confirmed:

  • docs/quickstart/claude-code.mdx:13, cursor.mdx:13, codex.mdx:13, hermes-agent.mdx:13, opencode.mdx:13: "pre-configures HTTPS_PROXY and CA trust so outbound HTTPS routes through Agent Vault transparently". Same five files at line ~39: "HTTPS_PROXY routes the call through Agent Vault".
  • docs/quickstart/custom-agent.mdx:3,13,27,62,89 — frontmatter description and body all HTTPS_PROXY-only.
  • docs/quickstart/nanoclaw.mdx:43, openclaw.mdx:43: "Invited agents like NanoClaw build their own HTTPS_PROXY…".
  • docs/index.mdx:24: "You point the agent at Agent Vault by setting HTTPS_PROXY and trusting the Agent Vault CA certificate.".
  • docs/agents/overview.mdx:12,22,162 — three HTTPS_PROXY-only mentions on the canonical agents overview page.
  • docs/learn/security.mdx:118 — section heading "Transparent proxy (HTTPS_PROXY) transport" plus body.
  • docs/learn/services.mdx:187 — Twilio walkthrough still says "via HTTPS_PROXY".
  • docs/guides/connect-custom-agent.mdx:3,27,41,62,69,91,118,127 — frontmatter description, body, env-var table row, and the heading "Set HTTPS_PROXY manually" all HTTPS_PROXY-only.
  • docs/self-hosting/local.mdx:81: "transparent HTTPS_PROXY listener on port 14322".
  • docs/self-hosting/docker.mdx:24: "transparent HTTPS proxy (14322) so agents' HTTPS_PROXY can reach the broker".
  • docs/self-hosting/fly-io.mdx:76: "Agent HTTPS_PROXY".
  • docs/agents/protocol.mdx: the body env-var table on line ~88-90 correctly added an HTTP_PROXY row, but the section heading "Route requests through HTTPS_PROXY" and the frontmatter description still describe HTTPS_PROXY-only behavior.

Step-by-step proof of impact.

  1. Operator follows docs/installation.mdx (updated in this PR) and stands up a server. They read line 121 — "the transparent HTTP/HTTPS proxy that agents' HTTPS_PROXY and HTTP_PROXY point at". Good.
  2. They click into the Claude Code quickstart at docs/quickstart/claude-code.mdx. Line 13 says: "pre-configures HTTPS_PROXY and CA trust so outbound HTTPS routes through Agent Vault transparently". They reasonably conclude that plain-http:// upstreams are NOT brokered.
  3. The agent makes a plain-http:// call to an internal service. Under the new code path it IS brokered (audited, credential-injected if matched, denied under unmatched_host_policy=deny). The operator is surprised — the docs told them otherwise.
  4. Now flip the scenario: a vault has unmatched_host_policy=deny and the operator hadn't added the internal service. Under the old behavior, a plain-http:// call would never have hit the broker. Under the new behavior it returns 403 from Agent Vault. The operator goes to docs/learn/security.mdx to debug, sees the heading "Transparent proxy (HTTPS_PROXY) transport" and the HTTPS-only body below it, and now genuinely cannot tell whether plain-HTTP traffic is supposed to go through the broker or not.

That's the operator-facing harm: not a runtime bug, but documentation that actively misleads someone trying to reason about traffic flow with the new code in place.

Why existing code does not prevent this. This is documentation, not code — there's no test that fails on doc drift, and the PR's own self-review checklist (run /simplify, run /security-review, make test) does not cover scanning unchanged docs for stale references. The only safeguard is the CLAUDE.md convention that this PR violates.

Impact. Severity nit. No functional regression — every line cited is factually correct for HTTPS, just incomplete with respect to HTTP in the new world. Operators reading these pages will conclude HTTP_PROXY is best-effort or only-some-pages, which is the opposite of true. Cosmetic, but pervasive (~14 pages on a docs surface this PR explicitly touched).

How to fix. Mechanical sed pass over the listed paths replacing the same wording the PR already established in the updated pages: HTTPS_PROXYHTTPS_PROXY/HTTP_PROXY, HTTPS trafficHTTP and HTTPS traffic, the section headings updated to drop the HTTPS_PROXY-specific framing, and the agents/protocol.mdx frontmatter description + section heading updated to match the body table that was already corrected.


## Where to look for details

Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ The server starts the HTTP API on port `14321` and a TLS-encrypted transparent H

### CLI — local agents (Claude Code, Cursor, Codex, OpenClaw, Hermes, OpenCode)

Wrap any local agent process with `agent-vault run` (long form: `agent-vault vault run`). Agent Vault creates a scoped session, sets `HTTPS_PROXY` and CA-trust env vars, and launches the agent — all HTTPS traffic is transparently proxied and authenticated:
Wrap any local agent process with `agent-vault run` (long form: `agent-vault vault run`). Agent Vault creates a scoped session, sets `HTTPS_PROXY`/`HTTP_PROXY` and CA-trust env vars, and launches the agent — all HTTP and HTTPS traffic is transparently proxied and authenticated:

```bash
agent-vault run -- claude
Expand Down Expand Up @@ -120,9 +120,9 @@ const session = await av
// certPath is where you'll mount the CA certificate inside the sandbox.
const certPath = "/etc/ssl/agent-vault-ca.pem";

// env: { HTTPS_PROXY, NO_PROXY, NODE_USE_ENV_PROXY, SSL_CERT_FILE,
// NODE_EXTRA_CA_CERTS, REQUESTS_CA_BUNDLE, CURL_CA_BUNDLE,
// GIT_SSL_CAINFO, DENO_CERT }
// env: { HTTPS_PROXY, HTTP_PROXY, NO_PROXY, NODE_USE_ENV_PROXY,
// SSL_CERT_FILE, NODE_EXTRA_CA_CERTS, REQUESTS_CA_BUNDLE,
// CURL_CA_BUNDLE, GIT_SSL_CAINFO, DENO_CERT }
const env = buildProxyEnv(session.containerConfig!, certPath);
const caCert = session.containerConfig!.caCertificate;

Expand Down
35 changes: 19 additions & 16 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,16 @@ Environment variables set on the child:
AGENT_VAULT_ADDR — base URL of the Agent Vault HTTP control server
AGENT_VAULT_VAULT — vault the session is scoped to

The child also inherits HTTPS_PROXY / NO_PROXY / NODE_USE_ENV_PROXY plus
the root CA trust variables (SSL_CERT_FILE, NODE_EXTRA_CA_CERTS,
REQUESTS_CA_BUNDLE, CURL_CA_BUNDLE, GIT_SSL_CAINFO, DENO_CERT) so standard
HTTPS clients transparently route through the broker. NODE_USE_ENV_PROXY=1
enables Node.js built-in proxy support (v22.21.0+) so fetch() and
https.get() honor HTTPS_PROXY natively. HTTP_PROXY is intentionally not
set — the MITM proxy only handles HTTPS (CONNECT) and would 405 any plain
http:// request. The root CA PEM is written to ~/.agent-vault/mitm-ca.pem.
The child also inherits HTTPS_PROXY / HTTP_PROXY / NO_PROXY /
NODE_USE_ENV_PROXY plus the root CA trust variables (SSL_CERT_FILE,
NODE_EXTRA_CA_CERTS, REQUESTS_CA_BUNDLE, CURL_CA_BUNDLE, GIT_SSL_CAINFO,
DENO_CERT) so both HTTPS and plain-HTTP clients transparently route
through the broker. NODE_USE_ENV_PROXY=1 enables Node.js built-in proxy
support (v22.21.0+) so fetch() and http.get()/https.get() honor the
proxy env natively. HTTPS_PROXY and HTTP_PROXY both point at the same
TLS-wrapped proxy URL — the listener accepts CONNECT for https://
upstreams and absolute-form forward-proxy requests for http:// on the
same port. The root CA PEM is written to ~/.agent-vault/mitm-ca.pem.

Example:
` + examplePrefix + ` -- claude
Comment on lines 42 to 57
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 The runtime startup banner at cmd/run.go:149 still prints routing HTTPS through MITM proxy and the surrounding step comment at line 141 still says "Route the child's HTTPS traffic", even though this PR's whole point is that plain-HTTP traffic now also routes through the MITM ingress. Operators see this banner first thing on every successful vault run launch, immediately after reading the new help text that promises HTTP/HTTPS coverage. Trivial fix: s/routing HTTPS/routing HTTP\/HTTPS/ and update the step comment to match.

Extended reasoning...

What the bug is. The PR rewrote every text surface that mentioned HTTPS-only routing — the cobra Long help (lines 42–57 of the diff), the docstring on augmentEnvWithMITM (lines 386–395), the assertions in run_test.go, README.md, CLAUDE.md, skill_cli.md, skill_http.md, docs/agents/protocol.mdx, docs/installation.mdx, docs/reference/cli.mdx, docs/self-hosting/environment-variables.mdx, docs/guides/connect-coding-agent.mdx, docs/guides/container-isolation.mdx, even the TypeScript SDK's ContainerConfig JSDoc — but the one user-visible runtime confirmation message was missed.\n\nWhere it manifests. cmd/run.go:149:\n\ngo\nfmt.Fprintf(os.Stderr, "%s routing HTTPS through MITM proxy (127.0.0.1:%d)\n", successText("agent-vault:"), mitmPort)\n\n\nand the surrounding step comment on line 141:\n\ngo\n// 6. Route the child's HTTPS traffic through the transparent MITM\n// proxy. The MITM ingress is the only credential-injection path,\n// so a failure here is fatal.\n\n\nBoth predate this PR (the line itself isn't in the diff) but are now stale because the headline behavioral change of this PR is that HTTP_PROXY is also injected and plain-HTTP traffic is also intercepted.\n\nWhy this slipped past existing checks. The line is a literal fmt.Fprintf to stderr — no tests assert on the banner string, and grep HTTPS on the diff would surface only deleted/changed strings, not unchanged ones. The text-sweep otherwise was thorough; this is the single drift.\n\nStep-by-step proof.\n1. Build the new binary: make build.\n2. Start a server: ./agent-vault server -d.\n3. Run ./agent-vault run -- printenv | grep -E '^(HTTP|HTTPS)_PROXY'.\n4. Stderr emits: agent-vault: routing HTTPS through MITM proxy (127.0.0.1:14322).\n5. Stdout shows both HTTPS_PROXY AND HTTP_PROXY set to the same TLS-wrapped MITM URL — exactly what augmentEnvWithMITM now does per the updated docstring at run.go:386–395 and per isolation.BuildProxyEnv at internal/isolation/env.go:52–53.\n6. Result: the operator-facing message contradicts what was actually injected, and contradicts every doc the operator might cross-reference (Long help directly above the call site, README, docs/reference/cli.mdx, etc.).\n\nImpact. Cosmetic — no functional defect. But the PR description explicitly highlights plain-HTTP coverage as the breaking change ("After upgrading, agents' plain-http:// requests are now intercepted by Agent Vault"). Leaving the only observable runtime confirmation as "HTTPS"-only undersells exactly the breaking change operators most need to notice.\n\nHow to fix. One-line edit at cmd/run.go:149 — change the format string to "%s routing HTTP/HTTPS through MITM proxy (127.0.0.1:%d)\n". Optionally also update the step comment at line 141 from "Route the child's HTTPS traffic" to "Route the child's HTTP and HTTPS traffic" to mirror the augmentEnvWithMITM docstring's wording ("transparently routes HTTP and HTTPS").

Expand Down Expand Up @@ -381,15 +383,16 @@ func requireMITMEnv(env []string, addr, token, vault, caPath string) ([]string,
return newEnv, port, nil
}

// augmentEnvWithMITM extends env so the child transparently routes HTTPS
// through the broker. Returns (env, 0, false, nil) when the server has
// MITM disabled. The second return value is the port the server reported;
// callers log it so operators see the actual listen port (not a constant).
// caPath is a test seam — pass "" for the default location.
// augmentEnvWithMITM extends env so the child transparently routes HTTP
// and HTTPS through the broker. Returns (env, 0, false, nil) when the
// server has MITM disabled. The second return value is the port the
// server reported; callers log it so operators see the actual listen
// port (not a constant). caPath is a test seam — pass "" for the
// default location.
//
// Only HTTPS_PROXY is injected — not HTTP_PROXY. The MITM proxy handles
// HTTP CONNECT only and returns 405 for every other method, so setting
// HTTP_PROXY would route plain http:// requests into a dead end.
// Both HTTPS_PROXY and HTTP_PROXY are injected, pointing at the same
// TLS-wrapped proxy URL. The listener handles CONNECT for https://
// upstreams and absolute-form forward-proxy requests for http://.
func augmentEnvWithMITM(env []string, addr, token, vault, caPath string) ([]string, int, bool, error) {
pem, port, enabled, mitmTLS, err := fetchMITMCA(addr)
if err != nil {
Expand Down
13 changes: 8 additions & 5 deletions cmd/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ func TestAugmentEnvWithMITM_Enabled(t *testing.T) {

want := map[string]string{
"HTTPS_PROXY": "", // checked separately below
"HTTP_PROXY": "", // checked separately below
"NO_PROXY": "localhost,127.0.0.1",
"NODE_USE_ENV_PROXY": "1",
"SSL_CERT_FILE": caPath,
Expand All @@ -184,10 +185,11 @@ func TestAugmentEnvWithMITM_Enabled(t *testing.T) {
}
}

// HTTP_PROXY must NOT be set — the MITM proxy is HTTPS-only and would
// return 405 for plain http:// requests routed through it.
if v, ok := vars["HTTP_PROXY"]; ok {
t.Errorf("HTTP_PROXY should not be set (MITM is HTTPS-only), got %q", v)
// HTTP_PROXY must equal HTTPS_PROXY — both point at the same TLS-
// wrapped MITM ingress so plain http:// upstreams route through the
// broker via absolute-form forward-proxy requests.
if vars["HTTP_PROXY"] != vars["HTTPS_PROXY"] {
t.Errorf("HTTP_PROXY = %q, want it to equal HTTPS_PROXY = %q", vars["HTTP_PROXY"], vars["HTTPS_PROXY"])
}

// Proxy URL must parse cleanly and carry token:vault userinfo.
Expand Down Expand Up @@ -254,6 +256,7 @@ func TestAugmentEnvWithMITM_DedupesParentEnv(t *testing.T) {
parentEnv := []string{
"FOO=bar",
"HTTPS_PROXY=http://corp-proxy:3128",
"HTTP_PROXY=http://corp-proxy:3128",
"NO_PROXY=internal.example.com",
"SSL_CERT_FILE=/etc/ssl/corp-ca.pem",
"NODE_EXTRA_CA_CERTS=/etc/ssl/corp-ca.pem",
Expand All @@ -276,7 +279,7 @@ func TestAugmentEnvWithMITM_DedupesParentEnv(t *testing.T) {
counts[kv[:i]]++
}
}
for _, k := range []string{"HTTPS_PROXY", "NO_PROXY", "NODE_USE_ENV_PROXY", "SSL_CERT_FILE", "NODE_EXTRA_CA_CERTS", "REQUESTS_CA_BUNDLE", "CURL_CA_BUNDLE", "GIT_SSL_CAINFO", "DENO_CERT"} {
for _, k := range []string{"HTTPS_PROXY", "HTTP_PROXY", "NO_PROXY", "NODE_USE_ENV_PROXY", "SSL_CERT_FILE", "NODE_EXTRA_CA_CERTS", "REQUESTS_CA_BUNDLE", "CURL_CA_BUNDLE", "GIT_SSL_CAINFO", "DENO_CERT"} {
if counts[k] != 1 {
t.Errorf("%s appears %d times in env, want exactly 1 (POSIX getenv returns first match)", k, counts[k])
}
Expand Down
9 changes: 5 additions & 4 deletions cmd/skill_cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ By default each vault forwards unmatched hosts as plain proxy traffic (no creden
| `AGENT_VAULT_SESSION_TOKEN` | Bearer token for authenticating with Agent Vault's control-plane endpoints (`discover`, proposals, etc.) |
| `AGENT_VAULT_VAULT` | Vault name (set for user-scoped sessions via `agent-vault run`) |

`agent-vault run` also pre-configures `HTTPS_PROXY`, `NO_PROXY`, `NODE_USE_ENV_PROXY`, and CA-trust variables (`SSL_CERT_FILE`, `NODE_EXTRA_CA_CERTS`, `REQUESTS_CA_BUNDLE`, `CURL_CA_BUNDLE`, `GIT_SSL_CAINFO`, `DENO_CERT`) so HTTPS calls from your process route through the broker transparently. You don't manage these yourself.
`agent-vault run` also pre-configures `HTTPS_PROXY`, `HTTP_PROXY`, `NO_PROXY`, `NODE_USE_ENV_PROXY`, and CA-trust variables (`SSL_CERT_FILE`, `NODE_EXTRA_CA_CERTS`, `REQUESTS_CA_BUNDLE`, `CURL_CA_BUNDLE`, `GIT_SSL_CAINFO`, `DENO_CERT`) so HTTP and HTTPS calls from your process both route through the broker transparently. You don't manage these yourself.

Under `--isolation=container`, the same env shape is injected inside a Docker container, but the proxy URL host is `host.docker.internal` instead of `127.0.0.1` and egress to any other destination is blocked by iptables. From your perspective nothing changes — standard HTTP clients pick up the envvars as normal.

Expand All @@ -59,18 +59,19 @@ Response includes `vault`, `services` (host + description), and `available_crede

## Making Requests

**Just call the real API URL.** When you were launched via `agent-vault run` (or the long form `agent-vault vault run`), your HTTPS traffic already routes through Agent Vault transparently — `HTTPS_PROXY` and the broker's CA cert are pre-configured in your environment. Agent Vault intercepts the call, looks up the host in the vault's services, injects the credential, and forwards over HTTPS.
**Just call the real API URL.** When you were launched via `agent-vault run` (or the long form `agent-vault vault run`), your HTTP and HTTPS traffic already route through Agent Vault transparently — `HTTPS_PROXY`, `HTTP_PROXY`, and the broker's CA cert are pre-configured in your environment. Agent Vault intercepts the call, looks up the host in the vault's services, injects the credential, and forwards to the upstream.

```bash
curl https://api.stripe.com/v1/charges
curl https://api.github.com/user
curl http://internal.example/api/v1/items # plain http:// works the same way
```

Your code can leave the upstream auth header blank or set it to a placeholder — Agent Vault attaches the real credential at the proxy boundary, so the value in your env can be anything (or absent). Standard HTTP clients (curl, fetch, requests, axios, the Go stdlib, etc.) honor `HTTPS_PROXY` automatically.
Your code can leave the upstream auth header blank or set it to a placeholder — Agent Vault attaches the real credential at the proxy boundary, so the value in your env can be anything (or absent). Standard HTTP clients (curl, fetch, requests, axios, the Go stdlib, etc.) honor `HTTPS_PROXY`/`HTTP_PROXY` automatically.

### WebSocket / Streaming

`wss://` URLs are brokered through the same `HTTPS_PROXY` mechanism as regular HTTPS. Credentials are injected into the WebSocket handshake (`Authorization`, `Sec-WebSocket-Protocol`) the same way as on a normal request — point your client at the real `wss://` URL and Agent Vault attaches the real credential at the proxy boundary.
`wss://` and `ws://` URLs are brokered through the same proxy mechanism as regular HTTP/HTTPS. Credentials are injected into the WebSocket handshake (`Authorization`, `Sec-WebSocket-Protocol`) the same way as on a normal request — point your client at the real WebSocket URL and Agent Vault attaches the real credential at the proxy boundary.

```
wss://api.openai.com/v1/realtime?model=gpt-realtime
Expand Down
9 changes: 5 additions & 4 deletions cmd/skill_http.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ By default each vault forwards unmatched hosts as plain proxy traffic (no creden
| `AGENT_VAULT_SESSION_TOKEN` | Bearer token for authenticating with Agent Vault's control-plane endpoints (`/discover`, proposals, etc.) |
| `AGENT_VAULT_VAULT` | Vault name (set for user-scoped sessions via `vault run`) |

`vault run` also pre-configures `HTTPS_PROXY`, `NO_PROXY`, `NODE_USE_ENV_PROXY`, and CA-trust variables (`SSL_CERT_FILE`, `NODE_EXTRA_CA_CERTS`, `REQUESTS_CA_BUNDLE`, `CURL_CA_BUNDLE`, `GIT_SSL_CAINFO`, `DENO_CERT`) so HTTPS calls from your process route through the broker transparently. You don't manage these yourself.
`vault run` also pre-configures `HTTPS_PROXY`, `HTTP_PROXY`, `NO_PROXY`, `NODE_USE_ENV_PROXY`, and CA-trust variables (`SSL_CERT_FILE`, `NODE_EXTRA_CA_CERTS`, `REQUESTS_CA_BUNDLE`, `CURL_CA_BUNDLE`, `GIT_SSL_CAINFO`, `DENO_CERT`) so HTTP and HTTPS calls from your process both route through the broker transparently. You don't manage these yourself.

Under `--isolation=container`, the same env shape is injected inside a Docker container, but the proxy URL host is `host.docker.internal` instead of `127.0.0.1` and egress to any other destination is blocked by iptables. From your perspective nothing changes — standard HTTP clients pick up the envvars as normal.

Expand Down Expand Up @@ -68,18 +68,19 @@ Returns built-in service templates with suggested credential keys and auth types

## Making Requests

**Just call the real API URL.** When you were launched via `agent-vault vault run`, your HTTPS traffic already routes through Agent Vault transparently — `HTTPS_PROXY` and the broker's CA cert are pre-configured in your environment. Agent Vault intercepts the call, looks up the host in the vault's services, injects the credential, and forwards over HTTPS.
**Just call the real API URL.** When you were launched via `agent-vault vault run`, your HTTP and HTTPS traffic already route through Agent Vault transparently — `HTTPS_PROXY`, `HTTP_PROXY`, and the broker's CA cert are pre-configured in your environment. Agent Vault intercepts the call, looks up the host in the vault's services, injects the credential, and forwards to the upstream.

```
GET https://api.stripe.com/v1/charges
GET https://api.github.com/user
GET http://internal.example/api/v1/items # plain http:// works the same way
```

Your code can leave the upstream auth header blank or set it to a placeholder — Agent Vault attaches the real credential at the proxy boundary, so the value in your env can be anything (or absent). Standard HTTP clients (curl, fetch, requests, axios, the Go stdlib, etc.) honor `HTTPS_PROXY` automatically.
Your code can leave the upstream auth header blank or set it to a placeholder — Agent Vault attaches the real credential at the proxy boundary, so the value in your env can be anything (or absent). Standard HTTP clients (curl, fetch, requests, axios, the Go stdlib, etc.) honor `HTTPS_PROXY`/`HTTP_PROXY` automatically.

### WebSocket / Streaming

`wss://` URLs are brokered through the same `HTTPS_PROXY` mechanism as regular HTTPS. Credentials are injected into the WebSocket handshake (`Authorization`, `Sec-WebSocket-Protocol`) the same way as on a normal request — point your client at the real `wss://` URL and Agent Vault attaches the real credential at the proxy boundary.
`wss://` and `ws://` URLs are brokered through the same proxy mechanism as regular HTTP/HTTPS. Credentials are injected into the WebSocket handshake (`Authorization`, `Sec-WebSocket-Protocol`) the same way as on a normal request — point your client at the real WebSocket URL and Agent Vault attaches the real credential at the proxy boundary.

```
wss://api.openai.com/v1/realtime?model=gpt-realtime
Expand Down
5 changes: 3 additions & 2 deletions docs/agents/protocol.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,13 @@ The agent writes this line unchanged. No URL rewriting. No `Authorization` heade
| Variable | Purpose |
|---|---|
| `HTTPS_PROXY` | Points at the MITM listener (`http://{token}:{vault}@host:14322`) |
| `HTTP_PROXY` | Same URL as `HTTPS_PROXY` — plain `http://` upstreams route through the same TLS-wrapped listener via absolute-form forward-proxy requests |
| `NO_PROXY` | `localhost,127.0.0.1` — so agent-to-vault traffic skips the proxy |
| `NODE_USE_ENV_PROXY` | `1` — enables Node.js v22.21+ built-in `HTTPS_PROXY` support for `fetch()` and `https.get()` |
| `NODE_USE_ENV_PROXY` | `1` — enables Node.js v22.21+ built-in proxy support for `fetch()` and `http.get()`/`https.get()` |
| `SSL_CERT_FILE`, `NODE_EXTRA_CA_CERTS`, `REQUESTS_CA_BUNDLE`, `CURL_CA_BUNDLE`, `GIT_SSL_CAINFO`, `DENO_CERT` | Point standard HTTP libraries at the Agent Vault root CA so the proxied TLS handshake validates |

<Tip>
HTTP_PROXY is intentionally not set. The MITM listener only handles HTTPS (CONNECT) and would 405 on plain `http://` requests.
The MITM listener accepts both `CONNECT host:port` (HTTPS upstreams) and absolute-form forward-proxy requests (`POST http://host/path HTTP/1.1`) on the same port. Plain-HTTP upstreams are intercepted, audited, and credential-injected just like HTTPS upstreams.
</Tip>

## Propose changes
Expand Down
Loading