#5435·atproto

Space-only blobs are readable through the public `com.atproto.sync.getBlob`

Author: theFestestCreated Aug 22, 2026Updated Aug 22, 2026
Labelsbug

Describe the bug

A blob referenced only by a record in a space is served by com.atproto.sync.getBlob to an unauthenticated client that knows the blob's CID.

To Reproduce

Create a space with an empty member list, upload a blob, and reference it from one record inside that space. Then, with no credential on either call:

com.atproto.space.getBlob   -> HTTP 401
com.atproto.sync.getBlob    -> HTTP 200   "members only"

Script:

bash
PDS=http://localhost:2583
TOK=$(curl -sS "$PDS/xrpc/com.atproto.server.createSession" -H 'content-type: application/json' \
  -d '{"identifier":"alice.test","password":"alice-pass"}' | jq -r .accessJwt)
DID=$(curl -sS "$PDS/xrpc/com.atproto.server.getSession" -H "authorization: Bearer $TOK" | jq -r .did)

SPACE=$(curl -sS "$PDS/xrpc/com.atproto.simplespace.createSpace" -H "authorization: Bearer $TOK" \
  -H 'content-type: application/json' -d "{\"type\":\"my.repro.private\",\"skey\":\"leak-$(date +%s%N)\",
  \"policy\":{\"\$type\":\"com.atproto.simplespace.defs#memberListPolicy\"},
  \"appAccess\":{\"\$type\":\"com.atproto.simplespace.defs#open\"}}" | jq -r .uri)

CID=$(curl -sS "$PDS/xrpc/com.atproto.repo.uploadBlob" -H "authorization: Bearer $TOK" \
  -H 'content-type: text/plain' --data-binary 'members only' | jq -r '.blob.ref["$link"]')

curl -sS -o /dev/null "$PDS/xrpc/com.atproto.space.createRecord" -H "authorization: Bearer $TOK" \
  -H 'content-type: application/json' -d "{\"space\":\"$SPACE\",\"repo\":\"$DID\",
  \"collection\":\"my.repro.doc\",\"rkey\":\"secret\",\"validate\":false,
  \"record\":{\"\$type\":\"my.repro.doc\",\"file\":{\"\$type\":\"blob\",
  \"ref\":{\"\$link\":\"$CID\"},\"mimeType\":\"text/plain\",\"size\":12}}}"

# no credential on either call
curl -s -o /dev/null -w 'space.getBlob -> %{http_code}\n' \
  "$PDS/xrpc/com.atproto.space.getBlob?space=$SPACE&repo=$DID&cid=$CID"
curl -s -w '\nsync.getBlob  -> %{http_code}\n' \
  "$PDS/xrpc/com.atproto.sync.getBlob?did=$DID&cid=$CID"

Expected behavior

The Permissioned Data proposal's "Blob sync" section specifies blobs are "fetched via com.atproto.space.getBlob with the relevant space credential".

I would expect the space credential to be required via any access method that returns space-only content.

Since com.atproto.sync.getBlob is public, refusing to act on space-only blobs is probably the desirable behavior rather than requiring a space credential.

Details

  • Spaces alpha local testing
  • Branch permissioned-data @ 89deb9f, against the @atproto/dev-env multi-PDS network.