HTTP traffic recording, redaction, and replay — embeddable Go library, CLI, and 6 MB Docker image.
10K+
HTTP traffic recording, redaction, and replay.
Embeddable Go library · CLI · Docker · Testcontainers
httptape captures HTTP request/response pairs (including SSE streams), redacts sensitive data on write, and replays them as a mock server. Think WireMock, but with a 3 MB Docker image, an embeddable Go library, SSE record/replay with per-event timing, and a redaction pipeline built into the core.
Docs: vibewarden.dev/docs/httptape · From: VibeWarden
The 3 Rs:
http.RoundTrippergock, httpmock) only work inside test code -- no standalone server, no recording, no fixture managementRecord real API interactions once, replay forever. Deterministic CI without live API credentials.
store := httptape.NewMemoryStore()
rec := httptape.NewRecorder(store, httptape.WithSanitizer(sanitizer))
defer rec.Close()
client := &http.Client{Transport: rec}
// ... hit real APIs, fixtures are recorded and redacted ...
srv := httptape.NewServer(store)
ts := httptest.NewServer(srv)
// ... replay against ts.URL in your tests ...
Use httptape as a mock backend while building your UI -- no real backend needed.
# Hand-write fixtures or record from a staging API
httptape record --upstream https://staging-api.example.com \
--fixtures ./mocks --config redact.json
# Serve as a mock backend for your frontend
httptape serve --fixtures ./mocks --port 3001
Your frontend on localhost:3000 hits httptape on localhost:3001. Edit JSON fixture files, and the next request picks up the changes -- instant hot-reload.
Record a sample of live traffic, safely redacted:
docker run -v ./fixtures:/fixtures -v ./config.json:/config/config.json \
tibtof/httptape record \
--upstream https://api.internal:8080 \
--fixtures /fixtures --config /config/config.json
Sensitive data (secrets, PII) is redacted before it touches disk. Export redacted fixtures for dev/CI use.
Use proxy mode for frontend development with automatic fallback to cached responses when the backend is unavailable:
httptape proxy --upstream https://api.example.com \
--fixtures ./cache --config redact.json
When the upstream is reachable, requests are forwarded and responses are cached in two tiers (L1 in-memory, L2 on disk). When the upstream is down, httptape transparently serves cached responses. See Proxy Mode for details.
Record SSE streams from OpenAI, Anthropic, or any SSE-based API. Each event is stored individually with timing metadata, so replay can simulate the original streaming behavior.
store := httptape.NewMemoryStore()
// Record -- SSE detection is automatic for text/event-stream responses.
sanitizer := httptape.NewPipeline(
httptape.RedactHeaders("Authorization"),
httptape.RedactSSEEventData("$.choices[*].delta.content"),
)
rec := httptape.NewRecorder(store,
httptape.WithSanitizer(sanitizer),
httptape.WithRoute("openai"),
)
defer rec.Close()
client := &http.Client{Transport: rec}
// Hit the real LLM API -- SSE events are captured with per-event timing.
resp, _ := client.Post("https://api.openai.com/v1/chat/completions",
"application/json", strings.NewReader(`{
"model": "gpt-4", "stream": true,
"messages": [{"role": "user", "content": "Hello"}]
}`))
io.Copy(io.Discard, resp.Body)
resp.Body.Close()
// Replay with instant timing for fast tests.
srv := httptape.NewServer(store, httptape.WithSSETiming(httptape.SSETimingInstant()))
ts := httptest.NewServer(srv)
defer ts.Close()
// Point your code at ts.URL -- streaming responses replay instantly.
See Recording and Replay for details on SSE support.
Go library:
go get github.com/VibeWarden/httptape
CLI:
go install github.com/VibeWarden/httptape/cmd/httptape@latest
Docker (~3 MB, multi-arch):
docker pull tibtof/httptape
store := httptape.NewMemoryStore()
rec := httptape.NewRecorder(store, httptape.WithRoute("github-api"))
defer rec.Close()
client := &http.Client{Transport: rec}
resp, err := client.Get("https://api.github.com/users/octocat")
// Tape is automatically saved to store
Strip secrets and fake PII -- on write, before anything hits disk:
sanitizer := httptape.NewPipeline(
httptape.RedactHeaders("Authorization", "Cookie"),
httptape.RedactBodyPaths("$.card.number", "$.ssn"),
httptape.FakeFields("my-seed", "$.email", "$.user_id"),
)
rec := httptape.NewRecorder(store, httptape.WithSanitizer(sanitizer))
Or declaratively via JSON config:
{
"version": "1",
"rules": [
{"action": "redact_headers", "headers": ["Authorization", "Cookie"]},
{"action": "redact_body", "paths": ["$.card.number", "$.ssn"]},
{"action": "fake", "seed": "my-seed", "paths": ["$.email", "$.user_id"]}
]
}
srv := httptape.NewServer(store)
ts := httptest.NewServer(srv)
defer ts.Close()
resp, err := http.Get(ts.URL + "/users/octocat")
Composable matching with weighted scoring:
srv := httptape.NewServer(store,
httptape.WithMatcher(httptape.NewCompositeMatcher(
httptape.MethodCriterion{}, // score: 1
httptape.PathCriterion{}, // score: 2
httptape.HeadersCriterion{Key: "Accept", Value: "application/json"}, // score: 3
httptape.QueryParamsCriterion{}, // score: 4
httptape.BodyHashCriterion{}, // score: 8
)),
)
// In-memory (for tests)
mem := httptape.NewMemoryStore()
// Filesystem (for fixtures)
fs := httptape.NewFileStore(httptape.WithDirectory("./testdata/fixtures"))
l1 := httptape.NewMemoryStore()
l2, _ := httptape.NewFileStore(httptape.WithDirectory("./cache"))
proxy := httptape.NewProxy(l1, l2,
httptape.WithProxySanitizer(sanitizer),
)
client := &http.Client{Transport: proxy}
// Upstream reachable: real response returned, cached in L1 + L2
// Upstream down: cached response returned transparently
The proxy can expose a small technical surface for operators and downstream UIs to react to upstream state changes in real time. Off by default; opt in with --health-endpoint (CLI) or WithProxyHealthEndpoint() (library):
httptape proxy --upstream https://api.example.com --fixtures ./cache \
--health-endpoint --upstream-probe-interval 2s
# JSON snapshot
curl http://localhost:8081/__httptape/health
# {"state":"live","upstream_url":"https://api.example.com","probe_interval_ms":2000,"since":"2026-04-16T10:00:00Z","last_probed_at":"2026-04-16T10:00:02Z"}
# SSE stream — one event on connect, one per state transition
curl -N http://localhost:8081/__httptape/health/stream
# retry: 2000
#
# data: {"state":"live", ... }
#
# data: {"state":"l1-cache", ... }
state mirrors the existing X-Httptape-Source header (live, l1-cache, l2-cache). With both flags absent, no endpoints are mounted and no probe goroutine is started — behavior is byte-for-byte unchanged.
Replay recorded SSE streams with configurable timing. Use SSETimingInstant() for fast tests:
srv := httptape.NewServer(store,
httptape.WithSSETiming(httptape.SSETimingInstant()),
)
ts := httptest.NewServer(srv)
defer ts.Close()
resp, _ := http.Get(ts.URL + "/v1/chat/completions")
// Events are streamed back instantly -- no waiting for original timing.
Other timing modes: SSETimingRealtime() preserves original inter-event gaps, SSETimingAccelerated(10) replays 10x faster.
// Export redacted fixtures as a portable bundle
r, _ := httptape.ExportBundle(ctx, store,
httptape.WithRoutes("stripe-api"),
httptape.WithSince(time.Now().Add(-24*time.Hour)),
)
// Import on another machine
httptape.ImportBundle(ctx, store, r)
httptape serve --fixtures ./mocks --port 8081
httptape record --upstream https://api.example.com --fixtures ./mocks --config redact.json
httptape proxy --upstream https://api.example.com --fixtures ./cache --config redact.json
httptape export --fixtures ./mocks --output bundle.tar.gz
httptape import --fixtures ./mocks --input bundle.tar.gz
Pull from either registry — the same image is published to both on every release:
docker pull tibtof/httptape # Docker Hub
docker pull ghcr.io/vibewarden/httptape # GHCR
Examples below use tibtof/httptape; substitute ghcr.io/vibewarden/httptape
freely.
# Replay mode
docker run -v ./mocks:/fixtures -p 8081:8081 tibtof/httptape serve --fixtures /fixtures
# Record mode (with redaction)
docker run -v ./mocks:/fixtures -v ./config.json:/config/config.json -p 8081:8081 \
tibtof/httptape record --upstream https://api.example.com \
--fixtures /fixtures --config /config/config.json
# Proxy mode (with fallback-to-cache)
docker run -v ./cache:/fixtures -v ./config.json:/config/config.json -p 8081:8081 \
tibtof/httptape proxy --upstream https://api.example.com \
--fixtures /fixtures --config /config/config.json
import httptapetest "github.com/VibeWarden/httptape/testcontainers"
container, err := httptapetest.RunContainer(ctx,
httptapetest.WithFixturesDir("./testdata/fixtures"),
)
defer container.Terminate(ctx)
// container.BaseURL() returns the mock server URL
resp, _ := http.Get(container.BaseURL() + "/api/users")
Runnable end-to-end examples live in examples/:
ts-frontend-first — Vite + React talking to an httptape proxy with live source-state updates over SSE. Demonstrates fallback-to-cache (live → L1 → L2), per-event redaction, and the /__httptape/health endpoint.More examples coming. See examples/README.md for the index.
| Feature | httptape | WireMock | json-server | MSW | gock |
|---|---|---|---|---|---|
| Embeddable in Go | yes | no (Java) | no (Node) | no (browser) | yes |
| Standalone server | yes | yes | yes | no | no |
| Docker | 3 MB | 200 MB+ | 50 MB+ | n/a | n/a |
| Recording | yes | yes | no | no | no |
| Redaction on write | yes | no | no | no | no |
| Deterministic faking | yes | no | no | no | no |
| Proxy with fallback | yes | no | no | no | no |
| SSE record/replay | yes | no | no | partial | no |
| Frontend mock backend | yes | yes | yes | yes (browser) | no |
| Fixture import/export | yes | partial | no | no | no |
| Dependencies | zero | JVM | npm | npm | 1 |
| Decision | Choice | Reason |
|---|---|---|
| Dependencies | stdlib only | Zero transitive deps for embedders |
| Redaction | On write | Sensitive data never touches disk |
| Faking | HMAC-SHA256 | Deterministic -- same input always produces the same fake |
| Fixtures | JSON | Human-readable, easy to inspect and edit |
| Storage | Pluggable | MemoryStore for tests, FileStore for persistence |
| Recording | Async by default | Non-blocking, minimal hot-path overhead |
| Matching | Composable | Start simple, add specificity as needed |
| Proxy | L1/L2 caching | Raw in-memory for session, redacted on disk for persistence |
Full docs at vibewarden.dev/docs/httptape.
httptape is developed as part of VibeWarden. For the full docs, guides, and other projects from the VibeWarden team, visit vibewarden.dev.
Content type
Image
Digest
sha256:eff550ccc…
Size
3.4 MB
Last updated
about 1 month ago
docker pull tibtof/httptape