Filecoin
Dealbot Events & Metrics
Dealbot Methodology
This document is the intended source of truth for the events emitted by dealbot checks and the metrics computed from them. It is intended for dealbot dashboard consumers and maintainers who need to understand what each metric means and where it comes from.
Note on "events": the entries in the Event List are named timing markers used to define metric Timer Starts/Ends — they are not all emitted as discrete Prometheus events or log lines. Each marker is anchored in code (as a timestamp variable, log line, or status transition) and used to compute the metrics in the Metrics section.
Rendered from the dealbot documentation · cached for 24 hours
Data Storage Event Model
Below are the sequence of events for a Data Storage check. The Data Storage flow is used because it encapsulates a Retrieval check as well.
Data Storage Event Timeline
sequenceDiagram
autonumber
participant Dealbot
participant SP as PDP Storage Provider
participant RPC as Chain RPC Provider
participant FPNI as filecoinpin.contact IPNI Instance
participant CPNI as cid.contact IPNI Instance
rect rgba(120, 120, 200, 0.15)
%% Data Storage Only
Dealbot->>SP: uploadToSpStart
SP-->>Dealbot: uploadToSpEnd (2xx, piece CID)
Dealbot-->>Dealbot: dealCreated (upload result returned)
SP-->>Dealbot: pieceAdded (tx hash, async)
RPC-->>Dealbot: pieceConfirmed (async)
SP-->>Dealbot: spIndexingComplete
SP-->>Dealbot: spAnnouncedAdvertisementToIpni
end
Dealbot->>FPNI: ipniVerificationStart (filecoinpin.contact)
FPNI-->>Dealbot: ipniVerificationComplete (filecoinpin.contact)
opt filecoinpin.contact verified
Dealbot->>CPNI: ipniVerificationStart (cid.contact)
CPNI-->>Dealbot: ipniVerificationComplete (cid.contact)
end
Dealbot-->>SP: ipfsRetrievalStart
SP-->>Dealbot: ipfsRetrievalFirstByteReceived
SP-->>Dealbot: ipfsRetrievalLastByteReceived
Dealbot-->>Dealbot: ipfsRetrievalIntegrityChecked
Event List
| Event | Definition | Relevant Checks | Source of truth |
|---|---|---|---|
uploadToSpStart |
Dealbot is about to start an upload attempt for a piece to an SP. | Data Storage | deal.service.ts (anchor: deal.uploadStartTime) |
uploadToSpEnd |
Upload finished (success with HTTP 2xx, failure). | Data Storage | deal.service.ts (handleStored) |
dealCreated |
Deal reaches DealStatus.DEAL_CREATED after all sub-checks (upload, onchain, IPNI, retrieval) succeed. Upload completion alone sets DealStatus.UPLOADED, not DEAL_CREATED. |
Data Storage | deal.service.ts |
pieceAdded |
Piece submission is recorded on-chain. Driven by Synapse piecesAdded progress event; transaction hash is known. |
Data Storage | deal.service.ts |
pieceConfirmed |
Piece is confirmed on-chain. Driven by Synapse piecesConfirmed progress event. |
Data Storage | deal.service.ts (sets piecesConfirmedTime, observes pieceConfirmedOnChainMs histogram) |
spIndexingComplete |
By polling SP, dealbot learned SP has indexed the piece locally (indexed=true). When the SP returns indexedAt, dealbot uses that SP timestamp; otherwise it uses the local poll time. |
Data Storage | ipni.strategy.ts |
spAnnouncedAdvertisementToIpni |
By polling SP, dealbot learned SP has announced the advertisement to IPNI (advertised=true). When the SP returns advertisedAt, dealbot uses that SP timestamp; otherwise it uses the local poll time. |
Data Storage | ipni.strategy.ts |
ipniVerificationStart |
Dealbot begins polling an IPNI indexer for a ipniVerifyMs timer starts: for Data Storage {indexer="filecoinpin.contact"}, prefer the SP's advertisedAt when available and sane (otherwise the local time dealbot observed advertised=true while polling the SP; if no advertisement timestamp exists, fall back to this polling-start time). For Data Storage {indexer="cid.contact"}, use filecoinpin.contact verification completion. Retrieval checks always anchor from this local polling-start time. |
Data Storage, Retrieval | ipni.strategy.ts, ipni-verification.service.ts |
ipniVerificationComplete |
An IPNI indexer call completes (pass or timeout). Occurs once per indexer: filecoinpin.contact always, cid.contact only after filecoinpin.contact verified. | Data Storage, Retrieval | ipni.strategy.ts |
ipfsRetrievalStart |
Dealbot to SP /ipfs/ retrieval begins. |
Data Storage, Retrieval | retrieval-addons.service.ts (anchor: retrieval startTime; logs retrieval_started) |
ipfsRetrievalFirstByteReceived |
First byte received from /ipfs/{rootCid}. |
Data Storage, Retrieval | retrieval-addons.service.ts (drives ipfsRetrievalFirstByteMs) |
ipfsRetrievalLastByteReceived |
Last byte received from /ipfs/{rootCid}. |
Data Storage, Retrieval | retrieval-addons.service.ts (drives ipfsRetrievalLastByteMs) |
ipfsRetrievalIntegrityChecked |
Retrieved content matches expected CID (per-block sha256 hash verification via createBlock). Inline check at end of DAG traversal; no discrete event emission. |
Data Storage, Retrieval | ipfs-block.strategy.ts |
Pull Check Event Model
Below are the events for a Pull Check. Pull checks reverse the data flow of the Data Storage check: instead of dealbot uploading bytes, it asks the SP to pull bytes from a temporary /api/piece/{pieceCid} URL.
Pull Check Event Timeline
sequenceDiagram
autonumber
participant Dealbot
participant SP as PDP Storage Provider
participant RPC as Chain RPC Provider
Dealbot->>Dealbot: hostedPieceRegistered
Dealbot->>SP: pullRequestSubmittedToSp (pullPieces)
SP-->>Dealbot: pullRequestAcknowledgedBySp
SP-->>Dealbot: pullRequestStartedBySp
Dealbot->>SP: pullStatusPolled (waitForPullPieces, repeated)
SP-->>Dealbot: pullRequestIsTerminal
Dealbot->>SP: directPieceFetchStarted (/piece/{cid})
SP-->>Dealbot: directPieceFetchCompleted
Dealbot-->>Dealbot: pullRequestIntegrityChecked
Pull Check Event List
| Event | Definition | Implemented | Source of truth |
|---|---|---|---|
pullRequestSubmittedToSp |
Dealbot calls Synapse pullPiece (POST /pdp/piece/pull) against the SP for the registered piece CID. |
Yes | pull-check.service.ts |
pullRequestAcknowledgedBySp |
SP returns from pullPieces (success or non-terminal-failure). |
Yes | pull-check.service.ts |
pullRequestStartedBySp |
Dealbot receives SP request for /api/piece/{pieceCid} from dealbot. Recorded once per registration. |
Yes | pull-piece.controller.ts |
pullRequestIsTerminal |
Dealbot determines the pull request is in terminal pull status (complete, failed, ...) via waitForPullPieces or the polling operation has timed out. Intermediate poll statuses are not counted. |
Yes | pull-check.service.ts |
pullRequestIntegrityChecked |
Dealbot completed direct /piece/{pieceCid} retrieval from the SP and confirmed the bytes match the pieceCid. |
Yes | pull-check.service.ts |
Sampled Retrieval Check Event Model
Below are the events for an Sampled Retrieval check. Unlike the Data Storage flow, sampled retrieval is a single-shot per-piece flow: select a publicly-discoverable piece from the FWSS subgraph → fetch the piece from the SP → optionally parse the CAR, verify IPNI, and sample block fetches → write one row to ClickHouse. The check emits one row to sampled_retrieval_checks even on abort or unexpected error, so partial timing/byte evidence is never lost.
Sampled Retrieval Check Event Timeline
sequenceDiagram
autonumber
participant Dealbot
participant Subgraph as Subgraph
participant SP as Storage Provider
participant IPNI as filecoinpin.contact
Dealbot->>Subgraph: sampledPieceSelectionStart
Subgraph-->>Dealbot: sampledPieceSelected
Dealbot->>SP: sampledPieceFetchStart (GET /piece/{pieceCid})
SP-->>Dealbot: sampledPieceFetchFirstByteReceived
SP-->>Dealbot: sampledPieceFetchLastByteReceived
Dealbot-->>Dealbot: sampledCommPVerified
opt if piece advertises IPFS indexing
Dealbot-->>Dealbot: sampledCarParsed
Dealbot->>IPNI: sampledIpniVerificationStart
IPNI-->>Dealbot: sampledIpniVerificationComplete
Dealbot->>SP: sampledBlockFetchStart (GET /ipfs/{cid}?format=raw, sampled)
SP-->>Dealbot: sampledBlockFetchComplete
end
Dealbot-->>Dealbot: sampledRetrievalCheckComplete
Sampled Retrieval Check Event List
| Event | Definition | Source of truth |
|---|---|---|
sampledPieceSelectionStart |
Dealbot begins selecting a sampled piece for the SP under test from Dealbot's subgraph (size-bucket + indexed/any pool sampling with fallbacks). | sampled-piece-selector.service.ts |
sampledPieceSelected |
Subgraph returned a candidate piece (or all fallbacks were exhausted and the check is recorded as skipped). |
sampled-piece-selector.service.ts |
sampledPieceFetchStart |
Dealbot initiates GET {spBaseUrl}/piece/{pieceCid}. |
piece-retrieval.service.ts (logs sampledRetrieval_started) |
sampledPieceFetchFirstByteReceived |
First byte received from /piece/{pieceCid}. |
piece-retrieval.service.ts (drives sampledPieceRetrievalFirstByteMs) |
sampledPieceFetchLastByteReceived |
Last byte received from /piece/{pieceCid}. |
piece-retrieval.service.ts (drives sampledPieceRetrievalLastByteMs) |
sampledCommPVerified |
Response bytes hashed and the resulting CommP compared against the declared pieceCid. Inline check; no discrete event emission. Failure flips piece fetch to failure.commp. |
piece-retrieval.service.ts |
sampledCarParsed |
Fetched piece bytes are parsed as a CAR and a random sample of SAMPLED_RETRIEVAL_BLOCK_SAMPLE_COUNT CIDs is selected. Only emitted when the piece advertises IPFS indexing. |
piece-validation.service.ts |
sampledIpniVerificationStart |
Dealbot begins polling filecoinpin.contact for <rootCid + sampled CIDs, SP> provider records. |
piece-validation.service.ts |
sampledIpniVerificationComplete |
IPNI verification finishes (all CIDs resolved to the SP, IPNI_VERIFICATION_TIMEOUT_MS reached, or error). |
piece-validation.service.ts (drives ipni_verify_ms) |
sampledBlockFetchStart |
Dealbot starts fetching the sampled CIDs via GET {spBaseUrl}/ipfs/{cid}?format=raw. |
piece-validation.service.ts |
sampledBlockFetchComplete |
All sampled block fetches finished; each response was hash-verified against its declared CID (any non-2xx, hash mismatch, unsupported codec, a timeout, or transport error counts as a failed block). | piece-validation.service.ts |
sampledRetrievalCheckComplete |
Sampled retrieval check terminates — successful piece fetch (plus optional CAR/IPNI/block-fetch validations) or any failure / abort / timeout. Drives the sampled_retrieval_checks ClickHouse row and sampledRetrievalCheckMs. |
sampled-retrieval.service.ts (logs sampledRetrieval_completed) |
Metrics
- Many of the metrics below are derived from the events above.
- They are exported via Prometheus.
- All Prometheus/OpenTelemetry metrics have label/attributes for:
network=calibration|mainnetcheckType=dataStorage|retrieval|sampledRetrieval|dataRetention|dataSetCreation|dataSetLifecycleCheck|pullCheck— attribute metrics to a particular check/jobproviderId— attribute metrics to a particular SPproviderName— human-readable name of the SP (defaults to"unknown"when not available)providerStatus=approved|unapproved— attribute metrics to only approved SPs for example
Time Related Metrics
- All time-related metrics are emitted as histograms.
- Histogram buckets are defined in
metrics-prometheus.module.ts.
| Metric | Relevant Checks | Timer Starts | Timer Ends | Additional Info | Source of truth |
|---|---|---|---|---|---|
ingestMs |
Data Storage | uploadToSpStart |
uploadToSpEnd |
deal.service.ts |
|
ingestThroughputBps |
Data Storage | n/a | n/a | (uploadedPieceBytes / ingestMs) * 1000 |
deal.service.ts |
pieceAddedOnChainMs |
Data Storage | uploadToSpEnd |
pieceAdded |
deal.service.ts |
|
pieceConfirmedOnChainMs |
Data Storage | pieceAdded |
pieceConfirmed |
deal.service.ts |
|
spIndexLocallyMs |
Data Storage | uploadToSpEnd |
spIndexingComplete |
ipni.strategy.ts |
|
spAnnounceAdvertisementMs |
Data Storage | uploadToSpEnd |
spAnnouncedAdvertisementToIpni |
ipni.strategy.ts |
|
ipniVerifyMs |
Data Storage, Retrieval | ipniVerificationStart |
ipniVerificationComplete |
One histogram observation per indexer call. Extra labels: value (success, or failure.* — same vocabulary as Discoverability Status for filecoinpin.contact; cid.contact Verification Status for cid.contact) and indexer (filecoinpin.contact or cid.contact). Filter {value="success", indexer="filecoinpin.contact"} for advertisement-to-filecoinpin.contact latency. The cid.contact observation is only emitted after filecoinpin.contact succeeds. Timer-start rules: ipniVerificationStart. |
ipni.strategy.ts, retrieval.service.ts |
ipfsRetrievalFirstByteMs |
Data Storage, Retrieval | ipfsRetrievalStart |
ipfsRetrievalFirstByteReceived |
retrieval.service.ts |
|
ipfsRetrievalBlockFirstByteMs |
Data Storage, Retrieval | Each IPFS block request | First byte received for each block | Emitted for block-fetch retrievals (one observation per block) | retrieval.service.ts |
ipfsRetrievalLastByteMs |
Data Storage, Retrieval | ipfsRetrievalStart |
ipfsRetrievalLastByteReceived |
retrieval.service.ts |
|
ipfsRetrievalThroughputBps |
Data Storage, Retrieval | n/a | n/a | (downloadedCarBytes / ipfsRetrievalLastByteMs) * 1000 |
retrieval.service.ts |
dataStorageCheckMs |
Data Storage | uploadToSpStart |
ipfsRetrievalIntegrityChecked |
Duration of a Data Storage check | |
retrievalCheckMs |
Retrieval | Retrieval check start | ipfsRetrievalIntegrityChecked |
Duration of a Retrieval check | |
dataSetCreationMs |
Data-Set Creation | Data-set creation uploadToSpStart | Data-set creation pieceConfirmed | Duration of one data-set creation with confirmed piece (all using createDataSetWithPiece) |
deal.service.ts |
dataSetLifecycleCheckMs |
Data-Set Lifecycle Check | Both variants start (parallel) | Both variants settle | Wall-clock duration of the full parallel execution (both empty and with-pieces variants). Emitted on success and failure.timedout only. See data-set-lifecycle-check.md. |
data-set-lifecycle.service.ts |
pullRequestAcknowledgementLatencyMs |
Pull | pullRequestSubmittedToSp |
pullRequestAcknowledgedBySp |
Time from pullPieces submission to SP request acknowledgement. |
pull-check.service.ts |
pullRequestStartedMs |
Pull | pullRequestSubmittedToSp |
pullRequestStartedBySp |
Time from pullPieces submission to the SP reading the first byte of /api/piece/{pieceCid}. Skipped (no observation) when the SP never fetches from dealbot. |
pull-check.service.ts, pull-piece.controller.ts |
pullRequestCompletionLatencyMs |
Pull | pullRequestSubmittedToSp |
pullRequestIsTerminal |
Time from pullPieces submission to terminal SP pull status. Emitted once for the check, either on success or failure. |
pull-check.service.ts |
pullRequestThroughputBps |
Pull | n/a | n/a | (pieceSizeBytes / pullRequestCompletionLatencyMs) * 1000. Upper-bound on actual transfer rate because pullRequestCompletionLatencyMs includes SP-side scheduling and dealbot's polling cadence. |
pull-check.service.ts |
sampledPieceRetrievalFirstByteMs |
Sampled Retrieval | sampledPieceFetchStart |
sampledPieceFetchFirstByteReceived |
Time to first byte for sampled piece retrievals | sampled-retrieval.service.ts |
sampledPieceRetrievalLastByteMs |
Sampled Retrieval | sampledPieceFetchStart |
sampledPieceFetchLastByteReceived |
Total time to retrieve a sampled piece | sampled-retrieval.service.ts |
sampledPieceRetrievalThroughputBps |
Sampled Retrieval | n/a | n/a | (bytesRetrieved / sampledPieceRetrievalLastByteMs) * 1000 |
sampled-retrieval.service.ts |
sampledRetrievalCheckMs |
Sampled Retrieval | sampledPieceSelected |
sampledRetrievalCheckComplete |
End-to-end sampled retrieval check duration (excludes piece selection; includes CAR/IPNI/block-fetch validation when applicable). Emitted even on abort. | sampled-retrieval.service.ts |
Count Related Metrics
- These count metrics are used to track the occurrence of a particular status for a check.
- All Prometheus/OpenTelemetry status count metrics have additional label/attributes for:
value- attribute counts to different outcomes.- Note: this is an ambiguous and confusing label/tag. It isn't to be confused with the metric counter's value. In retrospect, this should have been called something like
statusinstead to avoid confusion with first classvalueterm for metrics. The presence of this confusingvaluelabel causes Clickhouse metric queries to havelabel('value')andvalue_avg.
- Note: this is an ambiguous and confusing label/tag. It isn't to be confused with the metric counter's value. In retrospect, this should have been called something like
- Most of these count metrics emit a counter value of 1. Exceptions are noted below in the
Counter Valuecolumn.
| Metric | Relevant Checks | When Emitted In Successful Case | value Label Values |
Additional Info | Counter Value | Source of truth |
|---|---|---|---|---|---|---|
dataStorageUploadStatus |
Data Storage | uploadToSpEnd |
success, failure.timedout, failure.other from Data Storage Sub-status meanings. |
1 | ||
dataStorageOnchainStatus |
Data Storage | pieceConfirmed |
success, failure.timedout, failure.other from Data Storage Sub-status meanings. |
1 | deal.service.ts |
|
dataStorageStatus |
Data Storage | When the Data Storage check completes (all four sub-statuses done) | success, failure.timedout, failure.other from Deal Status Progression. |
1 | deal.service.ts |
|
discoverabilityStatus |
Data Storage, Retrieval | ipniVerificationComplete |
success, failure.timedout, failure.other, skipped from Data Storage Sub-status meanings. |
Driven exclusively by the filecoinpin.contact result; cid.contact outcome is tracked separately in cidContactVerification. |
1 | |
cidContactVerification |
Data Storage | ipniVerificationComplete for the cid.contact call (or when cid.contact is bypassed) |
success, failure.timedout, failure.other, skipped from cid.contact Verification Status. |
Emitted once per check. skipped when filecoinpin.contact did not reach success (including discoverability skipped or failure.*); otherwise reflects the cid.contact poll outcome. Does not affect overall Data Storage check status. |
1 | ipni.strategy.ts |
ipfsRetrievalHttpResponseCode |
Data Storage, Retrieval | ipfsRetrievalLastByteReceived |
200, 500, 2xxSuccess, 4xxClientError, 5xxServerError, otherHttpStatusCodes, failure |
1 | retrieval.service.ts |
|
retrievalStatus |
Data Storage, Retrieval | ipfsRetrievalIntegrityChecked |
success, failure.timedout, failure.other from Data Storage Sub-status meanings. |
On the Retrieval path, the pre-flight branches on the on-chain PDPVerifier.pieceLive(dataSetId, pieceId) result. When pieceLive=false (dataset terminated, piece never created, or piece hard-removed), skipped.piece_missing is emitted and the deal is marked cleaned_up=true; no SP probe runs. When pieceLive=true and the SP returns 404 on /pdp/piece/:pieceCid/status, failure.other is emitted and a failed retrieval row is recorded (deal stays in the candidate pool for re-probing). |
1 | |
dataSetCreationStatus |
Data-Set Creation | Not tied to an event above but rather to data-set creation start (pending) and completion (success/failure.*) |
pending, success, failure.timedout, failure.other |
1 | deal.service.ts |
|
dataSetLifecycleCheckStatus |
Data-Set Lifecycle Check | When a data_set_lifecycle_check invocation finishes (after both variants settle) |
success, failure.timedout, failure.other |
success requires both variants to pass: createDataSet → terminateService (empty) and uploadPieceStreaming → findPiece → createDataSetAndAddPieces → terminateService (with-pieces). failure.* means at least one variant failed; per-variant outcome is in the log lines. See data-set-lifecycle-check.md. |
1 | data-set-lifecycle.service.ts |
dataSetChallengeStatus |
Data Retention | Emitted on each Data Retention Check poll when a provider's confirmed proving-period totals advance (strictly positive deltas since the last poll). | success (challenges in newly confirmed successful proving periods), failure (challenges in newly confirmed faulted periods) |
Counter increment = period delta × 5 (CHALLENGES_PER_PROVING_PERIOD). Period delta is the increase in subgraph-confirmed proving periods since the previous poll for that provider (not "challenges per poll" in the abstract). See data-retention.md §3. |
data-retention.service.ts |
|
pullRequestProviderStatus |
Pull | When the SP reports a terminal pull status via waitForPullPieces. Recorded exactly once per check (intermediate poll statuses are not counted). |
Raw SP-reported pull status, for example complete, failed, not_found. Use this to separate SP-side pull failures from dealbot-side validation failures. |
1 | pull-check.service.ts |
|
pullCheckStatus |
Pull | When the Pull Check terminates (success after direct piece validation, or any failure). Recorded exactly once per check. | success, failure.timedout, failure.other from Pull Check Status. |
1 | pull-check.service.ts |
|
sampledPieceRetrievalStatus |
Sampled Retrieval | sampledRetrievalCheckComplete |
success, skipped, failure.http, failure.commp, failure.timedout, failure.other from Sampled Retrieval Sub-status meanings. |
sampled-retrieval.service.ts |
||
sampledPieceHttpResponseCode |
Sampled Retrieval | sampledPieceFetchLastByteReceived |
Same as ipfsRetrievalHttpResponseCode. |
sampled-retrieval.service.ts |
||
sampledCarParseStatus |
Sampled Retrieval | sampledCarParsed, or when CAR parsing didn't run (records skipped) |
success, skipped, failure.not_parseable from Sampled Retrieval Sub-status meanings. |
sampled-retrieval.service.ts |
||
sampledIpniStatus |
Sampled Retrieval | sampledIpniVerificationComplete, or when piece fetch failed (records skipped) |
success, skipped, failure.timedout, failure.other from Sampled Retrieval Sub-status meanings. |
sampled-retrieval.service.ts |
||
sampledBlockFetchStatus |
Sampled Retrieval | sampledBlockFetchComplete, or when piece fetch failed (records skipped) |
success, skipped, failure.other from Sampled Retrieval Sub-status meanings. |
sampled-retrieval.service.ts |
Other Gauges
Unlike the count metrics above, gauges below expose a level (for example overdue proving periods right now). They do not use the value outcome label. The scraped sample magnitude is the "Gauge Value" measurement.
| Metric | Relevant Checks | When Emitted | Gauge Value | Source of truth |
|---|---|---|---|---|
pdp_provider_estimated_overdue_periods |
Data Retention | Emitted on every Data Retention Check poll for every successfully processed provider. | Number of overdue proving periods (non-negative integer) | data-retention.service.ts |
ClickHouse Tables
When CLICKHOUSE_URL is configured, dealbot writes one row per check result to ClickHouse for long-term storage and analysis. All tables are partitioned by month with a 1-year TTL.
Source of truth: the DDL and column-level comments in
clickhouse.schema.tsare authoritative. The summary below is for orientation only.
data_storage_checks— one row written each time a deal is saved (on every status transition). Populated bydeal.service.ts.retrieval_checks— one row per retrieval attempt. Populated byretrieval.service.ts.sampled_retrieval_checks— one row per Sampled Retrieval check attempt; emitted even on abort or unexpected error. Populated bysampled-retrieval.service.ts. See Sampled Retrieval § Result Recording for column-level meanings.data_retention_challenges— one row per provider per poll cycle. Populated bydata-retention.service.ts.
All tables share the primary key (probe_location, sp_address, timestamp):
probe_location- identifies which dealbot instance produced the row, allowing multiple deployments to be distinguished in queries (set viaDEALBOT_PROBE_LOCATION)sp_address- the Ethereum/FEVM address of the storage provider under testtimestamp- when the row was written (milliseconds UTC)