chadtmiller.com/knotless
git that lives in your Atmosphere account
A private repository's space syncs past a record that cannot be read
MergedI read the whole diff but did not run it or read the checks. I find the verification argument sound, so I approve.
ReadOn::changesreturns a mismatched value as a no-value change (deleted from the index) and reports it as unreadable. The set hash is still folded over the op CIDs and checked against the MAC'd commit inread_on, so dropping a bad value loosens nothing.starnow hashes a non-map record by its bytes and sets it aside. An archive that does come to its commit still verifies. A spoiled archive fails the commit check, andmemberthen callsfrom_start.from_startreads the oplog from the empty hash. A compacted oplog fails the commit check, so the repo is not read, as today.- The last-write-with-no-value race still errors in
changesand sends the repo to be read whole.
Minor, not blocking: member falls back to from_start on any error from whole, including transient network errors. That only costs an extra oplog read, and the commit check is just as strict there.
The docs page is updated, and the Changeset: none line is on a line of its own.
I still approve. Since my earlier review (cbd810e) the only change is in crates/knotless-serve/src/spaces.rs. whole and from_start now pass the commit rev to replace as &str (rev.as_str()) instead of &String. This is a type fix and changes no behavior, so the verification argument I checked before still holds. My earlier non-blocking note about falling back to from_start on any error from whole still applies. The check, test, e2e and docs runs for this commit were still queued or running when I looked, so I haven't seen them pass.
8 passed, 2 skipped
What changes
One unreadable record no longer stops a space's sync. A record in a member's repo whose value does not encode to the CID it was written with, or whose bytes are not a DRISL map, is left out of knotless-serve's index of the space (taken out, if an earlier read put it there). It is logged at warn as
space record unreadable, with the space, the member's DID, the record's path (key) and why. The rest of that repo, and of the space, is synced as before.Reading on from the oplog (
space_sync::ReadOn::changes) now gives back the records that do not check out against their write's CID, rather than failing the read. A last write with no value is still a race with a later write, and still sends the repo to be read whole, as before.Reading whole (
space_sync::star) no longer fails on a record that is not a DRISL map. It hashes the record's bytes as it does any other, sets it aside as unreadable, and leaves the check against the commit to decide.A repo whose STAR archive does not come to its commit is now read from the start of its oplog (
listRepoOpswith nosince, from the empty set hash). Its records replace those the repo gave before, as the archive's would. This is logged asspace repo read from its oplog.space_sync::read_ontakessinceas anOptionfor this.docs/design/access.mdsays all this, and why.Why
A pds.js encoder bug, fixed now, stored some
dev.knotless.checkrecords cut short: integers of 2^32 or more lost their bytes. The runner's repo in group-host's space holds 96 such records. Their stored bytes neither decode nor hash to their CIDs. Each sync then went:a record that does not come to its CID, so read whole.a record that is not a DRISL map, so the repo failed.The space's cursor then stayed where it was, so no new check reached the repo's pulls page. One bad record hid that whole repo.
Verification. The commit, vouched for by the MAC, holds the digest of a set hash over each record's
(path, CID), not over its bytes. So the question is where each CID comes from:An archive that does come to its commit but holds a non-map record is verified as it is. That record is simply not indexed.
I considered going record by record with
getRecord's per-record MAC. knotless does not check that MAC yet. It vouches for one record's CID, not for the set the repo holds, so we would also need a listing we could trust. The oplog already gives both under one commit MAC.Nothing here loosens what a host can pass off. Every record indexed has a value whose DRISL encoding is a CID inside a set that a MAC'd commit vouches for. The checks against the commit are unchanged.
Changeset: none
How it was checked
space_syncunit tests:private_repositories_indexed_here(gateway, over the spaces alpha), which the request asked for first:space_record.value), keeping its CID, and forces the repo off its oplog as the test already does.harness/pds.mjsnow prints the PDS's actor store directory (actors).telemetrynotes test for the two new log lines.checkandtest(the coverage gate). Nothing was built locally.Shortcomings
listRepoOpsfails on bytes it cannot decode, so it cannot serve that case. The undecodable case is covered by thestarunit test. pds.js does serve such values (as the ones that do not come to their CID that prod logged).ReadOn::changes. It is covered by the unit test, not end to end: a test cannot spoil a write between its landing and the sync that reads it.Review
Start with
ReadOn::changesandstarincrates/knotless/src/space_sync.rs, thenmemberandfrom_startincrates/knotless-serve/src/spaces.rs. The verification argument above is the part to check.🤖 Generated with Claude Code