Conversation
…ASE_URL ANTHROPIC_BASE_URL is visible to the agent, which disables client-side features when it points anywhere but api.anthropic.com. warp gives the child HTTPS_PROXY on an ephemeral loopback port and a per-run CA, then decrypts only /v1/messages and tunnels every other host blind. The agent believes it is talking to api.anthropic.com. Ported from wardex proxy.go/ca.go.
Four defects found reviewing the CONNECT path: - SAN extension emitted the host as a bare string, so leaf certs failed hostname verification on Node 20+. Wrap it in a DNSName choice. - Client aborts left the upstream socket open, leaking a connection per cancelled request. Tear down both halves on either side closing. - Forwarded requests dropped the original Host, so upstreams that route on it returned the wrong vhost. Preserve it. - Stale comments described the pre-CONNECT env var design.
Pin injection already worked for codex: the proxy rewrites the request and relocates file pins last. Only the command reply was gated on isAnthropicMessagesPath, so `@pxpipe pin` was forwarded to the model, which then guessed at what it meant. Add chat/completions and /responses synthesizers so the reply streams in the shape each client expects, and widen the gate. Requests whose last item is a tool result are left alone, so a pin command earlier in the transcript cannot replay mid tool loop.
The resolver asks the interactive shell first because an alias can shadow a real binary: `cc` is Apple clang on PATH and a Claude Code alias in the user's zsh. But it trusted the alias unconditionally, so an alias whose target had been uninstalled killed the launch with a working binary on PATH. Before: alias claude=/opt/homebrew/bin/claude # removed, now a mise install $ pxpipe warp -- claude zsh:1: no such file or directory: /opt/homebrew/bin/claude After: [pxpipe] warp: ignoring stale alias claude → /opt/homebrew/bin/claude (runs the claude on PATH) A live alias still wins over PATH. An env-assignment prefix (`FOO=1 claude`) cannot be checked, so it is still taken at its word.
Claude Code appends a system-role message (its agent-type catalogue) after the
user's turn, and the user's first typed line rides behind the CLAUDE.md
envelope. isPinOnlyRequest inspected only the literal last element, and
isCommandOnlyTurn rejects both a non-user role and a leading reminder, so every
cc pin command went upstream unanswered: the pin was folded into later requests
but the user never got a status reply.
Before:
messages = [ user: "<system-reminder>CLAUDE.md...</system-reminder>@pxpipe pin",
system: "Available agent types for the Agent tool:..." ]
-> forwarded to the model
After:
-> answered locally, no upstream call
liveTurn() skips only trailing system-role metadata. A trailing tool_result or
assistant prefill still forwards, so a mid-tool-loop turn is never hijacked.
The envelope is tolerated on the live path only. Answering locally forwards
nothing and the client resends message 0 intact, but rewriting it would drop the
whole block, so the outbound strip path still rejects it and CLAUDE.md survives.
README says the dashboard carries the full instructions; it carried none. Adds a "Connect an agent" panel next to the model-scope panel: warp invocations, alias and --route notes, the manual ANTHROPIC_BASE_URL fallback, and the @pxpipe pin/unpin commands including the file-backed CLAUDE.md / AGENTS.md tier that unpin cannot reach.
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.
ANTHROPIC_BASE_URLis visible to the agent. Claude Code checks it and disablesclient-side features when it points anywhere but api.anthropic.com, and OAuth
paths sign against the URL they were given.
pxpipe warpremoves the env var from the equation.Before:
ANTHROPIC_BASE_URL=http://127.0.0.1:47821 claude # agent sees the overrideAfter:
pxpipe warp -- claude # agent sees api.anthropic.comThe child gets
HTTPS_PROXYpointed at a loopback CONNECT listener on akernel-assigned port,
ANTHROPIC_BASE_URLdeleted, and the warp CA injectedthrough
NODE_EXTRA_CA_CERTS,SSL_CERT_FILE,CURL_CA_BUNDLE, andREQUESTS_CA_BUNDLE. Trust is scoped to that child; nothing is installed into asystem or keychain store.
The CA lives at
~/.pxpipe/warp-ca.pemwith its key at~/.pxpipe/warp-ca-key.pem(mode 0600), created on first run and reused afterwards, so a re-run does not
invalidate anything already trusted.
Only
api.anthropic.com/v1/messages*is decrypted and diverted to the runningpxpipe proxy. Every other host is tunneled blind at CONNECT, so OAuth, telemetry,
/remote-control, and claude.ai connectors are untouched. TheANTHROPIC_BASE_URLflow still works unchanged.Ported from wardex (
proxy.go,ca.go) with two narrowings: warp decrypts onlyhosts a route could match rather than every host, and it forwards matches to the
pxpipe already running rather than starting a second transforming proxy.
No automated coverage yet. Verified manually with
claude, shell aliases, and curl.Also fixes
@pxpipe <cmd>never being answered outside opencode.codex posts to /v1/responses and /v1/chat/completions, which the pin handler did
not recognize, so the command reached the model as plain text.
Claude Code appends a system-role agent catalogue after the user's turn and hides
the first typed line behind the CLAUDE.md envelope, so the pin-only check never
matched. Only trailing system-role metadata is skipped; a trailing tool_result or
assistant prefill still forwards.
Verified: oc, cx, cc all answer
@pxpipe pinlocally with no upstream call.Fixes #141
Fixes #60