Overview#
The chain records which rows each validator promised to store. Tensile checks, from outside and as an ordinary client, whether it serves them, and publishes every record a figure rests on.
The chain records who registered a Fibre host (x/valaddr), every MsgPayForFibre with the validator signatures on it, and the x/fibre parameters. It records nothing about serving afterwards: there is no serving proof and no slashing for not serving.
Blob readingsonce per blob · one location
Each settled blob is read 10 minutes before its retention window ends. Every validator that endorsed it is asked for its own rows, and every row is verified against the blob commitment.
Endpoint checksevery five minutes · two locations
Every registered Fibre endpoint is checked for DNS, TCP, a TLS 1.3 handshake and a certificate signed by the validator’s consensus key. No blob data is asked for.
Chain recordas each block arrives
Settlements, endorsements and parameter changes, read from each block as it arrives, and escrow balances from chain state. Nothing here is measured.
A reading is an observation, not an availability proof, and produces no slashing evidence. Validators are ranked only by stored readings anyone can download from the API.
Blobs and settlements#
A blob is the data; a settlement is one payment for it on chain. Most blobs settle once.
- Blob ID#
- The version byte and commitment the Fibre client returns, in base64. Search also takes it in hex.
- Transaction and promise hash#
- Each settlement is a
MsgPayForFibretransaction carrying one payment promise, which validators endorse. A blob’s page is addressed by its promise hash; search takes the transaction hash too. - Failed transactions#
- A Fibre transaction that failed in a block (a blob payment, an escrow deposit, withdrawal request or timeout, or an endpoint registration) has its own page by its hash, the same page a successful one has. A failed endpoint registration signed by a validator’s operator address is also listed in its Endpoint history, and a failed blob payment in the Blobs list, with TX status Failed and a dash for its size, fee and endorsement. It moved nothing and is in no figure. The reason is the chain’s error code in plain words, given only where Tensile has checked that code against the chain version the block ran under. The error is shown as the node returned it (a panic’s stack trace and anything past 8 KiB are left out).
- Settled more than once#
- A blob paid for twice is one blob and two settlements, each with its own promise, its own endorsements and its own reading. Blob counts count it once; settlement counts count both.
- Blob size#
- The size the blob paid for: Celestia’s upload size, with header and padding, without parity.
- Namespace#
- Shown as text when its bytes are text, as a name and its trailing tag in hex when a client numbers its namespaces (
tensile·04), and in hex otherwise. - Retention window#
- A validator that endorsed a settled promise owes its assigned rows to anyone who asks until the window ends:
must_serve_until = creation_timestamp + max(payment_promise_timeout, shard_retention)with the parameters in force when the blob settled
Results and metrics#
Each blob read gets one result, Available or Unavailable. Each validator that endorsed it is judged served or not served on it, and its service rate counts those results.
Reachability, endorsements and served are three separate facts: an endpoint that completes a handshake, a signature on chain, and rows that came back. None is inferred from another.
Shared rules
- A reading counts once its blob’s retention window has closed. Until then the blob is in retention window, shown as Awaiting check on a validator’s page; the reading shows on the blob’s page as soon as it is in.
- Every rate is printed beside its numerator and denominator.
- A service rate, reachability or throughput from fewer than 20 observations is shown without a gauge and sorts after the others.
Available#
Enough of the blob’s rows came back at its reading to reconstruct it.
The reading is an observation inside the retention window, made 10 minutes before the window ends. Its result is the Fibre client’s own, one of three:
- Available
- At least 4,096 distinct rows came back and verified.
- Unavailable no shards retrieved
- No rows came back.
- Unavailable not enough shards to reconstruct blob
- Some rows came back, fewer than 4,096.
available / (available + unavailable)over the blobs read whose retention window has ended- Not counted
- A blob still in its retention window, and a blob with no reading.
Served#
The validator’s own endorsed rows came back and verified against the blob commitment, at the reading or when it was asked again.
Rows of another settled promise over the same blob count too (shadowed; see Verifying the validator’s own rows). Only rows that came back count; nothing is extrapolated.
Not served#
None of the validator’s answers served its rows. Its last answer gives the reason, in one of the words below.
Counted neither way
A validator whose rows did not come back is not counted as not served when any of its answers at the reading was:
- a request that failed on Tensile’s side: before it reached the validator, on Tensile’s own network, resolver or clock, or cut off by a restart;
- a request, or a re-ask it was owed, that was not made;
- rows of the blob that are not its own and that no settled promise explains;
- an answer that cannot be judged: an unrecognised outcome, or a chain version whose row assignment the pinned code cannot compute.
Nor is anyone not served on a reading in which no request reached any server. In each case the validator counts neither way on that blob.
Two failures look the same whichever side caused them, so they count as the validator’s only when Tensile’s own side is shown working in the same minutes:
- a lookup of the validator’s host name that fails other than with “no such host”: Tensile’s resolver must have answered other validators’ names and a fresh test name, and its connections must have reached other servers;
- a connection turned away as unreachable: Tensile’s connections over the same IP version must have reached other servers.
Otherwise the failure is Tensile’s gap.
- Provisional
- A not-served reading younger than 30 minutes: counted, and still open to withdrawal until then.
A not-served reading can be challenged; see Disputes and corrections.
Words on a reading
Each answer is recorded with one of these words. A failure among them is not served only as the validator’s last answer, and never in the cases above.
Service rate#
The share of a validator’s endorsed shards that it served, over the period.
served / (served + not served)per endorsed shard- Counts
- Every endorsed shard whose retention window has closed.
- Not counted
- Shards still in their window (Awaiting check), shards on which it counts neither way, and blobs it did not endorse, which it does not owe.
Beside it, the network median: the median of the validators’ own rates, over those with at least 20 decided shards.
Reachability#
The share of endpoint checks in the period that completed a TLS handshake with the validator’s registered Fibre endpoint.
completed handshakes / checksa check every five minutes- Counts
- Every check from the main location, failed ones included.
- Not counted
- Checks from the second location, and a check that failed before it left Tensile.
A check asks for no blob data, so reachability says nothing about serving rows, and it is not signing uptime.
The status shown now, reachable or unreachable, is the newest check, with two allowances. One failed check after a success still shows reachable; two in a row show unreachable. An endpoint the main location cannot reach shows reachable when the second location completed a handshake with it in the last 15 minutes. Neither allowance changes the percentage.
Throughput#
The median download speed of the validator’s served shards of 2 MiB or more.
median(shard bytes / download time)shown from three shards upTimed over the download alone: the dial, handshake and identity check cost the same for a small shard as for a large one.
Endorsements#
How often the validator’s verified signature is on the settlement, of the settled blobs that assigned it rows while it had a Fibre host registered.
endorsed settlements / settlements that assigned it rowsA validator endorses a payment promise by signing it after storing its shard. A settlement needs signatures from ⅔ of the voting power, and the first validators to respond fill it. Endorsing is not a duty: a missing endorsement is unproven, not a fault. An endorsement counts when the blob settles, not when it is read, and only endorsed shards are owed and read.
On a blob’s page, Endorsed is the voting power whose signature on that settlement verified.
Shard data#
The row data of the shards a validator stored and endorsed, read from the chain.
Its rows of every settled blob it endorsed, kept for the retention window; each row is blob_size / original_rows bytes with padding. The row proofs stored beside them, about a fifth more on a 16 MiB blob, are not counted.
- Rows per blob
- Its share of every blob, by stake.
- Held now
- Shard data of the blobs it endorsed that are still in their retention window.
How checks work#
Blob readings ask validators for rows; endpoint checks only open a connection. The two run independently.
When a blob is read#
Once, 10 minutes before its retention window ends.
Every validator that endorsed the blob is asked for its own rows the way celestia-app’s Fibre client asks for a shard: in the client’s order, 15 s per request with one re-dial. Every row that comes back is verified against the blob commitment. Validators that did not endorse owe nothing and are not asked. Every request stays on record.
A host name with several addresses is connected to as the client connects: the next address is tried 250 ms after the last, or at once when it fails, and the first to connect is used. One dead address does not fail the request.
Timing limits
A blob whose window, counted from its settlement, is 10 minutes or shorter is read half way through it. No request starts later than a minute before must_serve_until.
Asked again#
A validator whose answer did not serve is asked again, up to two more times, 90 s after its last answer, while more than a minute remains before must_serve_until.
Each answer is a row of its own; the last carries the result and its reason. An attempt the validator’s own time leaves no room for is not owed. A validator is asked only about the blobs it endorsed: once per blob, and at most twice more.
One failure, several waiting attempts
A later attempt that fails before the validator’s identity is verified (no such host, a connection refused, timed out or unroutable, a failed handshake or certificate) also answers that validator’s other attempts waiting at the same host. Each gets a row of its own that names the request it repeats.
Verifying the validator’s own rows#
Every row is checked against the blob commitment, and the rows a validator returns against its own assignment.
- Exactly its assigned rows, verified
- served
- Exactly another settled promise’s rows of the same blob
- served (shadowed)
- Fewer of its own rows than it holds
- not served, as its last answer
- Rows that fail the commitment, or no such shard
- not served, as its last answer
- Genuine rows of the blob, not its own, that match no settled promise
- counted neither way
A request is addressed by commitment alone, and the store answers with the first shard in promise-hash order, so with several promises over one blob no client can ask for a particular promise’s rows.
When a verdict waits
Rows that verify but match no promise on record yet may belong to one that settles later. Their verdict is drawn once every promise that could own them is on record, payment_promise_timeout after the request.
Endpoint checks#
Every five minutes, the registered Fibre endpoint of each bonded validator gets DNS, TCP, a TLS 1.3 handshake and a check that its certificate is signed by the validator’s consensus key.
No blob data is asked for. The checks give Reachability and the validator’s TLS identity:
- verified
- The certificate is signed by this validator’s consensus key.
- expired
- The right key signed it, outside its validity window.
- not this validator’s key
- Signed by another key: no client downloads from this endpoint.
- no TLS
- The TLS handshake did not complete.
Observation locations#
Endpoints are checked from two locations; blobs are read from one, as one client’s download is.
The second location’s checks confirm the status shown now (see Reachability) and enter no percentage. A blob reading follows one network path, and asking again, twice, 90 s apart, keeps a passing failure on that path from counting.
Publishers and escrow#
A publisher is the account whose escrow pays for a blob. Every figure on the Publishers pages is read from the chain, except Tensile’s readings of the blobs.
Those carry Tensile’s eye: Available on a publisher’s page, and the Tensile column beside its blobs, as defined under Available.
- Fees#
- The module’s charge for a blob, taken from the publisher’s escrow; not the settlement transaction’s own fee. No event carries the amount, so it is recomputed with the module’s formula, at one utia per gas:The transaction fee is separate: the fee payer pays it from its bank balance, and it is in no figure.
650,000 + 45,000 × ceil(blob size / 256 KiB) gasthe same charge when a promise times out - Escrow available#
- What the account can spend from its escrow now: its balance less what is queued to withdraw. An amber dot marks an account that cannot pay for one more blob of its usual size, or one whose queued withdrawal was partly spent by settlements.
- Timeouts#
- A payment promise not settled in time, charged as a blob. A floor: only the timeouts someone submitted are on chain.
- Withdrawals#
- Read from chain state with each escrow balance, because settlements can draw on a queued withdrawal without emitting an event; that part is not paid out. A withdrawal is payable once the withdrawal delay has passed.
Protocol parameters#
Every deadline on the site is computed from these values, served at /api/v1/params.
Chain parameters are read from chain state and every EventUpdateFibreParams. Protocol constants are exposed by no RPC, so they are read from the celestia-app build the observer runs.
On chain now#
Loading…
Parameter history#
Pinned in code#
Loading…
Evidence and corrections#
Past observations are kept, and every result can be recomputed from them.
The record behind a figure#
Every figure is computed from the chain record and Tensile’s stored readings, through a block each snapshot names (record_through).
Pages keep the two apart: On chain for what the chain records; Observed by Tensile, and Tensile’s eye on a blob’s page and in the Blobs list, for its readings. The 7d, 30d and all figures are summed from per-day records sealed once a day is final, each re-checked against the raw readings in turn; they equal a count over every row.
Signed daily exports#
Each UTC day of the record is published as a tarball, with a manifest of SHA-256 digests signed with ed25519.
The list is at /api/v1/exports and the signing key at /api/v1/exports/pubkey; sentinel-verify-export checks the digests and the signature offline. Every reading row carries what produced it: the promise hash, the time, the wire outcome, the row indices returned, a digest of the returned rows, the gRPC status, the observer build and the chain’s app version.
Recomputing a figure#
sentinel-recompute re-derives every verdict and obligation figure from an export or the live record, and compares them with the API at the same moment.
cd fibre-sentinel && go run ./cmd/sentinel-recompute -data-dir <dir> \
-window 7d -as-of <RFC3339> -api https://tensile-mocha.huginn.tech/api?as_of= on /v1/network and /v1/validators answers what was published at any past moment. The tool is in the repository.
Methodology version#
2026-10-08.
Published as methodology_version in /v1/meta and in every export manifest. It changes with any rule that can move a figure.
Disputes and corrections#
To challenge a reading, open an issue with its promise hash and time, as the dispute route describes.
A correction is recorded as an amendment: the reading keeps the verdict it was given beside the new one, with when and why it changed (classification_at_probe, amended_at). A deadline corrected after a parameter change only moves earlier, so that correction can withdraw a not-served reading, never add one.