Skip to content
downpipes docs

Reading the Runs activity view and per-run detail

Runs is the activity view: what ran, when, and with what result, across the whole fleet and per downpipe. A silently stopped backup is loud here, a failed run shows its coarse reason inline, and a run still in progress is a real state rather than a missing row. This page is the entry point to day-2 operations for the self-hoster, and it explains how to read the table, the per-run detail, and the verify-at-seal verdict that tells you whether a backup is internally sound.

The screen does not poll. The run history is a deliberate read you open, not a live dashboard, so it loads on arrival and refreshes in place when you ask it to, which keeps a filtered view from being wiped underneath you. Everything it shows is counts, sizes, and outcomes; nothing on this screen transmits, logs, or stores a secret or a key.

The history table

The table flattens every downpipe’s run ring into one fleet-wide, newest-first list. You can filter it by downpipe name or run id, narrow it with status facets, and re-sort any column; the filter and sort state is reflected into the URL so a filtered view survives a refresh and can be shared (buildTable and reflectStateInUrl, console/src/screens/runs/table.ts).

Column What it shows
Downpipe The human name, with the slug id and run index demoted to a mono hint.
Status The run’s resolved state, rendered as a hue, a shape, and a label rather than a raw string. A failed run shows its coarse reason beside the status.
Started When the run began, relative with the absolute time on hover.
Duration The run’s wall-clock duration, or a dash when not reported.
Records The record count, or a dash when not reported.
Archive written The new archive bytes written this run, never the plaintext read size. A dash when not reported.
Run id The stable run id used to prefill a drill or a restore.
The Activity table with a filter box, ok, failed and in-flight status facets, a runs count and a Comfortable or Compact density toggle, over columns for Downpipe, Status, Started, Duration, Records, Archive written and Run id; each row shows a status with a verified badge such as verified, all 2 or verified at seal.

A summary band above the table reads throughput, freshness, and outcome at a glance: the total runs and how many are ok, the failed and in-flight counts, the age of the latest run, and the records and archive bytes written across the whole fleet. The band is always computed from the full run set, not the filtered or faceted view, so it stays a true read of fleet posture even while you are narrowed onto a single downpipe or status. Each figure is honest about gaps: a count that was not reported reads as a dash, never as a stale zero, and a sum states how many runs actually contributed so a partial wire shape does not read as a complete figure.

The Runs summary band of four cards reading Runs 34 with 34 ok and two downpipes with activity, Failed 0 with none in flight, Latest run ok with its age beside it, and Records backed up 47 with 3.4 KB archive written over 34 runs.

Run status, and the coarse error vocabulary

A run row carries one of four resolved states. The console renders each with a distinct hue and shape as well as a word, so the state is legible without relying on colour alone (runStatusTone, console/src/components/status.ts).

Status Meaning
ok The run completed and sealed. Read the badges beside it before treating that as a full copy; the next section says why.
failed The run errored. The row carries a short, coarse reason.
in-flight The run is still going. This is a real state, not a missing or stale row.
abandoned The engine gave up on a run that never completed, and retired the in-flight row rather than leaving it in flight forever. It is neither a success nor still running, so it carries its own warn tone.

An abandoned run is not counted and not filterable

The status facets, and the ?status= deep link the Overview uses, cover ok, failed and in-flight only (STATUS_FACETS, console/src/screens/runs/types.ts). An abandoned run is rejected by the filter parser, so selecting every facet still does not show you one, and /runs?status=failed never reaches it.

The summary band counts the three the facets know about, so an abandoned run is included in the total and in none of the three tallies (buildSummary, console/src/screens/runs/summary.ts). A band reading twenty runs, eighteen ok, none failed and none in flight is not a clean fleet: it is two runs the engine gave up on, unaccounted for. Reconcile the total against the sum of the three before you read the band as an all-clear, and find an abandoned run by scanning the unfiltered table for the warn tone.

The word in the Status cell is not the whole verdict, and on an ok run it is the badges beside it that carry the recoverability signal. Two can appear. A verify-at-seal verdict rides there as a quiet trust badge, or as a danger “seal suspect” badge when the read-back did not confirm. And a run short of its live source, by any of the incomplete, skipped or vanished record counts, carries a warn badge stating the total, because such a run still has a status of ok (shortfallLabel and the status cell in console/src/screens/runs/table.ts).

