OverviewFilesCommitsIssuesDocsReleasesPeers

atproto-bridge commits / 979258e

0.9.43: implement com.atproto.sync.listBlobs (org #32 slice 2)
Answered MethodNotImplemented until now, which made ORPHAN detection impossible for anyone reading this membrane. The referenced blob set is derivable by walking records; a blob uploaded and referenced by NO record is invisible to any such walk, and that is exactly the class that permanently lost the 2026-07-03 image. Relays to the peer's new list-blobs (self-custody: the bridge holds no blobs), paging by cursor. ⛔ `since` is accepted and NOT honoured — blob pointers carry no rev, so a rev filter would silently return the wrong set. The response says so (`sinceIgnored`) rather than pretending. This is the same trap getRepo?since already sits in (#34), and stating it beats a caller getting correct results by accident today and a silent regression later. Added to BOTH server.js and server-bare.js: bin/pds-serve.js picks by runtime (bare in prod), and the two are meant to mirror endpoint-for-endpoint. 64/64 membrane tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
979258e348f53b7722ce015aa8269d786637a1a8
by plankatron <fleeky@gmail.com> · 2026-08-07T02:24:07.000Z

Changed files (3) +33 -1

package.json +1 -1
……@@ -1,6 +1,6 @@
11{
22 "name": "atproto-bridge",
3 "version": "0.9.42",
3 "version": "0.9.43",
44 "description": "Stateless keyless atproto MEMBRANE \u2014 fronts self-custody hyper-proto-peers to bluesky over HTTP/WS, holds no keys and never signs. Bare-runnable. Deploy root / flat closure over the identity+repo+store+pds packages.",
55 "type": "module",
66 "main": "index.js",
pds/lib/server-bare.js +16 -0
……@@ -171,6 +171,22 @@ export function createPdsServer ({ peers = null, serverDid = null, availableUser
171171 if (!front) return sendErr(res, 404, 'RepoNotFound', 'repo not fronted here')
172172 return sendJson(res, 200, await front.getLatestCommit())
173173 }
174 // listBlobs: the DID's published blob INVENTORY, relayed from the peer. The referenced set can
175 // be derived by walking records, so this exists for exactly one thing that cannot: an ORPHAN,
176 // a blob uploaded and referenced by no record — the class that permanently lost the
177 // 2026-07-03 image. `since` is accepted and NOT honoured (pointers carry no rev); saying so is
178 // better than filtering wrongly, and it is reported in the response.
179 case 'com.atproto.sync.listBlobs': {
180 const front = await frontIfPeer(q.get('did'))
181 if (!front) return notHere(res)
182 if (typeof front.listBlobs !== 'function') return sendErr(res, 501, 'MethodNotImplemented', 'peer does not serve list-blobs')
183 const r = await front.listBlobs({ limit: q.get('limit'), cursor: q.get('cursor') })
184 if (!r || r.skipped) return sendErr(res, 501, 'MethodNotImplemented', 'peer has no blob inventory')
185 const body = { cids: r.cids || [] }
186 if (r.cursor) body.cursor = r.cursor
187 if (q.get('since')) body.sinceIgnored = true
188 return sendJson(res, 200, body)
189 }
174190 case 'com.atproto.sync.getRepo': {
175191 const front = await frontIfPeer(q.get('did') || undefined)
176192 if (!front) return sendErr(res, 404, 'RepoNotFound', 'repo not fronted here')
pds/lib/server.js +16 -0
……@@ -119,6 +119,22 @@ export function createPdsServer ({ peers = null, serverDid = null, availableUser
119119 if (!front) return sendErr(res, 404, 'RepoNotFound', 'repo not fronted here')
120120 return sendJson(res, 200, await front.getLatestCommit())
121121 }
122 // listBlobs: the DID's published blob INVENTORY, relayed from the peer. The referenced set can
123 // be derived by walking records, so this exists for exactly one thing that cannot: an ORPHAN,
124 // a blob uploaded and referenced by no record — the class that permanently lost the
125 // 2026-07-03 image. `since` is accepted and NOT honoured (pointers carry no rev); saying so is
126 // better than filtering wrongly, and it is reported in the response.
127 case 'com.atproto.sync.listBlobs': {
128 const front = await frontIfPeer(q.get('did'))
129 if (!front) return notHere(res)
130 if (typeof front.listBlobs !== 'function') return sendErr(res, 501, 'MethodNotImplemented', 'peer does not serve list-blobs')
131 const r = await front.listBlobs({ limit: q.get('limit'), cursor: q.get('cursor') })
132 if (!r || r.skipped) return sendErr(res, 501, 'MethodNotImplemented', 'peer has no blob inventory')
133 const body = { cids: r.cids || [] }
134 if (r.cursor) body.cursor = r.cursor
135 if (q.get('since')) body.sinceIgnored = true
136 return sendJson(res, 200, body)
137 }
122138 case 'com.atproto.sync.getRepo': {
123139 const front = await frontIfPeer(q.get('did') || undefined)
124140 if (!front) return sendErr(res, 404, 'RepoNotFound', 'repo not fronted here')