Bluesky / AT Protocol PDS
github.com · Storage · 1 record · JSON
Bluesky / AT Protocol PDS โ 5 MiB blob default, GC'd unless referenced Storage
fails: quotaemail?card?Rper-object-cap
| Free tier | blobUploadLimit defaults to 5 * 1024 * 1024 = 5 MiB per blob, overridable per-PDS via the BLOB_UPLOAD_LIMIT env var. Per-account: bsky.app's hosted PDS is generous in practice but the protocol layer only guarantees blobs that are REFERENCED by a record. unit mixed; metered on unstated |
|---|---|
| Price | none stated |
| Per-object cap | 5 MiB per blob (5242880 bytes) |
| Retention | not stated |
| Egress | Free on bsky.app's PDS and on any self-hosted PDS (it serves blobs directly from /xrpc/com.atproto.sync.getBlob). No egress meter. |
| Identity | email (placeholder) not published |
| Limits |
per-object-cap 5 MiB default (config.ts: blobUploadLimit: env.blobUploadLimit ?? 5 * 1024 * 1024); blobs unreferenced within a time window (minutes) are deleted; account-level repo size limits are enforced by the AppView/PDS separately. |
| Binding | per-object-cap the per-object cap sets the shard size and therefore the object count; it bounds the total more tightly than any headline quota |
| Interface | cli http-xrpc |
| Tooling | XRPC: POST /xrpc/com.atproto.repo.uploadBlob (binary body + Content-Type) -> returns a blob ref; must then be referenced by a record (e.g. app.bsky.feed.post embed) or it is garbage-collected. Client libs: @atproto/api, atproto Python SDK, goat CLI. |
| Composes with | Self-hosting a PDS removes the 5 MiB default (it is a config value), so a self-hosted PDS is bounded by YOUR disk, not Bluesky's โ that makes it a hosting product, not free storage. On bsky.app's PDS the 5 MiB cap plus the reference requirement makes it useless for bulk. The usable margin is only the 'unreferenced blobs are deleted within minutes' semantics working in reverse: referenced blobs are retained indefinitely. |
| Reliability | Blob GC is literal and fast โ an upload without a referencing record vanishes. That is the single most important fact about this candidate. |
| Notes | blobUploadLimit defaults to 5 * 1024 * 1024 = 5 MiB per blob, overridable per-PDS via the BLOB_UPLOAD_LIMIT env var. Per-account: bsky.app's hosted PDS is generous in practice but the protocol layer only guarantees blobs that are REFERENCED by a record. |
| Evidence rung | reported One page read moves it to documented. |
| Checked | 2026-10-21 (inherited); stale on 2026-11-04 |
| Parsed numbers |
|
Evidence (2)
| date | source | what it proved |
|---|---|---|
| 2026-10-21 | raw.githubusercontent.com/bluesky-social/atproto/main/packag | blobUploadLimit: env.blobUploadLimit ?? 5 * 1024 * 1024, // 5mb |
| 2026-10-21 | raw.githubusercontent.com/bluesky-social/atproto/main/lexico | 'The blob will be deleted if it is not referenced within a time window (eg, minutes). Blob restrictions (mimetype, size, etc) are enforced when the reference is created. Requires auth, implemented by PDS.' |
More fields (3)
{
"verdict_note": "PLAN.md ยง3.2 rules fails-quota by default, because no row in this set clears 500 GiB per unit for a user.",
"unit_note": "free_tier.unit is 'mixed' because the headline is denominated in MiB or KiB, which the schema enum does not carry; the verbatim free_tier.stated keeps the true unit. See the storage-dossier handover.",
"dead_end": {
"failure_class": "quota",
"why": "blobUploadLimit defaults to 5 * 1024 * 1024 = 5 MiB per blob, overridable per-PDS via the BLOB_UPLOAD_LIMIT env var. Per-account: bsky.app's hosted PDS is generous in practice but the protocol layer only guarantees blobs that are REFERENCED by a record.",
"retry_when": null,
"first_recorded": "2026-10-21"
}
}Unknown or withheld values (7)
$.extras.dead_end.retry_whenprocess$.free_tier.notesprocess$.identity_cost.detaildefaulted$.identity_cost.kindidentity-defaulted$.per_object_cap.notesnot-extracted$.retention.notesnot-extracted$.retention.statednot-extracted