-
Notifications
You must be signed in to change notification settings - Fork 4
Description
branch: ralph-credential-proxy
Spec: Ralph Credential Injection Proxy
Overview
A lightweight HTTP reverse proxy that runs in a Docker container on the host. It receives API requests from Claude Code running inside a Docker sandbox, strips the phantom auth token, injects the real OAuth token (received via stdin at startup), and forwards requests to the Anthropic API.
This is the foundation of ralph's credential injection architecture: the real token never enters the sandbox. Claude sees only a phantom token value, and the proxy swaps it for the real credential before forwarding upstream.
Designed to be agent-agnostic: the proxy doesn't know or care about Claude specifically — it injects a bearer token into requests to a configurable target. Future agents (Codex, etc.) can reuse the same proxy with different tokens/targets.
Architecture
┌─ Docker Sandbox (microVM) ──────────────────────┐
│ CLAUDE_CODE_OAUTH_TOKEN=phantom │
│ ANTHROPIC_BASE_URL=http://host.docker.internal:PORT │
│ │
│ Claude sends: Authorization: Bearer phantom │
└──────────────────┬────────────────────────────────┘
│ via Docker's MITM proxy
▼
┌─ Host Docker ─────────────────────────────────────┐
│ agent-loop-proxy container (port mapped) │
│ Token received via stdin at startup │
│ Strips Authorization header │
│ Injects real token │
│ Forwards to TARGET (api.anthropic.com) │
└───────────────────────────────────────────────────┘
1. Proxy Server
The proxy is a Python 3 stdlib-only HTTP server. It:
- Reads a single line from stdin on startup (the real token), stores in memory, closes stdin
- Listens on a configurable port (env var
LISTEN_PORT, default18080) - Forwards POST requests to
TARGET(env var, defaulthttps://api.anthropic.com) - Replaces the
Authorizationheader withBearer <real-token> - Copies all other headers (except
Host,Content-Length,Transfer-Encoding) - Streams the response back to the caller
- Responds to GET
/healthwith 200 for health checks - Logs each proxied request method + path to stderr (no token values in logs)
Must handle:
- Chunked/streaming responses (SSE from Anthropic API)
- HTTP errors from upstream (forward status + body back to caller)
- Large request bodies (prompt content)
Must NOT:
- Write the token to disk, logs, or environment
- Include the token in error messages
2. Dockerfile
docker/agent-loop/proxy/Dockerfile
docker/agent-loop/proxy/proxy.py
FROM python:3.12-slim(minimal image, no build tools needed)- Copy
proxy.py EXPOSE 18080CMD ["python3", "/proxy.py"]
3. Integration with ralph
Ralph will start the proxy container with:
echo "$TOKEN" | docker run -i --rm --name agent-loop-proxy-<agent> \
-p <port>:18080 \
-e TARGET=https://api.anthropic.com \
agent-loop-proxy:v1The <agent> suffix (e.g., claude) supports future multi-agent namespacing.
Implementation Plan
Step 1: Create proxy server [DONE]
Files:
docker/agent-loop/proxy/proxy.py— HTTP proxy server
Implement:
- Read token from stdin (one line), store in memory, close stdin
- HTTP server on
LISTEN_PORT(default 18080) - POST handler: read body, build upstream request to
TARGET + path, replace Authorization header, forward, stream response back - GET
/healthhandler: return 200 withagent-loop-proxy ok - Logging: method + path to stderr, never log token values
Test:
tests/test_ralph_proxy.py:- Token read from stdin and stored in memory
- Authorization header is replaced in forwarded requests
- Other headers are preserved (except Host, Content-Length, Transfer-Encoding)
- Health endpoint returns 200
- Upstream errors are forwarded with correct status code
- Token never appears in log output
- Use
unittest.mockto mockurllib.request.urlopenfor unit tests
Verify: Run pytest tests/test_ralph_proxy.py -v. Fix any failures.
Review: Check for token leakage in error paths, correct header handling, streaming correctness.
Address feedback: Fix all findings, re-run tests.
Step 2: Create Dockerfile [DONE]
Files:
docker/agent-loop/proxy/Dockerfile
Implement:
FROM python:3.12-slimCOPY proxy.py /proxy.pyEXPOSE 18080CMD ["python3", "/proxy.py"]
Test:
- Build succeeds:
docker build -t agent-loop-proxy:test docker/agent-loop/proxy/ - Container starts and responds to health check:
echo "test-token" | docker run -i --rm -p 18081:18080 agent-loop-proxy:test & curl -s http://localhost:18081/health
Verify: Health check returns agent-loop-proxy ok.
Review: Image size, no unnecessary layers.
Address feedback: Fix, re-test.
Step 3: Run all checks
Implement:
- Run
pytest tests/test_ralph_proxy.py -v - Run
shellcheckandzsh -non any shell scripts (if added) - Verify Docker build succeeds
Verify: All checks pass clean.
Step 4: Create commit
Implement:
- Stage all changes and create a commit summarizing the credential injection proxy.
Verify: git log -1 shows the commit.
Conventions
- Language: Python 3 (stdlib only, no third-party deps)
- Tests: pytest with unittest.mock
- Error messages: Prefix with
proxy: - Exit codes: 0=success, 1=runtime error