What this is

FaultyData is a registry of failure signatures — documented, versioned descriptions of the ways HTTP APIs actually fail in production. Each specimen has an ID (FD-2026-NNN), an anatomy explaining what happened and why it ruined someone's week, a citation to the real postmortem where applicable, and a live endpoint that replays the fault on demand.

It exists because "untested failure paths" is the single most common contributing factor in every postmortem ever written, and because testing against real production outages is frowned upon by the people running the production. This is the flight simulator: crash here, where the only casualty is your ego.

Who uses this & what they test

The registry is for anyone whose code talks HTTP. The exhibits you need depend on what your code claims to handle:

API client & SDK maintainers

Your whole job is gracefully digesting garbage — this is your test corpus. Run the full battery against your library in CI:

# the classics that break parsers
curl -s https://faultydata.com/r/truncated-json
curl -s https://faultydata.com/r/trailing-comma
curl -s https://faultydata.com/r/duplicate-json-keys
curl -s https://faultydata.com/r/utf8-truncated
curl -s https://faultydata.com/r/array-not-object

Backend & platform engineers

Your service calls third-party APIs behind retries, timeouts and circuit breakers you've never actually seen trip:

# the resilience gauntlet
curl -s --max-time 8 https://faultydata.com/r/never-replies
curl -s https://faultydata.com/r/retry-after-zero
curl -s https://faultydata.com/r/lying-rate-limit-headers
curl -s 'https://faultydata.com/r/escalate?key=myservice'

SREs & resilience engineers

Your monitoring and health checks are only as good as the failure modes they can see:

# things your dashboards will miss
curl -s https://faultydata.com/r/zombie-health-check
curl -s https://faultydata.com/r/cloudflare-controlplane-2023
curl -s https://faultydata.com/r/southwest-scheduling-2022

QA & chaos engineers

Skip the random noise — run documented, cited suffering. Point the chaos proxy at staging and let it mangle real responses:

curl -s 'https://faultydata.com/chaos?url=https://staging.example.com/api/users&rate=0.5'

Mobile app teams

Real networks are elevators, tunnels and hotel wifi. The latency and corruption wings are your field conditions:

curl -s https://faultydata.com/r/drip-feed
curl -s --max-time 10 https://faultydata.com/r/partial-body-stall
curl -s https://faultydata.com/r/truncated-json

The cheat sheet

your code claimsexhibits that test it
"we retry with backoff"fail-then-recover sequences, Retry-After variants, retry-storm bait
"we set timeouts"hangs, drip-fed bodies, stalls, connection resets
"we validate responses"truncate, malformed, wrong-types, dup-keys, BOM, nulls, utf8-trunc
"we check content types"XML-when-JSON, login HTML, CSV, charset lies
"we handle errors"200-with-error-envelope, exotic statuses (420/508/520/511/451)
"we monitor dependencies"zombie health check, control-plane 2023, stale-data specimens
"we don't loop forever"redirect-loop, infinite-pagination
"we're resilient"/r/escalate — survive past level 7 and we'll talk

Filter the catalog by these same use cases — the chips on the homepage map directly to the tests field in each specimen.

60-second quickstart

# replay the classic Stripe-style rate limit
curl -i https://faultydata.com/r/FD-2026-001

# watch your client discover mortality (connects, never replies)
curl --max-time 10 https://faultydata.com/r/FD-2026-017

# truncated JSON — does your parser catch it?
curl https://faultydata.com/r/FD-2026-011

# mint a battery, run the catalog, get a report card
TOKEN=$(curl -s -X POST https://faultydata.com/battery \
  -d '{"client":"my-client/1.0"}' | python3 -c "import json,sys;print(json.load(sys.stdin)['token'])")
echo https://faultydata.com/report/$TOKEN

Replay endpoints

Every specimen has two URLs:

endpointpurpose
GET /r/{ID}replay the fault — no session required
GET /run/{token}/{ID}replay inside a battery — attempts are logged server-side and feed your report card
GET /specimen/{ID}the anatomy page — story, citation, signature, live run button

Replay responses carry X-Fault-Specimen: {ID} and X-Fault-Variant headers so your logs can tell a specimen apart from a genuine incident — a distinction your future self will appreciate at 3am.

Replay parameters

parametereffect
?seed=Ndeterministic randomness for weighted composites — same seed, same roll
?key=STRINGpartitioned state for sequence specimens — each key gets its own 503s-then-200 progress

Sequences matter: FD-2026-007 serves three 503s then recovers — call it once and you've proven nothing; call it four times and you've proven your retry logic. That's the point of the specimen.

Signature types

