xrpc/com.atproto.sync.subscribeRepos
subscribe subscribeRepos
Subscribe to the repository event stream (firehose).
Repository event stream, aka Firehose endpoint. Outputs repo commits with diff data, and identity update events, for all repositories on the current server. Public; no auth. Implemented by PDS and Relay. Frames are binary WebSocket messages consisting of two concatenated DAG-CBOR objects: a header (`{op, t}`) followed by a payload whose shape depends on `t`.
asyncapi: '2.6.0'
info:
title: Bluesky / AT Protocol Event Streams
version: '1.1.0'
description: |
AsyncAPI definition for the public event streams of the Bluesky network and the
underlying AT Protocol.
Three streams are documented:
* **`com.atproto.sync.subscribeRepos`** - the primary repository event stream
("firehose"). Binary WebSocket frames carrying two concatenated DAG-CBOR
objects (header + payload). Implemented by every PDS and Relay; the public
Bluesky relay is reachable at `wss://bsky.network` (legacy) and
`wss://relay1.us-east.bsky.network` (Sync 1.1, with `prevData`).
* **`com.atproto.sync.subscribeLabels`** - the label / negation stream, served
by moderation services. Same binary CBOR frame format as `subscribeRepos`.
* **Jetstream** - a simplified JSON projection of the firehose. Plain
JSON-text WebSocket frames (optionally zstd-compressed). Operated by
Bluesky at `jetstream{1,2}.{us-east,us-west}.bsky.network`.
Schemas are reproduced verbatim from the upstream AT Protocol lexicons
(`com.atproto.sync.subscribeRepos`, `com.atproto.label.subscribeLabels`,
`com.atproto.label.defs`) and the `bluesky-social/jetstream` repository.
contact:
name: Bluesky Social PBC
url: https://bsky.social
email: [email protected]
license:
name: MIT
url: https://github.com/bluesky-social/atproto/blob/main/LICENSE.txt
termsOfService: https://bsky.social/about/support/tos
x-references:
- name: AT Protocol Event Stream specification
url: https://atproto.com/specs/event-stream
- name: AT Protocol Sync specification
url: https://atproto.com/specs/sync
- name: Bluesky Firehose advanced guide
url: https://docs.bsky.app/docs/advanced-guides/firehose
- name: subscribeRepos lexicon
url: https://github.com/bluesky-social/atproto/blob/main/lexicons/com/atproto/sync/subscribeRepos.json
- name: subscribeLabels lexicon
url: https://github.com/bluesky-social/atproto/blob/main/lexicons/com/atproto/label/subscribeLabels.json
- name: label defs lexicon
url: https://github.com/bluesky-social/atproto/blob/main/lexicons/com/atproto/label/defs.json
- name: Jetstream repository
url: https://github.com/bluesky-social/jetstream
defaultContentType: application/cbor
servers:
relay:
url: bsky.network
protocol: wss
description: |
Public Bluesky Relay firehose. Hosts the binary CBOR-framed
`com.atproto.sync.subscribeRepos` endpoint. Legacy endpoint; does not
emit the Sync 1.1 `prevData` field.
relay-sync-1-1:
url: relay1.us-east.bsky.network
protocol: wss
description: |
Bluesky Sync 1.1 relay. Same XRPC endpoints as `bsky.network`, but emits
the inductive-firehose `prevData` field and `#sync` events.
ozone:
url: mod.bsky.app
protocol: wss
description: |
Bluesky's Ozone moderation service. Hosts the binary CBOR-framed
`com.atproto.label.subscribeLabels` endpoint.
jetstream2-us-east:
url: jetstream2.us-east.bsky.network
protocol: wss
description: |
Public Jetstream instance (US-East). JSON projection of the firehose.
jetstream1-us-east:
url: jetstream1.us-east.bsky.network
protocol: wss
description: Public Jetstream instance (US-East).
jetstream1-us-west:
url: jetstream1.us-west.bsky.network
protocol: wss
description: Public Jetstream instance (US-West).
jetstream2-us-west:
url: jetstream2.us-west.bsky.network
protocol: wss
description: Public Jetstream instance (US-West).
channels:
#####################################################################
# com.atproto.sync.subscribeRepos
#####################################################################
xrpc/com.atproto.sync.subscribeRepos:
description: |
Repository event stream, aka Firehose endpoint. Outputs repo commits with
diff data, and identity update events, for all repositories on the
current server. Public; no auth. Implemented by PDS and Relay. Frames are
binary WebSocket messages consisting of two concatenated DAG-CBOR
objects: a header (`{op, t}`) followed by a payload whose shape depends
on `t`.
servers:
- relay
- relay-sync-1-1
parameters:
cursor:
description: The last known event seq number to backfill from.
schema:
type: integer
format: int64
bindings:
ws:
method: GET
query:
type: object
properties:
cursor:
type: integer
description: The last known event seq number to backfill from.
bindingVersion: '0.1.0'
subscribe:
operationId: subscribeRepos
summary: Subscribe to the repository event stream (firehose).
message:
oneOf:
- $ref: '#/components/messages/RepoCommitFrame'
- $ref: '#/components/messages/RepoSyncFrame'
- $ref: '#/components/messages/RepoIdentityFrame'
- $ref: '#/components/messages/RepoAccountFrame'
- $ref: '#/components/messages/RepoHandleFrame'
- $ref: '#/components/messages/RepoMigrateFrame'
- $ref: '#/components/messages/RepoTombstoneFrame'
- $ref: '#/components/messages/RepoInfoFrame'
- $ref: '#/components/messages/RepoErrorFrame'
#####################################################################
# com.atproto.label.subscribeLabels
#####################################################################
xrpc/com.atproto.label.subscribeLabels:
description: |
Subscribe to stream of labels (and negations). Public endpoint
implemented by mod services. Uses the same sequencing scheme as the
repository event stream and the same binary CBOR header/payload framing.
servers:
- ozone
parameters:
cursor:
description: The last known event seq number to backfill from.
schema:
type: integer
format: int64
bindings:
ws:
method: GET
query:
type: object
properties:
cursor:
type: integer
bindingVersion: '0.1.0'
subscribe:
operationId: subscribeLabels
summary: Subscribe to the label/negation stream.
message:
oneOf:
- $ref: '#/components/messages/LabelsFrame'
- $ref: '#/components/messages/LabelInfoFrame'
- $ref: '#/components/messages/LabelErrorFrame'
#####################################################################
# Jetstream
#####################################################################
subscribe:
description: |
Jetstream WebSocket endpoint. Emits a simplified JSON projection of the
AT Protocol firehose. Each WebSocket frame is one JSON event object
(optionally zstd-compressed if `compress=true` is set on the query).
servers:
- jetstream2-us-east
- jetstream1-us-east
- jetstream1-us-west
- jetstream2-us-west
bindings:
ws:
method: GET
query:
type: object
properties:
wantedCollections:
type: array
description: |
Filter events to only the listed collection NSIDs. Supports
NSID prefixes (e.g., `app.bsky.graph.*`). Up to 100 entries.
items:
type: string
wantedDids:
type: array
description: |
Filter events to only the listed repository DIDs. Up to 10,000
entries.
items:
type: string
format: did
maxMessageSizeBytes:
type: integer
description: |
Maximum payload size, in bytes. Zero (the default) is treated
as unlimited; negative values are treated as zero.
default: 0
cursor:
type: integer
format: int64
description: |
Unix microseconds timestamp to begin playback from. If absent
or in the future, the stream operates in live-tail mode.
compress:
type: boolean
description: When `true`, frames are zstd-compressed.
default: false
requireHello:
type: boolean
description: |
When `true`, the server pauses event delivery until the
client sends an `options_update` SubscriberSourcedMessage.
default: false
bindingVersion: '0.1.0'
subscribe:
operationId: jetstreamSubscribe
summary: Subscribe to the Jetstream JSON event stream.
message:
oneOf:
- $ref: '#/components/messages/JetstreamCommitCreate'
- $ref: '#/components/messages/JetstreamCommitUpdate'
- $ref: '#/components/messages/JetstreamCommitDelete'
- $ref: '#/components/messages/JetstreamIdentity'
- $ref: '#/components/messages/JetstreamAccount'
publish:
operationId: jetstreamSubscriberMessage
summary: |
Send a `SubscriberSourcedMessage` to update subscription options after
connecting. Currently supports the `options_update` message.
message:
$ref: '#/components/messages/JetstreamSubscriberSourcedMessage'
components:
messages:
#################################################################
# subscribeRepos frames
#################################################################
RepoCommitFrame:
name: RepoCommitFrame
title: '#commit'
summary: Repository commit event.
contentType: application/cbor
payload:
$ref: '#/components/schemas/RepoCommitFrame'
RepoSyncFrame:
name: RepoSyncFrame
title: '#sync'
summary: |
Sync event - asserts current repository state without including diff
data on the firehose. Used for recovery / desync resolution.
contentType: application/cbor
payload:
$ref: '#/components/schemas/RepoSyncFrame'
RepoIdentityFrame:
name: RepoIdentityFrame
title: '#identity'
summary: Account identity change event (handle, signing key, or PDS).
contentType: application/cbor
payload:
$ref: '#/components/schemas/RepoIdentityFrame'
RepoAccountFrame:
name: RepoAccountFrame
title: '#account'
summary: Account hosting-status change event.
contentType: application/cbor
payload:
$ref: '#/components/schemas/RepoAccountFrame'
RepoHandleFrame:
name: RepoHandleFrame
title: '#handle'
summary: DEPRECATED - use `#identity` instead. Legacy handle change event.
contentType: application/cbor
payload:
$ref: '#/components/schemas/RepoHandleFrame'
RepoMigrateFrame:
name: RepoMigrateFrame
title: '#migrate'
summary: DEPRECATED - use `#account` instead. Legacy account-migration event.
contentType: application/cbor
payload:
$ref: '#/components/schemas/RepoMigrateFrame'
RepoTombstoneFrame:
name: RepoTombstoneFrame
title: '#tombstone'
summary: DEPRECATED - use `#account` instead. Legacy account-tombstone event.
contentType: application/cbor
payload:
$ref: '#/components/schemas/RepoTombstoneFrame'
RepoInfoFrame:
name: RepoInfoFrame
title: '#info'
summary: |
Informational message (not persisted, no `seq`). Currently the only
defined name value is `OutdatedCursor`.
contentType: application/cbor
payload:
$ref: '#/components/schemas/InfoFrame'
RepoErrorFrame:
name: RepoErrorFrame
title: error
summary: |
Error frame (`op = -1`). Possible `error` values for subscribeRepos:
`FutureCursor`, `ConsumerTooSlow`. The connection is closed after this
frame is delivered.
contentType: application/cbor
payload:
$ref: '#/components/schemas/ErrorFrame'
#################################################################
# subscribeLabels frames
#################################################################
LabelsFrame:
name: LabelsFrame
title: '#labels'
summary: A batch of labels (and/or negations).
contentType: application/cbor
payload:
$ref: '#/components/schemas/LabelsFrame'
LabelInfoFrame:
name: LabelInfoFrame
title: '#info'
summary: Informational message on the label stream.
contentType: application/cbor
payload:
$ref: '#/components/schemas/InfoFrame'
LabelErrorFrame:
name: LabelErrorFrame
title: error
summary: |
Error frame (`op = -1`). Possible `error` values for subscribeLabels:
`FutureCursor`.
contentType: application/cbor
payload:
$ref: '#/components/schemas/ErrorFrame'
#################################################################
# Jetstream messages
#################################################################
JetstreamCommitCreate:
name: JetstreamCommitCreate
title: commit (create)
summary: |
Jetstream commit event with `commit.operation = "create"`. Contains
the full record JSON.
contentType: application/json
payload:
$ref: '#/components/schemas/JetstreamCommitCreate'
JetstreamCommitUpdate:
name: JetstreamCommitUpdate
title: commit (update)
summary: |
Jetstream commit event with `commit.operation = "update"`. Contains
the full record JSON, replacing the previous version.
contentType: application/json
payload:
$ref: '#/components/schemas/JetstreamCommitUpdate'
JetstreamCommitDelete:
name: JetstreamCommitDelete
title: commit (delete)
summary: |
Jetstream commit event with `commit.operation = "delete"`. No record
body.
contentType: application/json
payload:
$ref: '#/components/schemas/JetstreamCommitDelete'
JetstreamIdentity:
name: JetstreamIdentity
title: identity
summary: |
Jetstream identity event. Wraps the AT Protocol `#identity` event in
a Jetstream envelope.
contentType: application/json
payload:
$ref: '#/components/schemas/JetstreamIdentity'
JetstreamAccount:
name: JetstreamAccount
title: account
summary: |
Jetstream account event. Wraps the AT Protocol `#account` event in a
Jetstream envelope.
contentType: application/json
payload:
$ref: '#/components/schemas/JetstreamAccount'
JetstreamSubscriberSourcedMessage:
name: JetstreamSubscriberSourcedMessage
title: SubscriberSourcedMessage
summary: |
JSON message sent from client to Jetstream server after connecting.
Currently the only supported `type` is `options_update`.
contentType: application/json
payload:
$ref: '#/components/schemas/JetstreamSubscriberSourcedMessage'
schemas:
#################################################################
# Shared firehose frame primitives
#################################################################
FirehoseFrameHeader:
type: object
description: |
DAG-CBOR object that prefixes every WebSocket frame. `op = 1` for
regular messages (with `t` set to the short-form type, e.g.
`#commit`); `op = -1` for error frames (no `t`).
required: [op]
properties:
op:
type: integer
enum: [1, -1]
description: 1 = regular message; -1 = error frame.
t:
type: string
description: |
Short-form lexicon type identifier, e.g. `#commit`, `#identity`,
`#account`, `#handle`, `#migrate`, `#tombstone`, `#info`,
`#sync`, `#labels`. Required when `op = 1`.
ErrorFrame:
type: object
description: |
Payload of an error frame. The connection is closed by the server
immediately after the frame is sent.
required: [error]
properties:
error:
type: string
description: Short error name.
message:
type: string
description: Optional human-readable error message.
InfoFrame:
type: object
description: |
Informational message payload. Not persisted; has no `seq`.
required: [name]
properties:
name:
type: string
enum: [OutdatedCursor]
description: Short info name. Known values - `OutdatedCursor`.
message:
type: string
description: Optional human-readable message.
#################################################################
# com.atproto.sync.subscribeRepos payload schemas
#################################################################
RepoCommitFrame:
type: object
description: |
`#commit` payload. Represents an update of repository state. Empty
commits (no record changes, only `rev`/signature update) are allowed.
required:
- seq
- rebase
- tooBig
- repo
- commit
- rev
- since
- blocks
- ops
- blobs
- time
properties:
seq:
type: integer
format: int64
description: The stream sequence number of this message.
rebase:
type: boolean
description: DEPRECATED - unused.
tooBig:
type: boolean
description: |
DEPRECATED - replaced by `#sync` event and data limits. Indicates
this commit contained too many ops or was too large; consumers
need a separate fetch to recover.
repo:
type: string
format: did
description: |
The repo this event comes from. (All other message types use
`did` for the same purpose.)
commit:
type: string
format: cid
description: Repo commit object CID.
rev:
type: string
format: tid
description: |
The rev of the emitted commit. Also present inside the commit
block in `blocks` (unless `tooBig`).
since:
type: string
format: tid
nullable: true
description: The rev of the last emitted commit from this repo (if any).
blocks:
type: string
format: byte
description: |
CAR file (binary) containing relevant blocks as a diff since the
previous repo state. The commit block must be the first entry in
the CAR header `roots` list. Maximum 2,000,000 bytes.
maxLength: 2000000
ops:
type: array
maxItems: 200
description: Repo mutation operations included in this commit.
items:
$ref: '#/components/schemas/RepoOp'
blobs:
type: array
description: |
DEPRECATED - will soon always be empty. List of new blob CIDs
referenced by records in this commit.
items:
type: string
format: cid
prevData:
type: string
format: cid
description: |
Root CID of the MST tree for the previous commit (the `since`
revision). Required for the Sync 1.1 / "inductive" firehose.
time:
type: string
format: date-time
description: Timestamp of when this message was originally broadcast.
RepoOp:
type: object
description: A repo operation - a mutation of a single record.
required: [action, path, cid]
properties:
action:
type: string
enum: [create, update, delete]
path:
type: string
description: '`{collection}/{rkey}` path of the affected record.'
cid:
type: string
format: cid
nullable: true
description: |
For creates and updates, the new record CID. For deletions, null.
prev:
type: string
format: cid
description: |
For updates and deletes, the previous record CID (required for
inductive firehose). For creates, this field is omitted.
RepoSyncFrame:
type: object
description: |
`#sync` payload. Updates the repo to a new state without including the
diff on the firehose. Used to recover from broken commit streams or
data-loss incidents.
required: [seq, did, blocks, rev, time]
properties:
seq:
type: integer
format: int64
did:
type: string
format: did
description: |
The account this repo event corresponds to. Must match the value
inside the commit object.
blocks:
type: string
format: byte
description: |
CAR file (binary) containing the commit, as a block. CAR header's
first root must be the commit block CID. Maximum 10,000 bytes.
maxLength: 10000
rev:
type: string
description: |
The rev of the commit. Must match the value inside the commit
object.
time:
type: string
format: date-time
RepoIdentityFrame:
type: object
description: |
`#identity` payload. Signals that an account's identity may have
changed (handle, signing key, or PDS endpoint). Downstream services
should refresh their identity cache.
required: [seq, did, time]
properties:
seq:
type: integer
format: int64
did:
type: string
format: did
time:
type: string
format: date-time
handle:
type: string
format: handle
description: |
Current handle for the account, or `handle.invalid` if validation
fails. Optional; presence or absence does NOT indicate that the
handle itself changed.
RepoAccountFrame:
type: object
description: |
`#account` payload. Signals an account-status change on the emitting
host (PDS or Relay). The status reflects the host that emitted the
event, which may differ from the currently active PDS.
required: [seq, did, time, active]
properties:
seq:
type: integer
format: int64
did:
type: string
format: did
time:
type: string
format: date-time
active:
type: boolean
description: |
True if the host can still serve the account's repository.
status:
type: string
enum:
- takendown
- suspended
- deleted
- deactivated
- desynchronized
- throttled
description: |
If `active = false`, optionally indicates why the account is not
active.
RepoHandleFrame:
type: object
description: DEPRECATED - use `#identity` instead. Legacy handle event.
required: [seq, did, handle, time]
properties:
seq:
type: integer
format: int64
did:
type: string
format: did
handle:
type: string
format: handle
time:
type: string
format: date-time
RepoMigrateFrame:
type: object
description: DEPRECATED - use `#account` instead. Legacy migrate event.
required: [seq, did, migrateTo, time]
properties:
seq:
type: integer
format: int64
did:
type: string
format: did
migrateTo:
type: string
nullable: true
time:
type: string
format: date-time
RepoTombstoneFrame:
type: object
description: DEPRECATED - use `#account` instead. Legacy tombstone event.
required: [seq, did, time]
properties:
seq:
type: integer
format: int64
did:
type: string
format: did
time:
type: string
format: date-time
#################################################################
# com.atproto.label.subscribeLabels payload schemas
#################################################################
LabelsFrame:
type: object
description: '`#labels` payload - a batch of labels and/or negations.'
required: [seq, labels]
properties:
seq:
type: integer
format: int64
labels:
type: array
items:
$ref: '#/components/schemas/Label'
Label:
type: object
description: |
Metadata tag on an atproto resource (eg, repo or record). Defined by
`com.atproto.label.defs#label`.
required: [src, uri, val, cts]
properties:
ver:
type: integer
description: The AT Protocol version of the label object.
src:
type: string
format: did
description: DID of the actor who created this label.
uri:
type: string
format: uri
description: |
AT URI of the record, repository (account), or other resource
this label applies to.
cid:
type: string
format: cid
description: |
Optional; CID of the specific version of the `uri` resource this
label applies to.
val:
type: string
maxLength: 128
description: Short string name of the value/type of this label.
neg:
type: boolean
description: |
If true, this is a negation label that overrides a previous
label.
cts:
type: string
format: date-time
description: Timestamp when this label was created.
exp:
type: string
format: date-time
description: |
Timestamp at which this label expires (no longer applies).
sig:
type: string
format: byte
description: Signature of the dag-cbor encoded label.
#################################################################
# Jetstream payload schemas
#################################################################
JetstreamEventBase:
type: object
description: Fields present on every Jetstream event.
required: [did, time_us, kind]
properties:
did:
type: string
format: did
description: DID of the repository that produced the event.
time_us:
type: integer
format: int64
description: |
Unix microseconds timestamp at which Jetstream processed the
event. Also the cursor value to resume from.
kind:
type: string
enum: [commit, identity, account]
JetstreamCommitBase:
allOf:
- $ref: '#/components/schemas/JetstreamEventBase'
- type: object
required: [commit]
properties:
kind:
type: string
enum: [commit]
JetstreamCommitCreate:
allOf:
- $ref: '#/components/schemas/JetstreamCommitBase'
- type: object
properties:
commit:
type: object
required: [rev, operation, collection, rkey, record, cid]
properties:
rev:
type: string
format: tid
operation:
type: string
enum: [create]
collection:
type: string
description: NSID of the record's collection.
rkey:
type: string
description: Record key.
record:
type: object
description: |
Full record contents as JSON. Schema depends on the
`collection` NSID (e.g. `app.bsky.feed.post`,
`app.bsky.feed.like`, `app.bsky.graph.follow`).
cid:
type: string
format: cid
JetstreamCommitUpdate:
allOf:
- $ref: '#/components/schemas/JetstreamCommitBase'
- type: object
properties:
commit:
type: object
required: [rev, operation, collection, rkey, record, cid]
properties:
rev:
type: string
format: tid
operation:
type: string
enum: [update]
collection:
type: string
rkey:
type: string
record:
type: object
description: New full record contents, replacing the prior version.
cid:
type: string
format: cid
JetstreamCommitDelete:
allOf:
- $ref: '#/components/schemas/JetstreamCommitBase'
- type: object
properties:
commit:
type: object
required: [rev, operation, collection, rkey]
properties:
rev:
type: string
format: tid
operation:
type: string
enum: [delete]
collection:
type: string
rkey:
type: string
JetstreamIdentity:
allOf:
- $ref: '#/components/schemas/JetstreamEventBase'
- type: object
required: [identity]
properties:
kind:
type: string
enum: [identity]
identity:
type: object
description: |
Embedded AT Protocol `#identity` event. Fields mirror the
`com.atproto.sync.subscribeRepos#identity` payload.
required: [did, seq, time]
properties:
did:
type: string
format: did
handle:
type: string
format: handle
seq:
type: integer
format: int64
time:
type: string
format: date-time
JetstreamAccount:
allOf:
- $ref: '#/components/schemas/JetstreamEventBase'
- type: object
required: [account]
properties:
kind:
type: string
enum: [account]
account:
type: object
description: |
Embedded AT Protocol `#account` event. Fields mirror the
`com.atproto.sync.subscribeRepos#account` payload.
required: [active, did, seq, time]
properties:
active:
type: boolean
did:
type: string
format: did
seq:
type: integer
format: int64
time:
type: string
format: date-time
status:
type: string
enum:
- takendown
- suspended
- deleted
- deactivated
- desynchronized
- throttled
JetstreamSubscriberSourcedMessage:
type: object
description: |
Message sent from a Jetstream client to the server after connecting,
used to update subscription options on the fly. Maximum 10 MB per
message; payload limits - 100 collections and 10,000 DIDs.
required: [type, payload]
properties:
type:
type: string
enum: [options_update]
payload:
type: object
properties:
wantedCollections:
type: array
items:
type: string
wantedDids:
type: array
# --- truncated at 32 KB (32 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/bluesky/refs/heads/main/asyncapi/bluesky-asyncapi.yml