flakybin

June 7, 2026 · View on GitHub

Go Report Card GitHub License GitHub Release Go Version GitHub Stars

Deterministic HTTP chaos for testing uptime monitors and retry logic.

flakybin is a fault-injection server that fails on a deterministic, seed-reproducible schedule: it returns errors, hangs, and drops connections during scheduled outage windows, emulating a service that goes down on a repeating cycle.

Outage placement is computed purely from the request URL. The same parameters always yield the same outage windows on the wall clock, across restarts and across machines. No state, no database.

Run

go run .            # listens on :8080 (override with -addr or PORT)
PORT=9000 go run .

go install github.com/mikluko/flakybin@latest   # or install the binary

Then open http://localhost:8080/ for the interactive schedule explorer, or http://localhost:8080/docs for the full reference.

Modes

The first path segment selects how outages are placed within each period.

  • even (default) — count outages of length duration spaced uniformly across the period (one per period/count interval); the seed shifts the whole pattern's phase. No divisibility constraint; regular and predictable.
  • jittercount outages of length duration are dropped at seed-hashed offsets anywhere within the period. No divisibility constraint; looks like organic flapping.
  • noise — the period is divided into N = period/duration fixed slots; count of them are outages. period must divide evenly by duration. Grid-aligned.

Common parameters

parammeaning
periodlength of one repeating cycle, e.g. 24h, 1h, 10m (default 24h)
durationlength of a single outage, e.g. 5m (default 15m)
countnumber of outages per period — supply this or uptime (not both)
uptimetarget availability percent, e.g. 99.9; converted to a count. Defaults to 90 when neither count nor uptime is given. 100 → 400
seedRNG seed (default 0); change to reshuffle placement

Endpoints

routeduring an outage
GET /{mode}/status/{code}responds with HTTP {code}; 200 when up. Optional retry-after=auto or retry-after=<seconds> sets Retry-After.
GET /{mode}/hangwithholds the response until the outage ends, or for for=<duration>. Emulates a timeout.
GET /{mode}/dropwrites after=<bytes> of body (default 0) then resets the TCP connection.
GET /{mode}/inspectshows the schedule and its outage windows. Browsers get a graphical timeline, scripts get JSON (override with ?format=html/?format=json). The JSON includes a failure_links block with ready-to-call URLs for every status code plus hang and drop. Triggers no failure.

Examples

# Even (default): ~99% availability over a 24h period, evenly spaced, 503 + Retry-After.
curl -i 'localhost:8080/even/status/503?period=24h&duration=5m&uptime=99&retry-after=auto'

# Ten 1-minute blips scattered through each hour, seed 42.
curl -i 'localhost:8080/jitter/hang?period=1h&duration=1m&count=10&seed=42'

# Noise: 24h period, 5m slots, 12 down (~99.58% uptime).
curl -i 'localhost:8080/noise/status/503?period=24h&duration=5m&count=12&retry-after=auto'

# Noise, first 10 minutes of every hour are down.
curl -i 'localhost:8080/noise/status/500?period=1h&duration=10m&count=1'

# Inspect the schedule without triggering it.
curl -s 'localhost:8080/jitter/inspect?period=24h&duration=5m&uptime=99'

Response headers

Every response advertises the resolved schedule and current state via X-Flaky-* headers: Mode, Period, Duration, Count, Seed, Uptime, In-Outage, and either Outage-Ends or Next-Outage (RFC 3339).

Release

The version comes from the git tag. Tag and push, then build the OCI image (via ko, no Dockerfile) and the Helm chart together:

git tag v0.1.0 && git push --tags
make build          # ko build --bare + helm package/push to the OCI registry

make build pushes ghcr.io/mikluko/flakybin:<version> and the chart to oci://ghcr.io/mikluko/flakybin/charts/flakybin. Run make help for individual targets.

Deploy

Install from the OCI chart (or the local charts/flakybin path):

helm install flakybin oci://ghcr.io/mikluko/flakybin/charts/flakybin --version 0.1.0 \
  --set ingress.enabled=true \
  --set ingress.hosts[0].host=flakybin.example.com

The container listens on :8080, runs as non-root with a read-only root filesystem, and uses GET / for liveness/readiness. Set flakybin.quiet=true to disable per-request access logging. See charts/flakybin/values.yaml for all options.