The serving process gradually verifies the bytes of every indexed object against
its full content identity. It uses the same streaming verifier as doctor,
including envelope checks, decompression, payload length, and digest validation.
Packed and loose objects are covered. Verification never repairs, deletes,
quarantines, or accepts data.
This checks indexed object bytes. It does not replace the offline doctor:
metadata consistency, missing references absent from the locator, unindexed pack
entries, and whole-pack inventory still require that broader audit. A clean pass
describes the bytes observed during the pass, not an atomic snapshot or a promise
that the storage remains healthy afterward.
Configuration
The checker is enabled by default in serve, including with an existing
configuration that omits this section:
[integrity]
enabled = true
mib_per_second = 1
idle_secs = 1
interval_secs = 86400mib_per_second: maximum scheduled decoded payload throughput, 1–1024 MiB/s. Compressed input, object headers, index lookups, and progress-file I/O are additional. One 64 KiB read can start immediately; unused budget does not accumulate into a later burst.idle_secs: quiet time after foreground work, 0–3600 seconds. Queued and executing TCP requests pause verification; idle connections do not. Online checkpoint work also pauses it. The checker checks admission between reads, never holds the publication gate, and consumes no foreground worker permit.interval_secs: wait after a repository pass completes before starting its next pass, 60–31536000 seconds. The default is one day.enabled = false: the checker does not start on the next launch. Saved reports remain available.
There is one checker thread for the whole server. It holds one repository's progress and one streaming object reader at a time, with a 64 KiB read buffer and the existing bounded decompressor. It rotates repositories after at most 16 objects, caps object starts at 20 per second, and polls shutdown while waiting. A large object finishes before that rotation, but it can pause between reads. An OS read already in progress cannot be preempted. This is cooperative load control, not a guarantee of zero disk contention. Continuously busy servers can defer verification indefinitely; inspect coverage age rather than assuming a daily pass completes.
Coverage and reports
scribe-server integrity-status --runtime-dir /srv/scribe/run
scribe-server --json integrity-status --runtime-dir /srv/scribe/run --repo REPOSITORY_IDThe command reads saved progress without opening or locking the data root. It works while the server runs and after it stops. Reports include the current pass's visited-object count, successfully verified payload bytes, failures, bounded per-object findings, the last completed pass, and the last clean pass. They are saved reports, not a live health or process-liveness probe. Repository selection takes an ID, not a name.
Exit codes:
- 0: saved completed passes have no findings, and no current findings/error are recorded.
- 1: current or last-completed coverage contains failures, or a scan error was saved.
- 2: no matching report, no completed pass yet, or reports cannot be read.
Progress is saved after each batch, pass completion, or cooperative shutdown.
Up to 32 detailed findings are retained per pass; the failure count continues
beyond that. Every detected object failure emits integrity.object_failed with
check = objects.content, repository ID, object ID, and error. The offline
doctor emits that same event. integrity.pass_completed records pass totals;
integrity.scan_failed reports a checker that could not advance. Diagnostic
logs retain their normal bounded/lossy behavior.
A pass records an initial upper object-key boundary and seeks forward through the locator with short read transactions. It does not retain a database snapshot for hours. New uploads behind the cursor or beyond that boundary are covered by the next pass; concurrent uploads never become accepted history because the checker sees them. Indexed objects belonging to unfinished uploads are checked too.
Restart and recovery
Checksummed progress lives in <runtime-dir>/integrity/<repository-id>.json.
It is disposable operational state, excluded from canonical history, object
identity, and the wire protocol. No repository migration is required. Keep the
runtime directory between launches if you want to resume coverage.
Only completed object checks advance the cursor. Shutdown interrupts waiting
or throttled verification; after restart, an unfinished object is read from the
beginning. Progress replacement uses a temporary file and rename without
durability flushes: losing recent progress repeats work. It never permits a
partial check to be reported as verified. Crash leftovers with the matching
.writing name are replaced on the next progress save.
Root and repository incarnation changes discard coverage from the old generation
with an explicit event. Malformed, oversized, or checksum-damaged state is
reported and refused, rather than silently read as a valid cursor. To reset a
damaged report, stop the server, retain the report for diagnosis, and remove only
that repository's .json and .writing progress files. Restart begins a new
pass. Do not remove repository files to reset verification.
For object failures, preserve the evidence and use the existing diagnosis and explicit recovery tools. With a known-good copy, verified object repair can replace damaged bytes without rolling back newer accepted history. Online verification is not a substitute for independent backups and restore drills.