An `ok` run can be short of a full copy

ok means the run completed and sealed. It does not mean every record was captured. A run that skipped records, or found records that vanished mid-run, seals cleanly and reports ok, and the shortfall is stated on the warn badge, on a “Not fully captured” row in the detail drawer, and on the summary band’s latest-run tile as “ok, incomplete”.

Treat the badge as part of the status rather than decoration. A restore from a run that reads ok and carries that badge gives you back the records the run actually captured, and the ones it did not are simply missing, which is the failure the badge exists to prevent.

When a run fails, its error is a short, enumerated reason, never a stack trace or an internal detail that could carry a secret. It is the run’s recorded result, so the console surfaces it as an expected inline outcome rather than an error toast. You will see the same coarse vocabulary in a support bundle and behind the matching notification.

Here is the whole vocabulary. It is generated from the engine function that produces it (coarseRunError, engine/src/seal/slice.ts) rather than transcribed, so a reason the engine adds or renames cannot quietly go missing from this table.

There are 21 of them, and they are closed: nothing but a member of this list can reach the row. Of those, 16 carry a remedy the engine itself states. The remaining 5 name the system at fault and stop there, and are marked below rather than given an invented fix.

Reason on the failed runWhat it means, and what to do
destination access errorThe engine names the system at fault and states no action, so no remedy is derivable from it. Treat the class as an attribution rather than an instruction: collect a support pack, which carries the correlation digest that joins this row to the engine log line.
destination rejected the write (<code>)Read the S3 code in the brackets: it is the store's own reason, passed through rather than collapsed into a class. It is the actionable half of a destination failure, and unlike a throttle it will not clear on its own.
runlog write contendedRetry. This is compare-and-set contention on the run log, and it clears. A destination fault that will not clear is deliberately kept out of this class, so a run that keeps landing here is contending rather than misconfigured.
shard enumeration incomplete (refused to sign a truncated archive)Nothing was signed, which is the point: the engine found fewer shards than the checkpoint declared and refused to seal a short archive as a complete one. The found and expected counts are not on the run row; collect a support pack, which carries them.
checkpoint unwrap failed (resume state unreadable; signer may have rotated)The in-flight run cannot resume and will not recover by retrying that run. If the signing key was rotated while the run was in flight, that is the cause: the checkpoint was wrapped under the previous signer. Start a fresh run.
seal DO handoff refused (<class>)
<class> is one of invalid-payload, worker-spawn-refused, do-error, unreachable.
The fault is inside the engine's own control plane, not at your destination. The sub-class in the brackets says which handoff refused and is re-gated against a closed list, so it is never a passed-through message. Retry the run; if the same sub-class repeats, raise it with the cause digest.
seal DO handoff refusedAs above, with no sub-class recovered. Read it as the engine's control plane, not your destination: this class exists because these refusals used to arrive as "status 500" and be classified as a destination fault, which blamed the customer's bucket for an engine-side problem.
internal invariant violatedChange nothing about your configuration. The engine states this is its own bug rather than a misconfiguration, so no source, destination or schedule change will clear it. Collect a support pack and raise it.
shard id overflow (refused to seal a misordered archive)The engine names the system at fault and states no action, so no remedy is derivable from it. Treat the class as an attribution rather than an instruction: collect a support pack, which carries the correlation digest that joins this row to the engine log line.
resume state divergent (mid-record resume refused)The engine names the system at fault and states no action, so no remedy is derivable from it. Treat the class as an attribution rather than an instruction: collect a support pack, which carries the correlation digest that joins this row to the engine log line.
source cannot range-read (chunked capture unavailable)The record is too large to take in one piece and its source offers no ranged read, so it can never be chained across windows. The size ceiling is not the thing to change: capture that record out of band. The engine names this class ahead of the ceiling class for exactly that reason.
record over the in-band size ceiling (recover it out of band)Recover that record out of band, as the class says. This is a documented ceiling with a documented remedy rather than a source fault, and the rest of the run is unaffected.
unsupported source typeThis is a configuration fault, not a source fault: the downpipe names a source type this engine build cannot back up. Do not go looking at the source, which is healthy. Remove or replace the source, or move to an engine build that supports it.
source resource missingThe binding is present but the thing it points at (the KV namespace, R2 bucket or D1 database) is gone. Re-create the resource or repoint the source. This class exists so you are told the resource is missing rather than handed a destination error for a source-side fault.
source binding errorThe binding the downpipe names is not present in this deployment. The usual cause is a deploy that replaced the worker's bindings; deploy with the command that reads the live bindings and ships a superset, and check the Sources screen for a binding flagged missing.
source too large to back up in one slice (not yet resumable)There is no in-product remedy today, and the class says so: this source cannot be split across slices yet. Narrow the source so one slice can finish it.
source read errorThe engine names the system at fault and states no action, so no remedy is derivable from it. Treat the class as an attribution rather than an instruction: collect a support pack, which carries the correlation digest that joins this row to the engine log line.
cf-config source needs an accountId (re-save the source with an account id)Re-save the source with an account id, as the class says. This is a source saved before an account id was required; validation now refuses one without it up front, so re-saving is the whole fix.
source listing exceeded the page cap (too many items to enumerate in one run; narrow the source selector)Narrow the source selector, as the class says. The surface holds more items than one run can enumerate. This is a discovery failure with a structural cause, not a transient one, so retrying unchanged will land here again.
engine not fully configuredRequired configuration is absent on the engine. Complete the deployment configuration and re-run; no retry clears it while a required value is unset.
run failedThe engine names the system at fault and states no action, so no remedy is derivable from it. Treat the class as an attribution rather than an instruction: collect a support pack, which carries the correlation digest that joins this row to the engine log line.

