tibtof/httptape

By tibtof

Updated about 1 month ago

HTTP traffic recording, redaction, and replay — embeddable Go library, CLI, and 6 MB Docker image.

Image
Security
Integration & delivery
Developer tools
0

10K+

tibtof/httptape repository overview

httptape logo

Record, Redact, Replay

HTTP traffic recording, redaction, and replay.
Embeddable Go library · CLI · Docker · Testcontainers

Go Reference Tests OpenSSF Scorecard License Docker Image Size


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:

  1. Record -- capture real HTTP traffic via a transparent http.RoundTripper
  2. Redact -- strip secrets and PII on write, before anything touches disk
  3. Replay -- serve recorded fixtures as a deterministic mock server

Why httptape?

  • WireMock requires Java -- separate process, 200 MB+ memory, can't embed in a Go binary
  • Go mocking libraries (gock, httpmock) only work inside test code -- no standalone server, no recording, no fixture management
  • json-server / Mockoon -- no recording, no redaction, manual fixture writing only
  • Nobody does redaction -- existing tools record raw traffic including secrets and PII. httptape redacts on write -- sensitive data never hits disk
  • SSE record and replay -- record Server-Sent Event streams (LLM completions, real-time feeds) with per-event timing, replay them with configurable speed (realtime, accelerated, or instant), and redact PII from individual event payloads. No other Go mocking tool does this

Use cases

Integration testing

Record 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 ...
Frontend-first development

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.

Production traffic capture

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.

Fallback proxy

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.

Recording LLM streaming responses

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.

Install

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

Quick start

Record
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
Redact

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"]}
  ]
}
Replay
srv := httptape.NewServer(store)
ts := httptest.NewServer(srv)
defer ts.Close()

resp, err := http.Get(ts.URL + "/users/octocat")
Match

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
    )),
)
Store
// In-memory (for tests)
mem := httptape.NewMemoryStore()

// Filesystem (for fixtures)
fs := httptape.NewFileStore(httptape.WithDirectory("./testdata/fixtures"))
Proxy (fallback-to-cache)
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
Proxy health endpoints (opt-in)

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.

SSE replay

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.

Import / Export
// 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)

CLI

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

Docker

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

Testcontainers

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")

Examples

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.

How it compares

FeaturehttptapeWireMockjson-serverMSWgock
Embeddable in Goyesno (Java)no (Node)no (browser)yes
Standalone serveryesyesyesnono
Docker3 MB200 MB+50 MB+n/an/a
Recordingyesyesnonono
Redaction on writeyesnononono
Deterministic fakingyesnononono
Proxy with fallbackyesnononono
SSE record/replayyesnonopartialno
Frontend mock backendyesyesyesyes (browser)no
Fixture import/exportyespartialnonono
DependencieszeroJVMnpmnpm1

Key design decisions

DecisionChoiceReason
Dependenciesstdlib onlyZero transitive deps for embedders
RedactionOn writeSensitive data never touches disk
FakingHMAC-SHA256Deterministic -- same input always produces the same fake
FixturesJSONHuman-readable, easy to inspect and edit
StoragePluggableMemoryStore for tests, FileStore for persistence
RecordingAsync by defaultNon-blocking, minimal hot-path overhead
MatchingComposableStart simple, add specificity as needed
ProxyL1/L2 cachingRaw in-memory for session, redacted on disk for persistence

Documentation

Full docs at vibewarden.dev/docs/httptape.

About

httptape is developed as part of VibeWarden. For the full docs, guides, and other projects from the VibeWarden team, visit vibewarden.dev.

License

Apache 2.0

Tag summary

Content type

Image

Digest

sha256:eff550ccc

Size

3.4 MB

Last updated

about 1 month ago

docker pull tibtof/httptape