Skeets That You Misunderstood#
AT Protocol feed generator ranking posts by controversy using Wilson score intervals.
This is an experiment and not made to be used for real. It's only posts that make you go huh.
I can tell it works because it's all posts from The Onion, about US politics and gay p*rn.
What It Does#
Subscribes to the Bluesky firehose, tracks likes/unlikes, and serves ranked feeds via AT Protocol.
Feeds:
controversial— Daily controversy rankingscontroversial-weekly— Weekly controversy rankings
Quickstart#
1. Prerequisites#
- Bluesky account
- Go 1.26+
- Domain you control (for DID) (for dev, https://srv.us suffices)
2. Create Feed Records#
Create records in your Bluesky repo:
# Publish feeds (requires BSKY_HANDLE and BSKY_PASSWORD)
BSKY_HANDLE=your.handle BSKY_PASSWORD=your-pass go run ./cmd/tools/publish
This creates app.bsky.feed.generator/controversial and controversial-weekly records.
3. Configure#
cp .env.example .env
# Edit .env with:
# FEEDGEN_HOSTNAME=feeds.yourdomain.com
# FEEDGEN_SERVICE_DID=did:web:feeds.yourdomain.com
# FEEDGEN_PUBLISHER_DID=your-did
4. Run#
go run ./cmd/feed
5. Test#
# Get feed
curl "http://localhost:3000/xrpc/app.bsky.feed.getFeedSkeleton?feed=at://did:plc:you/app.bsky.feed.generator/controversial&limit=10" | jq .
# Health check
curl http://localhost:3000/xrpc/_health
# Metrics
curl http://localhost:3000/debug/metrics | jq .
Architecture#
All state is held in memory. A periodic checkpoint (every 10s, plus on shutdown) atomically persists to disk for crash recovery.
graph LR
B[Bluesky Firehose] -->|events| S[Subscriber]
S -->|inc| L[In-Memory Counters]
S -->|update scores| SC[ScoreStore]
L -->|checkpoint| CK[checkpoint.gob]
D[Deleted Set] -->|checkpoint| CK
P[Post Index] -->|checkpoint| CK
SC -->|rank| F[Feed Handler]
F -->|serve| B2[Bluesky PDS]
CK -.->|restart: load| L
CK -.->|restart: load| SC
Data Flow#
sequenceDiagram
participant B as Bluesky Firehose
participant S as Subscriber
participant L as Memory Counters
participant SC as ScoreStore
participant CK as Checkpoint (every 10s)
participant FH as Feed Handler
participant PDS as Bluesky PDS
B->>S: Like/Unlike/Post events
S->>L: IncLikes(uri) — atomic map increment
S->>SC: UpdateScoreWithCounts(uri, likes, unlikes)
L-->>CK: Snapshot maps → persist.go
CK->>CK: tempfile + rename (atomic commit)
PDS->>FH: GET /xrpc/app.bsky.feed.getFeedSkeleton
FH->>SC: Top(limit, window)
SC-->>FH: []ScoredPost sorted
FH-->>PDS: Feed skeleton (URIs only)
PDS-->>User: Hydrated feed
Configuration#
| Variable | Description | Default |
|---|---|---|
FEEDGEN_PORT |
HTTP port | 3000 |
FEEDGEN_HOSTNAME |
Public hostname | example.com |
FEEDGEN_SERVICE_DID |
Service DID | did:web:$HOSTNAME |
FEEDGEN_PUBLISHER_DID |
Your Bluesky DID | did:example:alice |
FEEDGEN_DATA_DIR |
Checkpoint storage dir | ./data |
FEEDGEN_SUBSCRIPTION_ENDPOINT |
Firehose URL | wss://relay1... |
FEEDGEN_LOGGER_LEVEL |
Log level (0=INFO) | 0 |
Project Structure#
cmd/
feed/ # Main feed generator
tools/
publish/ # Publish feed records
unpublish/ # Unpublish feed records
cursorstore/ # Firehose cursor tracking (in-memory)
likesstore/ # Like/unlike counters (in-memory)
deletestore/ # Deleted post tracking (in-memory)
scorestore/ # Wilson score + in-memory sorted indexes
persist/ # Atomic checkpoint save/load
subscriber/ # Firehose consumer
xrpc/ # AT Protocol XRPC handlers
Commands#
# Run feed generator
go run ./cmd/feed
# Publish feeds to Bluesky
BSKY_HANDLE=your.handle BSKY_PASSWORD=pass go run ./cmd/tools/publish
# Unpublish feeds
BSKY_HANDLE=your.handle BSKY_PASSWORD=pass go run ./cmd/tools/unpublish
# Run tests
go test ./...
# Build binary
go build -o feedgen ./cmd/feed