Two things about the table are worth knowing before you read a row.

The order is the classification order, and it decides ties. The engine takes the first branch that matches, so a fault whose message carries two recognisable shapes is reported as the higher one. That is why a source read failure mentioning an HTTP status is a source read error rather than a destination one, and why a refused handoff inside the engine is not reported as your destination being unreachable.

The reason on the row is all you get, by design. The raw message can name a record or an object key, which the archive encrypts end to end, so it never rides on the row or in a log line. What travels instead is a short one-way digest of the raw message, stamped on the failed row and on the matching engine log line, so support can join the two without either carrying the text. Quote that digest when you raise a failed run.

The per-run detail

Opening a run shows its full detail in a drawer. The header carries the downpipe name, the slug id, and the status badge; the body breaks the run down into its run facts, its throughput, and, when present, its verify-at-seal verdict.

The per-run detail drawer for one run, with an ok badge and the downpipe slug, a Run section listing run id, start time and duration, a Throughput section with Records, Plaintext read, Archive written and Segments written, a Verify at seal result reading verified at seal with full byte coverage, and Drill this run and Build a restore plan actions, the drill noted as needing a capability the Viewer role does not hold.

The Run section also names the run’s predecessor: the id of the downpipe’s previous successful run, so you can walk the chain backwards from the console rather than only from the sealed archive itself. A downpipe’s first run ever reads “None”. A predecessor that has aged out of the retained run-history window still shows its id, with a note that this is a bounded window rather than a broken chain. A run recorded before this field existed reads “Not recorded”.

The throughput section is where two different byte figures sit side by side, and the difference matters.

Figure What it measures
Records How many records the run backed up.
Plaintext read The total plaintext size the run read from the source before encryption.
Archive written The new bytes actually written to the destination archive this run.
Segments written The object count written, meaning segments plus the manifest, not skipped objects.

Plaintext read and archive written are not the same quantity and the larger one is usually the plaintext read. The archive is compressed and encrypted, and an incremental run writes only what changed, so archive written can be much smaller than the plaintext the run scanned. The table’s “Archive written” column shows the written figure only and never borrows the plaintext read in its place, so the column and the summary band agree. The segment count is the unit the archive is split into for streaming, so a large run writes many segments and a small one writes few.

The verify-at-seal verdict