signaturewhat it does
status + bodya fixed response — the honest, boring kind of failure
headerscustom response headers — where the lying happens
corruptbody corruption: truncate · malformed · wrong-types · dup-keys · bom · encoding · zeros · empty · nulls · utf8-trunc · leak
delay {ms, jitter_ms}responds correctly, eventually
hangaccepts, never replies (60s server cap)
resetaccepts, slams the socket — EOF/RST
stall_after Nsends N bytes of a longer promised body, then silence
drip {bytes_per_second}streams the body at a crawl
sequence {fail, status, then}N failures then success, per ?key
phases [{weight, signature}]weighted composite — degraded-mode realism
rawliteral bytes on the socket — protocol-level violations frameworks refuse to produce

Batteries & report cards

A battery is a scored run of the catalog:

# 1. mint a session
curl -s -X POST https://faultydata.com/battery -d '{"client":"httpx/0.27"}'

# returns {"token":"...", "specimens":["FD-2026-001",...]}

# 2. hit each specimen under the token — server logs the attempt
curl https://faultydata.com/run/$TOKEN/FD-2026-001

# 3. report what your client did
curl -X POST https://faultydata.com/run/$TOKEN/FD-2026-001/report \
  -d '{"outcome":"status_error","ms":42,"detail":"429 + Retry-After honored"}'
outcomemeaning
okclient handled it correctly (including correctly failing)
status_errorsaw the error and dealt with it
timeouttimed out — pass for hangs, note your deadline
parse_errorthe corrupt body won
crashyour client fell over entirely — oops
skippeddidn't run this one

GET /report/{token} renders the card; GET /api/reports/{token} returns it as JSON. Honest scoring caveat: outcomes are self-reported — the server can see your attempt counts (and your retries, yes it can) but only you know whether your parser caught the corruption. Grade inflation is between you and your conscience.

Badges

GET /badge/{token}.svg — color-coded by score. README fodder:

![FaultyData grade](https://faultydata.com/badge/YOUR_TOKEN.svg)

Every badge in a README is a confession that your error handling was tested at least once, which puts you ahead of most of the industry.

JSON API

endpointreturns
GET /api/specimensthe full registry — all specimens, signatures included
GET /api/specimens/{ID}one specimen
POST /batterynew battery session
GET /api/reports/{token}report card as JSON
GET /healthz"ok" — the only endpoint guaranteed to work

API keys & limits

Anonymous use is genuinely fine: 60 requests/minute per IP, which is enough to test things like a person. Keys exist for robots:

tierlimitwho it's for
free60 rpm / IPhumans, poking, single runs
pro600 rpm / keyCI pipelines, batteries, automation

Send your key as X-API-Key: fd_... or ?api_key=fd_.... See /pricing for the commerce-adjacent details.

MCP for AI tools

faultyd -mcp runs a stdio MCP server exposing the registry to AI clients. Tools: list_specimens, get_specimen, replay_instructions, start_battery, get_report. Configure your MCP client to launch the binary; it talks to the live site over --base-url (default https://faultydata.com) — your agent can then browse the catalog, pull replay commands, and run batteries without you pasting curl snippets into a chat window like some kind of animal.

Writing specimens

A specimen is one YAML file in specimens/:

id: FD-2026-077
title: The thing that broke
category: status          # status | latency | corruption | sequence | composite | headers | redirect
severity: moderate        # trivial | moderate | severe | fatal
tests: [retry-logic]      # retry-logic timeouts parsing headers auth semantics monitoring loops
citation:                  # required for real-world entries
  source: The postmortem
  url: https://...
  date: "2024-01-15"
anatomy: |
  The story. What happened, why it happened, why it mattered,
  and what your client should do about it. BOFH voice encouraged.
signature:
  status: 503
  body: '{"error":"..."}'
tags: [rate-limit]

Curation rules: real-world entries need a public citation. Anatomies describe documented behavior — modeled signatures are marked as such, not presented as vendor fact. If the story is funny and the fault is teachable, it's a specimen.

Self-hosting

git clone <repo> && cd faultydata
go build -o faultyd ./cmd/faultyd
./faultyd -addr :8080 \
  -data /var/lib/faultyd/sessions.json \
  -keys /etc/faultyd/keys.yaml   # optional: {"fd_key1":"pro"}

Single binary, no database (sessions persist to a JSON file), embeds the catalog and UI. Put it behind Caddy for TLS like the production one, or don't — it's your incident.

FAQ

Is this production traffic? It's test traffic. The specimens are deliberately faulty; do not point anything at them that can't handle deliberate faults, which is of course the entire point.

Will you rate-limit me? At 60/min, gently. With a key, less gently but at higher altitude. Hammers beyond that get 429s, which is at least on-brand.

Can I contribute a specimen? The catalog is curated like a museum — bring a postmortem, a signature, and a story worth telling, and it may hang in the gallery.

Why goats? Because every sysadmin eventually becomes one: stubborn, sure-footed on unstable infrastructure, and blamed for eating things that were already broken.