OverviewFilesCommitsIssuesDocsReleasesPeers

atproto-store commits / ff07c0a

0.0.16: localOid — the missing half of the pointer design's integrity story
PointerSubstrate stores { $oid, size } in the shared autobase and resolves it through ensureLocal, which returns a local file hit WITHOUT comparing it to $oid. So a node holding a superseded copy of a record serves it forever and never re-fetches, while its pointer set looks perfectly converged from every surface. Nothing in the stack could even ask "does my file match the pointer?". Live consequence on wj62: two co-owners, identical 139 converged pointers, both resolving all 139, yet different signed repos — differing on exactly one record (app.bsky.actor.profile/self, two different avatar blob refs). The signed content flipped on every signer change. Diagnosing it needed two signed CARs diffed by hand. localOid(collection, rkey) -> { oid, size } | null: what the record file currently on disk hashes to, via the overlay's read-only hashFile (hyper-overlay 0.3.12), which owns the hash definition. Writes nothing. Compare against the pointer's $oid to answer the question directly. This adds the observation, not the fix — ensureLocal still does not verify. 53/53 tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ff07c0aa0d54c30d75e0ddc0d0dae8879417f60c
by plankatron <fleeky@gmail.com> · 2026-07-27T16:50:27.000Z

Changed files (2) +19 -1

lib/overlay-repo-store.js +18 -0
……@@ -110,6 +110,24 @@ export class OverlayRepoStore {
110110 return join(this._recordsDir, collection, rkey + EXT)
111111 }
112112
113 // READ-ONLY: the overlay oid the record file CURRENTLY on disk hashes to, or null when absent.
114 // Writes nothing (no registration, no index entry) — safe against a live store.
115 //
116 // This is the missing half of the pointer design's integrity story. PointerSubstrate stores
117 // { $oid, size } in the shared autobase and resolves it through ensureLocal, which returns a local
118 // file hit WITHOUT comparing it to $oid — so a node holding a superseded copy of a record serves it
119 // forever and never re-fetches, while its pointer set looks perfectly converged. Nothing in the
120 // stack could even ask "does my file match the pointer?", which is why the wj62 profile divergence
121 // (two co-owners, identical 139 pointers, different avatar blob refs) was only visible by diffing
122 // two signed CARs against each other. Compare this against the pointer's $oid to answer it directly.
123 async localOid (collection, rkey) {
124 if (!this._overlay || typeof this._overlay.hashFile !== 'function') return null
125 let p
126 try { p = this._pathFor(collection, rkey) } catch { return null }
127 const r = await this._overlay.hashFile(p)
128 return r ? { oid: r.contentHash, size: r.size } : null
129 }
130
113131 // Returns { oid, size }: oid = the overlay content hash (blake2b) that a PEER fetches the bytes by
114132 // (PointerSubstrate stores {oid,size} as the symmetric autobase pointer). null oid when no overlay.
115133 async put (collection, rkey, record) {
package.json +1 -1
……@@ -1,6 +1,6 @@
11{
22 "name": "atproto-store",
3 "version": "0.0.15",
3 "version": "0.0.16",
44 "description": "Holepunch-native atproto repo store — records in a classic Hyperbee (collection/rkey -> DAG-CBOR). The P2P home; the atproto-repo module is the bridge serializer over it.",
55 "type": "module",
66 "main": "lib/repo-store.js",