Right after a run seals the archive and the run log to the destination, and before the run is reported a clean success, the engine reads the just-written archive back from the destination and verifies it. This catches a corrupt or partial backup at seal time rather than at the next periodic drill, which could be up to a week later. The result is recorded on the run row as a verdict and rendered calmly in the drawer (verifyAtSeal, engine/src/seal/verify-at-seal.ts; the drawer in console/src/screens/runs/detail.ts).

The verdict is one of two states.

Verdict What it means
Verified The bytes that landed are internally consistent and correctly signed. The verdict also names the depth that ran, so you can tell a keyless chain attestation from a run that additionally decrypt-checked a sample of records.
Suspect The read-back verification did not pass. The run still completed, the archive was not deleted, and the verdict carries a short, coarse, secret-free reason.

A verified verdict at the keyless tier confirms three things about the archive: the hybrid signature over the stored root holds, every signed shard is present and hashes to that root, and the anti-rollback freshness check passes. When the run is not too large, it goes further and decrypt-checks a sample of records as well, and the verdict says how many. That deeper check no longer depends on your posture: the engine reaches the run’s key either by decapsulating its recipient wrap with the operational key, or from the per-run key it still holds while finalising that run, so a break-glass-only downpipe gets it too. The verdict records which route was used, because they prove different things: opening the wrap also proves that wrap opens, while opening from the run’s own key proves the bytes decrypt and says nothing about any wrap. When neither route is available, or the run is too large or has too many shards, verification is honestly the keyless tier only and the verdict names the reason rather than claiming a deeper check it did not run.

A verify-at-seal verdict is an engine-side check of the archive’s completeness and anti-rollback, performed by reading the bytes back from your destination. It is not a check performed by Cloudflare, and it is tamper-evident rather than a guarantee that corruption is impossible. A suspect verdict is a real recoverability signal: the run was not failed and nothing was deleted, but you should investigate the archive before relying on that run, and the quickest confirmation is to run a drill.

Drill and dry-run restore from a run

A run’s drawer is also where you exercise recovery against that specific backup, and the two actions are gated differently on purpose.

The Drill action drills the specific run whose drawer is open, not necessarily the latest one: you can open any run in the history table and drill that one. With the operational key present it verifies the chain end to end and restores a sample record while writing nothing back; the result carries isLatest, so a drill on an older run tells you plainly that it is not the latest run. It is the in-account proof that the backup is recoverable, and it is gated on the drill.run capability, which Operator, Approver, Restore-operator and Owner hold and Viewer and Access-admin do not. A role without it sees the Drill button disabled with the reason inline, and its dry-run restore stays available. A passing drill records a dated recoverability entry best-effort, so a trail exists. On a break-glass-only posture the drill returns an expected outcome that says recovery must be rehearsed offline, which the console presents as the normal result of that posture rather than a fault.

The dry-run restore builds a restore plan from the run and is available from the Viewer role up, one rung lower than the drill, so a read-only operator can still see what a recovery would write without being able to launch a drill. Building a plan does not write anything; it is the safe first step of the restore flow.

Neither the drill nor the dry-run restore is a point-in-time, choose-any-timestamp restore screen. The console works from a specific run you select rather than an arbitrary clock time. The restore screen’s browse-by-date modal does call the engine’s point-in-time resolver, but only to read the authoritative floor of the recent-run history ring; picking a day still lands you on a real run. The recovery point you act on is a real run, and the freshest one is the newest good run shown in the summary band.

Tying runs back to alerts

What you read here is the same outcome that drives the notification stream. A failed run is what backup-failure alerts on, a downpipe whose runs stop happening is what backup-stale alerts on, and a scheduled restore test maps to restore-test-pass or restore-test-fail. A suspect verify-at-seal verdict is itself routed as a restore-test-fail at critical, so a backup that looks fine in the table but failed its read-back will page you. Reading a run’s detail is how you confirm what an alert is telling you.

Where this fits

For the full, honest catalogue of which events fire and at what severity, read what downpipes alerts you about, and to route those events to a channel read the notifications guide. To see how a backup flows from source to destination across the fleet, read the topology view. The verify-at-seal mechanism in the context of a backup run is covered in verify at seal, and the drill as a recoverability proof in prove recoverability. For what a restore can and cannot write back, see what restore can and cannot write back.

Last updated .