Quickstart

1. Is my path to S3 healthy? (zero-config)

No capture needed. check drives its own probe:

$ s3tap check                    # regional round-trip map + a health check nearby
$ s3tap check my-bucket/my-key   # a verdict on YOUR object (strict exit code)

check is the friendly front-end to doctor --live. It loads eBPF. Without the uprobe caps it only judges the network floor: the fastest round trip the network physically allows. See check.

2. Capture an app, then judge it

Capture the S3 traffic of a workload as JSONL, then pipe it into doctor:

$ sudo s3tap --app python3 --format jsonl > capture.jsonl
# ...run your workload...
$ s3tap doctor --from capture.jsonl

Or straight through in one pipe:

$ sudo s3tap --app python3 --format jsonl | s3tap doctor

doctor exits non-zero when any metric is outside its envelope (⚠). So it drops into CI as a gate. See doctor.

--from wants a capture and it checks. doctor, advise and scorecard refuse an input that parsed to zero s3tap.* records: they print what they actually read and exit 4 (tool failure), rather than judging an empty set. A doctor --json findings file is not a capture. Neither is an empty file, a truncated one, or the wrong log. This matters because the old behaviour was silent: advise --strict over a broken path printed "no advisories" and exited 0, so the gate had been dead for as long as the path was wrong. Exit 1 always means a verdict about your traffic. Exit 4 means fix the command.

A capture with no S3 request in it is not a pass either. If nothing decoded the HTTP layer (a Go or rustls client, or a capture taken without the uprobe caps) doctor reports NO OPERATIONS and exits 2. Only the network path below S3 was judged, so a green verdict there would say something the capture cannot support. A capture whose requests were decoded but never answered, which is what Ctrl-C mid-workload leaves behind, reports NO RESPONSES and exits 2 on the same reasoning.

3. How is the app using S3?

$ s3tap advise --from capture.jsonl

Optimization advice (client churn, missing parallelism, redundant re-fetches, throttling, caching go/no-go), attributed per process. See advise.

4. Machine-readable output

Every consumer speaks NDJSON findings for fleet ingest:

$ s3tap doctor --from capture.jsonl --json     # one s3tap.finding/1 per check + a run roll-up

5. Track a regression

$ s3tap doctor --from after.jsonl --baseline before.jsonl   # gate: fails on a regression

--baseline takes a capture of records, the same JSONL --from reads, not a findings file. Keep a known-good capture around as the reference. If you have no capture to keep, s3tap doctor --live --endpoint <url> --save baseline.jsonl drives its own workload and writes one.

--baseline is checked the same way --from is. A baseline path that opens but parses to zero s3tap.* records is refused with exit 4 rather than accepted as a baseline of nothing. It used to be accepted. An empty one did not even print a note, so a healthy capture passed the gate against a file that was never a capture. The --baseline section of the doctor metrics reference has the detail.

--save creates its file with O_EXCL at mode 0600, so it refuses a path that already exists and never truncates or follows a symlink. Re-running the recipe above over the same filename therefore fails, up front, before any traffic is driven. Delete or rename the old capture first, or save to a new name. There is no --force. The reason is that doctor --live routinely re-execs under sudo, so that write commonly happens as root with a path taken from an argument. Mode 0600 is for the same reason: a capture names your buckets, endpoint IPs, SNI hostnames and key hashes.

Composition

Because the record is the contract, you can capture once, then re-judge it offline, diff it or feed it to a different tool. The capture and the analysis never have to happen at the same time or place.