diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b6be30..454a3da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] +### Added + +- **guide/observability.md — 3 nouvelles sections monitoring** (+214 lignes) + - **Activity Monitoring** : audit des actions Claude Code via les JSONL de session — quels fichiers lus, commandes exécutées, URLs fetchées. Requêtes `jq` prêtes à l'emploi. Tableau des patterns sensibles (.env, rm -rf, WebFetch externe) + - **External Monitoring Tools** : tableau comparatif ccusage / claude-code-otel / Akto / MLflow / ccboard avec decision guide et exemples d'install + - **Proxying Claude Code** : pourquoi Proxyman/Charles échouent (Node.js ignore les proxies système), 4 solutions : `NODE_EXTRA_CA_CERTS`, `ANTHROPIC_API_URL`, mitmproxy (recommandé), proxy Python minimal +- **docs/resource-evaluations/ccboard-activity-module-plan.md** : plan complet pour le module Activity de ccboard (Tab 10) + - Data models Rust (`ToolCall`, `FileAccess`, `BashCommand`, `NetworkCall`, `Alert`) + - Parser JSONL stream avec détection destructive/sensible + - Schéma SQLite + stratégie de cache lazy (invalidation par mtime) + - Layout TUI avec 5 sous-tabs (Files/Commands/Network/Alerts/Timeline) + - Endpoints Web API (`GET /api/activity/:session_id/...`) + - 7 règles d'alertes avec sévérités + - 5 phases d'implémentation avec checklists +- **machine-readable/reference.yaml** : 6 nouvelles entrées (`activity_monitoring`, `external_monitoring_tools`, `proxying_claude_code`, `ccboard_activity_plan`...) + ## [3.28.0] - 2026-02-21 ### Added diff --git a/docs/resource-evaluations/2026-02-20-mergify-cross-system-support-investigator.md b/docs/resource-evaluations/2026-02-20-mergify-cross-system-support-investigator.md new file mode 100644 index 0000000..4e711ea --- /dev/null +++ b/docs/resource-evaluations/2026-02-20-mergify-cross-system-support-investigator.md @@ -0,0 +1,70 @@ +# Resource Evaluation: Mergify — Cross-System Support Investigator + +**Date**: 2026-02-20 +**Evaluator**: Claude (eval-resource skill) +**Score**: 4/5 +**Action**: Integrated + +--- + +## Source + +- **URL**: https://mergify.com/blog/how-we-turned-claude-into-a-cross-system-support-investigator +- **Author**: Julian Maurin (Mergify) +- **Date**: November 2025 +- **Type**: Engineering blog post / production case study + +--- + +## Summary + +Mergify built a support ticket investigation system combining Claude Code as orchestrator with 5 custom MCP servers (Datadog, Sentry, PostgreSQL, Linear, GitHub). The system executes parallel queries across all systems and produces a structured report for human review. + +Key results (self-reported): triage time reduced from ~15 min → <5 min; 75% first-pass accuracy. + +--- + +## Scoring + +| Criterion | Assessment | +|-----------|-----------| +| Relevance to guide | High — fills gap on MCP operational orchestration | +| Novelty vs. existing content | High — "Claude Code as ops runtime" not covered | +| Production evidence | Medium — self-reported metrics, no public repo | +| Technical depth | Medium — architecture clear, code not published | +| Source reliability | Medium — company blog (potential marketing bias) | + +**Final score: 4/5** + +--- + +## Integration + +- **Where**: `guide/ultimate-guide.md` — §8.4 Server Selection Guide → "Combining Servers" → new subsection "Production Case Study: Multi-System Support Investigator" +- **Angle**: Claude Code as operational orchestrator (ops/support use case, not dev tool) +- **Key addition**: Architecture diagram, parallel fan-out pattern, design decisions, results with caveat + +--- + +## Fact-Check + +| Claim | Status | Notes | +|-------|--------|-------| +| Author: Julian Maurin | ✅ | Confirmed via Perplexity deep research | +| Date: November 2025 | ✅ | Confirmed | +| 5 systems: Datadog, Sentry, PostgreSQL, Linear, GitHub | ✅ | Confirmed | +| Triage: 15 min → <5 min | ⚠️ | Self-reported, not independently verified | +| First-pass accuracy: 75% | ⚠️ | Self-reported, same caveat | +| Architecture: MCP + parallel execution | ✅ | Consistent with MCP ecosystem | + +Metrics are presented in the guide with "self-reported" caveat. + +--- + +## Challenge Notes (technical-writer agent) + +- Score 4/5 confirmed +- 75% accuracy = 25% still need human follow-up → presented as "assistance", not "automation" +- Source bias noted (Mergify sells CI/CD automation, article serves their brand) +- Real value: "orchestrateur stateful avec MCP adaptateurs" pattern, distinct from all existing guide cases +- Risk of non-integration: moderate — guide will lag on ops/support angle as similar cases multiply \ No newline at end of file diff --git a/docs/resource-evaluations/ccboard-activity-module-plan.md b/docs/resource-evaluations/ccboard-activity-module-plan.md new file mode 100644 index 0000000..b34fcc8 --- /dev/null +++ b/docs/resource-evaluations/ccboard-activity-module-plan.md @@ -0,0 +1,382 @@ +# ccboard Activity Module — Implementation Plan + +**Date**: 2026-02-21 +**Status**: Draft +**Target repo**: [ccboard](https://github.com/FlorianBruniaux/ccboard) +**Triggered by**: User question on monitoring Claude Code file/command/network activity + +--- + +## Context + +ccboard currently covers session management, cost tracking, and basic stats. The missing layer is *activity auditing*: what files did Claude Code read, what commands did it execute, what URLs did it fetch? + +Session JSONL files already contain this data — every `tool_use` block in `type: "assistant"` messages is a complete record of Claude Code's actions. The Activity module parses these and surfaces them in a dedicated Tab 10. + +--- + +## Architecture + +### New Files + +``` +ccboard-core/ +├── src/ +│ ├── parsers/ +│ │ └── activity.rs # Parse tool_use blocks from JSONL +│ └── models/ +│ └── activity.rs # Structs: ToolCall, FileAccess, BashCommand, NetworkCall, Alert + +ccboard-tui/ +└── src/ + └── tabs/ + └── activity.rs # Tab 10 — TUI rendering + +ccboard-web/ +└── src/ + └── pages/ + └── activity.rs # /activity route — Web UI rendering +``` + +### Modified Files + +``` +ccboard-core/src/db/schema.rs # Add activity_events table +ccboard-core/src/db/queries.rs # Add activity query functions +ccboard-tui/src/app.rs # Register Tab 10 +ccboard-web/src/router.rs # Register /activity route +``` + +--- + +## Data Models + +```rust +// ccboard-core/src/models/activity.rs + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ToolCall { + pub id: String, + pub session_id: String, + pub timestamp: DateTime, + pub tool_name: String, + pub input: serde_json::Value, + pub duration_ms: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct FileAccess { + pub session_id: String, + pub timestamp: DateTime, + pub path: String, + pub operation: FileOperation, // Read | Write | Edit | Glob | Grep + pub line_range: Option<(usize, usize)>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum FileOperation { + Read, + Write, + Edit, + Glob, + Grep, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct BashCommand { + pub session_id: String, + pub timestamp: DateTime, + pub command: String, + pub is_destructive: bool, // Heuristic: rm -rf, git push --force, etc. + pub output_preview: String, // First 200 chars of output +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct NetworkCall { + pub session_id: String, + pub timestamp: DateTime, + pub url: String, + pub tool: NetworkTool, // WebFetch | WebSearch | McpCall + pub domain: String, // Extracted from URL +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum NetworkTool { + WebFetch, + WebSearch, + McpCall { server: String }, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Alert { + pub session_id: String, + pub timestamp: DateTime, + pub severity: AlertSeverity, + pub category: AlertCategory, + pub detail: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum AlertSeverity { + Info, + Warning, + Critical, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum AlertCategory { + CredentialAccess, // .env, *.pem, id_rsa + DestructiveCommand, // rm -rf, git push --force, DROP TABLE + ExternalExfil, // WebFetch to unknown domain + ScopeViolation, // Write outside project root + ForcePush, // git push --force +} +``` + +--- + +## Parser + +```rust +// ccboard-core/src/parsers/activity.rs + +pub fn parse_tool_calls(session_jsonl: &Path) -> Result> { + // 1. Stream-read JSONL (don't load entire file) + // 2. Filter lines where .type == "assistant" + // 3. Extract .message.content[].type == "tool_use" blocks + // 4. Map to ToolCall structs + // 5. Return Vec +} + +pub fn classify_tool_calls(calls: Vec) -> ActivitySummary { + // Fan out into typed collections + ActivitySummary { + file_accesses: extract_file_accesses(&calls), + bash_commands: extract_bash_commands(&calls), + network_calls: extract_network_calls(&calls), + alerts: generate_alerts(&calls), + } +} + +fn is_destructive_command(cmd: &str) -> bool { + let patterns = [ + "rm -rf", "rm -r ", "git push --force", "git push -f", + "DROP TABLE", "DROP DATABASE", "truncate ", "git reset --hard", + "git clean -f", "pkill", "kill -9", + ]; + patterns.iter().any(|p| cmd.to_lowercase().contains(p)) +} + +fn is_sensitive_file(path: &str) -> bool { + let patterns = [".env", ".pem", "id_rsa", "id_ed25519", ".p12", + "secrets.json", "credentials.json", ".npmrc", ".netrc"]; + patterns.iter().any(|p| path.contains(p)) +} +``` + +--- + +## SQLite Schema + +```sql +-- Add to ccboard-core/src/db/schema.rs migrations + +CREATE TABLE IF NOT EXISTS activity_events ( + id TEXT PRIMARY KEY, + session_id TEXT NOT NULL, + timestamp TEXT NOT NULL, + tool_name TEXT NOT NULL, + input_json TEXT NOT NULL, -- Full tool input as JSON + duration_ms INTEGER, + FOREIGN KEY (session_id) REFERENCES sessions(id) +); + +CREATE INDEX IF NOT EXISTS idx_activity_session ON activity_events(session_id); +CREATE INDEX IF NOT EXISTS idx_activity_tool ON activity_events(tool_name); +CREATE INDEX IF NOT EXISTS idx_activity_ts ON activity_events(timestamp); + +CREATE TABLE IF NOT EXISTS activity_alerts ( + id TEXT PRIMARY KEY, + session_id TEXT NOT NULL, + timestamp TEXT NOT NULL, + severity TEXT NOT NULL, -- 'info' | 'warning' | 'critical' + category TEXT NOT NULL, + detail TEXT NOT NULL, + FOREIGN KEY (session_id) REFERENCES sessions(id) +); +``` + +--- + +## TUI Tab (Tab 10) + +### Layout + +``` +┌─ Activity ─────────────────────────────────────────────────────────┐ +│ Session: my-project / 2026-02-21 14:32 [←][→] navigate sessions │ +├─────────────────────────────────────────────────────────────────────┤ +│ [Files 47] [Commands 12] [Network 3] [Alerts ⚠ 2] [Timeline] │ +├─────────────────────────────────────────────────────────────────────┤ +│ │ +│ FILES │ +│ 14:32:01 READ src/main.rs │ +│ 14:32:04 READ src/lib.rs │ +│ 14:32:09 EDIT src/main.rs ← lines 45-67 │ +│ 14:32:15 WRITE src/new_module.rs │ +│ 14:32:20 READ ⚠ .env ← credential access │ +│ │ +│ [j/k scroll] [Enter: expand] [f: filter] [a: alerts only] │ +└─────────────────────────────────────────────────────────────────────┘ +``` + +### Tabs within Activity + +| Sub-tab | Key | Content | +|---------|-----|---------| +| Files | `1` | File reads/writes/edits with path, timestamp, line range | +| Commands | `2` | Bash commands, destructive flag, output preview | +| Network | `3` | WebFetch URLs, MCP calls, domain list | +| Alerts | `4` | Auto-flagged events by severity | +| Timeline | `5` | Chronological view of all actions (merged) | + +### Keybindings + +| Key | Action | +|-----|--------| +| `Tab` / `Shift+Tab` | Switch sub-tabs | +| `j` / `k` | Scroll list | +| `Enter` | Expand item (full command / full path / response preview) | +| `f` | Filter by pattern | +| `a` | Jump to Alerts sub-tab | +| `s` | Jump to session picker | +| `/` | Search within current view | +| `y` | Copy selected item to clipboard | +| `e` | Export current view to JSON | + +--- + +## Web UI Page (/activity) + +``` +GET /activity → Session picker → redirect to /activity?session=ID +GET /activity?session=ID → Full activity view for session +GET /activity?session=ID&tab=files +GET /activity?session=ID&tab=commands +GET /activity?session=ID&tab=network +GET /activity?session=ID&tab=alerts +GET /activity?session=ID&tab=timeline + +GET /api/activity/:session_id → Full ActivitySummary JSON +GET /api/activity/:session_id/files → Vec +GET /api/activity/:session_id/commands → Vec +GET /api/activity/:session_id/network → Vec +GET /api/activity/:session_id/alerts → Vec +GET /api/activity/:session_id/timeline → Vec sorted by timestamp +``` + +--- + +## Alert Detection Rules + +| Rule | Condition | Severity | +|------|-----------|----------| +| Credential file access | `Read` path matches `*.env`, `*.pem`, `id_rsa`, `id_ed25519`, `secrets.json` | Warning | +| Destructive bash command | `Bash` input matches `rm -rf`, `git push --force`, `DROP TABLE`, `git reset --hard` | Critical | +| Force push | `Bash` input contains `git push` with `--force` or `-f` flag | Critical | +| External URL fetch | `WebFetch` URL domain not in project's known domains | Info | +| Write outside project root | `Write`/`Edit` path is outside `$PWD` at session start | Warning | +| Large file write | `Write` content size > 100KB | Info | +| Secrets in bash output | `Bash` output preview matches `sk-`, `ghp_`, `AKIA` (AWS key prefix) | Critical | + +Alerts are generated at parse time and stored in `activity_alerts`. They do **not** block Claude Code — ccboard is read-only. + +--- + +## Performance Constraints + +### Requirements + +- Startup time: < 2s for 1000+ sessions (same as current ccboard guarantee) +- JSONL parsing: lazy — only parse when session is selected, not on startup +- SQLite cache: parse once, cache `activity_events` by session_id + mtime. Invalidate on file change. +- Memory: stream JSONL line-by-line, never load full file + +### Caching Strategy + +``` +Session selected + → Check SQLite: activity_events WHERE session_id = ? AND mtime = file_mtime + → Cache hit: return from DB (< 10ms) + → Cache miss: parse JSONL → insert to DB → return (< 500ms for typical session) +``` + +### Index Strategy + +Parse activity index (tool counts, alert counts) at startup alongside session metadata — same lazy-index pattern used for session list. Full detail only on demand. + +``` +Startup: + For each session: read first/last line only → session metadata + Do NOT parse tool_use blocks + +On session select: + Check SQLite cache → parse if stale → display +``` + +--- + +## Implementation Phases + +### Phase 1: Parser + Models (1-2 days) + +- [ ] `activity.rs` models +- [ ] `activity.rs` parser (stream JSONL, extract tool_use) +- [ ] Alert detection rules +- [ ] Unit tests for parser with fixture JSONL files + +### Phase 2: SQLite Integration (1 day) + +- [ ] Schema migration +- [ ] Cache queries (insert / select / invalidate) +- [ ] Benchmark: 1000 sessions cold start + +### Phase 3: TUI Tab (2-3 days) + +- [ ] Tab 10 registration in `app.rs` +- [ ] Files sub-tab rendering +- [ ] Commands sub-tab rendering +- [ ] Network sub-tab rendering +- [ ] Alerts sub-tab rendering +- [ ] Timeline sub-tab rendering +- [ ] Keybindings + +### Phase 4: Web UI (1-2 days) + +- [ ] API endpoints +- [ ] /activity page routing +- [ ] HTML rendering (same style as existing pages) + +### Phase 5: Polish (1 day) + +- [ ] Export to JSON (`e` key in TUI) +- [ ] Clipboard copy (`y` key) +- [ ] Filter (`f` key) +- [ ] Documentation update in ccboard README + +--- + +## Out of Scope + +- **Real-time monitoring** (watching live session as it runs) — future phase +- **Cross-session aggregation** (e.g., "all files read this week") — future phase +- **Alert notifications** (desktop/Slack notifications) — future phase +- **Blocking rules** (ccboard stays read-only, no Claude Code process interaction) + +--- + +## Related + +- `guide/observability.md` — Section "Activity Monitoring" and "External Monitoring Tools" +- `guide/data-privacy.md` — What leaves your machine and why +- ccboard README — Architecture and contribution guide diff --git a/guide/observability.md b/guide/observability.md index 78785a0..f418b24 100644 --- a/guide/observability.md +++ b/guide/observability.md @@ -15,8 +15,11 @@ tags: [observability, guide, performance] 3. [Setting Up Session Logging](#setting-up-session-logging) 4. [Analyzing Session Data](#analyzing-session-data) 5. [Cost Tracking](#cost-tracking) -6. [Patterns & Best Practices](#patterns--best-practices) -7. [Limitations](#limitations) +6. [Activity Monitoring](#activity-monitoring) +7. [External Monitoring Tools](#external-monitoring-tools) +8. [Proxying Claude Code](#proxying-claude-code) +9. [Patterns & Best Practices](#patterns--best-practices) +10. [Limitations](#limitations) --- @@ -461,6 +464,217 @@ claude_budget_check --- +## Activity Monitoring + +Cost tracking tells you *how much* you spend. Activity monitoring tells you *what Claude Code actually did*: which files it read, which commands it ran, which URLs it fetched. This is the audit layer. + +### Session JSONL: The Ground Truth + +Every tool call Claude Code makes is recorded in the session JSONL files at `~/.claude/projects//`. Each entry with `type: "assistant"` contains a `content` array where `type: "tool_use"` blocks document every action. + +```bash +# Find your session files +ls ~/.claude/projects/-$(pwd | tr '/' '-')-/ + +# Inspect tool calls in a session +cat ~/.claude/projects/-your-project-/SESSION_ID.jsonl | \ + jq 'select(.type == "assistant") | .message.content[]? | select(.type == "tool_use") | {tool: .name, input: .input}' +``` + +### What Tool Calls Reveal + +| Tool | What It Exposes | +|------|----------------| +| `Read` | Files accessed (path, line range) | +| `Write` / `Edit` | Files modified (path, content delta) | +| `Bash` | Commands executed (full command string) | +| `WebFetch` | URLs fetched (may include data sent in POST) | +| `Task` | Subagent spawns (prompt passed to sub-model) | +| `Glob` / `Grep` | Search patterns and scope | + +### Practical Audit Queries + +```bash +# All files read in a session +SESSION=~/.claude/projects/-your-project-/SESSION_ID.jsonl +jq 'select(.type == "assistant") | .message.content[]? | select(.type == "tool_use" and .name == "Read") | .input.file_path' "$SESSION" + +# All bash commands executed +jq 'select(.type == "assistant") | .message.content[]? | select(.type == "tool_use" and .name == "Bash") | .input.command' "$SESSION" + +# All URLs fetched +jq 'select(.type == "assistant") | .message.content[]? | select(.type == "tool_use" and .name == "WebFetch") | .input.url' "$SESSION" + +# Count tool usage by type +jq -r 'select(.type == "assistant") | .message.content[]? | select(.type == "tool_use") | .name' "$SESSION" | sort | uniq -c | sort -rn +``` + +### Sensitive Patterns to Watch + +These tool call patterns are worth flagging in automated audits: + +| Pattern | Risk | Detection | +|---------|------|-----------| +| `Read` on `.env`, `*.pem`, `id_rsa` | Credential access | `jq '... | select(.input.file_path | test("\\.(env|pem|key)$"))'` | +| `Bash` with `rm -rf`, `git push --force` | Destructive action | `jq '... | select(.input.command | test("rm -rf\|force-push"))'` | +| `WebFetch` on external URLs | Data exfiltration risk | `jq '... | select(.name == "WebFetch") | .input.url'` | +| `Write` on files outside project root | Scope creep | Check paths against working directory | + +> **Security context**: Claude Code operates read-write on your filesystem with your user permissions. The JSONL audit trail is your record of what happened. For teams, consider syncing these logs to immutable storage. + +--- + +## External Monitoring Tools + +Beyond the hook-based approach above, the community has built purpose-specific tools. This is a factual snapshot as of early 2026. + +| Tool | Type | What It Does | Install | +|------|------|-------------|---------| +| **ccusage** | CLI / TUI | Cost tracking from JSONL — the de-facto reference for pricing data. ~10K GitHub stars. | `npm i -g ccusage` | +| **claude-code-otel** | OpenTelemetry exporter | Emits spans to any OTEL collector. Integrates with Prometheus + Grafana dashboards. Enterprise-focused. | `npm i -g claude-code-otel` | +| **Akto** | SaaS / self-hosted | API security guardrails + audit trail. Intercepts at the API level, flags policy violations. | [akto.io](https://akto.io) | +| **MLflow Tracing** | SDK integration | Structured traces (tool usage, latency, inputs/outputs). Requires wrapping calls in Python. | `pip install mlflow` | +| **ccboard** | TUI + Web | Unified dashboard for sessions, costs, stats. Activity/audit tab in development. | `cargo install ccboard` | + +### Decision Guide + +``` +Want cost numbers fast? → ccusage (CLI, 0 config) +Need enterprise audit trail? → claude-code-otel + Grafana or Akto +Already using MLflow for ML? → MLflow tracing integration +Want a persistent TUI/Web UI? → ccboard +``` + +### ccusage + +```bash +npm i -g ccusage +ccusage # Today's usage +ccusage --days 7 # Last 7 days +``` + +Reads directly from `~/.claude/projects/**/*.jsonl`. No API keys, no data sent externally. Source: [github.com/ryoppippi/ccusage](https://github.com/ryoppippi/ccusage). + +### claude-code-otel + +Exports Claude Code activity as OpenTelemetry spans: + +```bash +npm i -g claude-code-otel +claude-code-otel --collector http://localhost:4318 +``` + +Spans include tool name, duration, token counts. Plug into any OTEL-compatible backend (Jaeger, Tempo, Datadog). Source: [github.com/badger-99/claude-code-otel](https://github.com/badger-99/claude-code-otel). + +### ccboard + +```bash +cargo install ccboard +ccboard # Launch TUI +ccboard --web # Launch Web UI (localhost:3000) +``` + +Source: [github.com/FlorianBruniaux/ccboard](https://github.com/FlorianBruniaux/ccboard). An Activity tab covering file access, bash commands, and network calls is planned (see `docs/resource-evaluations/ccboard-activity-module-plan.md`). + +--- + +## Proxying Claude Code + +A common question: "Can I run Proxyman/Charles to see what Claude Code sends to Anthropic?" + +**Short answer**: Not directly. Here's why, and what works instead. + +### Why System Proxies Don't Work + +Claude Code is a Node.js process. By default, Node.js ignores system-level proxy settings (`HTTP_PROXY`, `HTTPS_PROXY`) — it uses its own TLS stack and doesn't read macOS/Windows proxy configurations. + +Additionally, even if traffic flows through your proxy, the TLS certificate mismatch causes Claude Code to fail (`CERT_UNTRUSTED`). + +### Option 1: Trust a MITM Certificate (Proxyman / Charles) + +Force Node.js to trust your proxy's CA certificate: + +```bash +# Export Proxyman's CA cert (File → Export → Root Certificate) +# Then point Node.js at it: +export NODE_EXTRA_CA_CERTS="/path/to/proxyman-ca.pem" + +# Start Claude Code — traffic will now route through Proxyman +claude +``` + +Same approach works for Charles: `Help → SSL Proxying → Export Charles Root Certificate`. + +**Caveats**: +- Some Claude Code versions use certificate pinning for `api.anthropic.com` — this may still fail +- This approach requires a running Proxyman/Charles instance listening on the configured port + +### Option 2: Redirect API Traffic with ANTHROPIC_API_URL + +Point Claude Code at a local interceptor instead of `api.anthropic.com`: + +```bash +export ANTHROPIC_API_URL="http://localhost:8080" +claude +``` + +Run any HTTP proxy/logger on port 8080 that forwards to `https://api.anthropic.com`. This bypasses TLS entirely for the Claude Code → proxy hop. + +**Use cases**: Logging request payloads, injecting headers, rate-limiting locally, replaying requests. + +### Option 3: mitmproxy (Recommended) + +[mitmproxy](https://mitmproxy.org) is the cleanest open-source solution. It provides a scriptable HTTPS proxy with a web UI and terminal interface. + +```bash +# Install +brew install mitmproxy # macOS +# or: pip install mitmproxy + +# Start transparent proxy on port 8080 +mitmproxy --listen-port 8080 + +# In a new terminal, point Claude Code at it +export NODE_EXTRA_CA_CERTS="$(python3 -c 'import mitmproxy.certs; print(mitmproxy.certs.Cert.default_ca_path())')" +export HTTPS_PROXY="http://localhost:8080" +claude +``` + +The mitmproxy web UI (`mitmweb`) at `http://localhost:8081` shows full request/response bodies — including the JSON payloads Claude Code sends to Anthropic. + +**What you'll see**: System prompt, user messages, tool definitions, tool results, model parameters. + +### Option 4: Minimal Python Logging Proxy + +For a zero-dependency approach: + +```python +# proxy.py — simple HTTPS logging proxy +from http.server import HTTPServer, BaseHTTPRequestHandler +import urllib.request, json, sys + +TARGET = "https://api.anthropic.com" + +class LoggingProxy(BaseHTTPRequestHandler): + def do_POST(self): + length = int(self.headers["Content-Length"]) + body = self.rfile.read(length) + print(json.dumps(json.loads(body), indent=2)) # Log request + # Forward to Anthropic... + +HTTPServer(("localhost", 8080), LoggingProxy).serve_forever() +``` + +```bash +python3 proxy.py & +export ANTHROPIC_API_URL="http://localhost:8080" +claude +``` + +> **Privacy note**: Proxied traffic includes everything in the conversation context — file contents Claude has read, your code, any secrets it encountered. Handle proxy logs accordingly. + +--- + ## Patterns & Best Practices ### 1. Weekly Review