music on atproto
plyr.fm
1# tap
2
3bluesky's ATProto sync utility for backfilling and streaming custom lexicons.
4
5## install
6
7```bash
8go install github.com/bluesky-social/indigo/cmd/tap@latest
9```
10
11binary lands at `~/go/bin/tap`
12
13## run locally
14
15```bash
16TAP_SIGNAL_COLLECTION=fm.plyr.track \
17TAP_COLLECTION_FILTERS=fm.plyr.* \
18TAP_LOG_LEVEL=info \
19~/go/bin/tap run
20```
21
22this will:
231. enumerate all repos with `fm.plyr.track` via `com.atproto.sync.listReposByCollection`
242. backfill those repos, extracting any `fm.plyr.*` records
253. stream the firehose for new records
264. serve events via websocket at `ws://localhost:2480/channel`
27
28## what we found
29
30initial network scan (dec 2025):
31- 35 repos with `fm.plyr.track` records
32- 497 total records indexed
33
34breakdown:
35```
36fm.plyr.track 273
37fm.plyr.like 90
38fm.plyr.list 41
39fm.plyr.dev.track 18
40fm.plyr.comment 15
41fm.plyr.actor.profile 13
42(plus staging/dev variants)
43```
44
45## consuming events
46
47events come through `/channel` websocket as JSON:
48
49```json
50{
51 "id": 439,
52 "type": "record",
53 "record": {
54 "live": false,
55 "did": "did:plc:...",
56 "collection": "fm.plyr.track",
57 "rkey": "3m7m3wyasmi2l",
58 "action": "create",
59 "record": {
60 "title": "...",
61 "artist": "...",
62 "audioUrl": "https://..."
63 }
64 }
65}
66```
67
68ack events to consume them: `{"ack": <id>}`
69
70see `sandbox/tap/read_events.py` for example consumer.
71
72## api endpoints
73
74- `GET /health` - status check
75- `POST /repos/add` - track a DID
76- `POST /repos/remove` - stop tracking
77- `GET /stats/repo-count` - tracked repos
78- `GET /stats/record-count` - indexed records
79- `WS /channel` - event stream
80
81## resources
82
83- [tap README](https://github.com/bluesky-social/indigo/blob/main/cmd/tap/README.md)
84- [bailey's guide](https://marvins-guide.leaflet.pub/3m7ttuppfzc23)
85- [@atproto/tap](https://www.npmjs.com/package/@atproto/tap) - typescript client