dontshowmethis#
An AI-powered Bluesky content moderation system that automatically labels replies to monitored accounts using LLM-based content analysis.
Overview#
This project monitors Bluesky posts in real-time via Jetstream and uses a local LLM (via LM Studio) to classify replies to watched accounts. It automatically applies labels such as "bad-faith", "off-topic", and "funny" to help users filter and moderate content.
The system consists of two components:
- Go Consumer - Monitors the Jetstream firehose and analyzes replies using an LLM
- Skyware Labeler - TypeScript server that manages and emits content labels
Architecture#
Jetstream → Go Consumer → LM Studio (LLM) → Labeler Service → Bluesky
- The Go consumer subscribes to Jetstream and monitors replies to specified accounts
- When a reply is detected, it fetches the parent post and sends both to LM Studio
- The LLM classifies the reply based on the system prompt
- Labels are emitted via the Skyware labeler service
- Labels are propagated to Bluesky's labeling system
Prerequisites#
- LM Studio with a compatible model loaded
- A Bluesky account for the labeler
Installation#
Clone the repository#
git clone https://github.com/haileyok/dontshowmethis.git
cd dontshowmethis
Install Go dependencies#
go mod download
Install labeler dependencies#
cd labeler
yarn install
cd ..
Configuration#
Copy the example environment file and configure it:
cp .env.example .env
Environment Variables#
For the Go Consumer:
PDS_URL- Your Bluesky PDS URL (e.g.,https://bsky.social)ACCOUNT_HANDLE- Your Bluesky account handleACCOUNT_PASSWORD- Your Bluesky account passwordWATCHED_OPS- Comma-separated list of DIDs to monitor for repliesJETSTREAM_URL- Jetstream WebSocket URL (default:wss://jetstream2.us-west.bsky.network/subscribe)LABELER_URL- URL of your labeler service (e.g.,http://localhost:3000)LABELER_KEY- Authentication key for the labeler APILMSTUDIO_HOST- LM Studio API host (e.g.,http://localhost:1234)
For the Skyware Labeler:
SKYWARE_DID- Your labeler's DIDSKYWARE_SIG_KEY- Your labeler's signing keyEMIT_LABEL_KEY- Secret key for the emit label API (must matchLABELER_KEYabove)
Running the Services#
1. Start LM Studio#
- Open LM Studio
- Load a compatible model (recommended:
google/gemma-3-27bor similar) - Start the local server (usually runs on
http://localhost:1234)
2. Start the Labeler Service#
cd labeler
npm start
The labeler will start two servers:
- Port 14831: Skyware labeler server
- Port 3000: Label emission API
3. Start the Go Consumer#
go run .
Or build and run:
go build -o dontshowmethis
./dontshowmethis
Usage#
Once running, the system will:
- Connect to Jetstream and monitor the firehose
- Watch for replies to accounts specified in
WATCHED_OPS - Automatically analyze and label qualifying replies
- Log all actions to stdout
Finding Account DIDs#
To monitor specific accounts, you need their DIDs. You can find a DID by:
curl "https://bsky.social/xrpc/com.atproto.identity.resolveHandle?handle=username.bsky.social"
Add the returned DID to your WATCHED_OPS environment variable.
How Content Classification Works#
See lmstudio.go:147 for the system prompt.
Development#
Project Structure#
.
├── main.go # CLI setup and consumer initialization
├── handle_post.go # Post handling and labeling logic
├── lmstudio.go # LLM client and content classification
├── sets/
│ └── domains.go # Political domain list (currently unused)
├── labeler/
│ ├── index.ts # Skyware labeler service
│ └── package.json # Labeler dependencies
├── .env.example # Example environment configuration
└── README.md # This file
Adding New Labels#
-
Add the label constant in
main.go:const LabelNewLabel = "new-label" -
Add it to the labeler's allowed labels in
labeler/index.ts:const LABELS: Record<string, boolean> = { 'bad-faith': true, 'off-topic': true, 'funny': true, 'new-label': true, // Add here } -
Update the LLM schema in
lmstudio.goto include the new classification -
Update the handling logic in
handle_post.goto emit the new label
License#
MIT