Start here

Core concepts

The handful of ideas every SDK, the CLI and the dashboard share. Learn these once and every platform page reads the same.

Capsule

A .bugcapsule file is gzip-compressed UTF-8 JSON with three parts:

Replay re-runs the real app code against that recording. Network calls are served from the capsule with the recorded timing, so races and data-dependent bugs come back. Full spec: Capsule format.

Flight recorder and capture

While a session records, the SDK keeps a bounded in-memory window: 5 minutes or 20,000 events. Nothing is written until a failure happens (or QA presses capture). Then the SDK writes a capsule (300 ms later, to let the failure settle; crashes immediately), redacts it, saves it locally and queues it for upload.

TriggerFailure kindNotes
Uncaught error / crashexceptionBoundaries differ per runtime (see each SDK page)
check(id, condition) / fail(id)assertionSignature assertion:<id>: everywhere
reportError(e)exceptionErrors your code caught
Manual capture(note)the last failure, or noneWithout a failure the capsule is ineligible

At most 3 automatic capsules per session, one per failure signature.

Capture modes

ModeRecordsTypical build
debugAlwaysLocal development
qaAlwaysQA and internal testing builds
productionWith probability min(productionSampleRate, remote sampleRate), decided at launch (default cap 10%)Store builds

Defaults when you don't set a mode: Flutter kReleaseMode ? production : debug, Android debuggable → debug, iOS DEBUG → debug, web always production. Sessions that aren't sampled install no recorders.

Checkpoints

Storage is captured only from an allowlist you configure, and passes through redaction. This is why init must run first: in replay it restores the checkpoint before app code reads storage.

Eligibility

The device computes it when writing the capsule, and lists every gap in eligibility.missing:

StatusMeaningCommon causes
eligibleEverything replay needs is there
partialReplayable, with known gapsNo git SHA, a fixture without a body, crash.persisted, a capability the SDK lacks
ineligibleA blocking gapNo failure assertion (manual capture)

Ineligible capsules are still stored and inspectable, but never counted in the reproduction rate.

Making a bug replayable

ConceptWhat it buys you
Semantic actions: wrap a handler with an id (cart.checkout)Replay invokes it by id even if the developer's screen differs. Required on iOS (no pointer replay).
Tracked text: register an input with an idTyped text is restored by id. Sensitive fields record [REDACTED].
Assertions: check / failWrong behaviour that doesn't throw becomes a failure replay can detect.
Deterministic hooks: SDK clock and randomReplay returns the recorded wall clock and seed. Only the SDK hooks are virtualized; Date.now(), timers and animations are not.
Git SHA at build timebugcapsule run checks out the exact commit that failed.

Request keys and endpoint rules

Each HTTP exchange is stored under a normalized key: METHOD host path?sortedQuery#bodyHash, plus an occurrence (0-based count of earlier requests with the same key). Replay serves the fixture with the same key and occurrence, after the recorded latency. Every runtime computes identical keys (checked by shared conformance fixtures).

Fields that change on every run (timestamps, nonces, request ids) would break matching, so drop them with an endpoint rule:

EndpointRule('POST', '/api/checkout', ignoreBodyFields: {'timestamp', 'nonce'})

Query params _ ts timestamp nonce cachebust are ignored by default. Rules can also restrict hashing to matchBodyFields. Bodies up to 64 KiB are hashed (never stored for the key); multipart is never hashed.

Replay is strict by default: a request the recording never saw fails like a connection error and reports network.unrecorded. Nothing reaches real services. --lenient-network lets it through but still reports it.

Credentials

CredentialPrefixWho holds itCan do
SDK tokenbcsdk.v1.The app (public)Read capture config, upload capsules and monitoring data. Carries the BugCapsule address. Rotate with a grace period, or revoke
Ingest keybci_The app, in setups from before SDK tokens (public)Read capture config, upload capsules
Secret keybcs_The CLI and CI (never an app)List, download, replay and triage your project's capsules; change capture settings
Share tokenin URLAnyone with the linkDownload one capsule until expiry

Capsule status

new ──run: reproduced/flaky──► reproduced ──verify: clean in every attempt──► fixed
 │                                │
 └─run: clean in every attempt──► not_reproduced         ineligible (on upload)   archived (manual)

Verdicts

The CLI judges each attempt (reproduced, clean or invalid), then the batch:

runverifyExit
REPRODUCEDFIXED0
NOT REPRODUCEDSTILL FAILING1
FLAKYFLAKY2
INCONCLUSIVE / INCOMPATIBLEINCONCLUSIVE / INCOMPATIBLE3

Attempts that never ran count against the request: one clean attempt plus one error is INCONCLUSIVE, never FIXED. Details and the divergence catalogue: Verdicts & divergences.

Remote config and the kill switch

Each device fetches your project's capture settings after startup, then every few minutes (default 5) and when the app returns to the foreground. You change them with bugcapsule capture or in the dashboard: capture on/off, sample rate, maximum capsule size, body capture, extra redaction keys and the refresh interval. They can only make capture more conservative: lower sampling, turn bodies off, add redaction, or switch capture off.

Nothing is uploaded until the settings have been fetched once (or a cached copy is younger than 7 days). The kill switch stops recording, drops the buffer, deletes queued and saved capsules, and persists across launches.

Glossary

Anchor
The first rendered frame; event t is milliseconds since it (negative before)
Divergence
Something replay couldn't reproduce exactly; material ones invalidate a clean attempt
Fixture
A recorded HTTP response served during replay
Integration
A plug-in (HTTP client, router, storage, state manager) that records events and owns one checkpoint key
Outbox
On-device upload queue: 10 capsules / 10 MiB / 7 days
Runtime
flutter, android, ios or web
Signature
<kind>:<type>:<normalized message>, the key replay compares failures by