This project is part of the content-maestro repository. If you want Bluesky integration and automatic publishing of posts there as well, you need to deploy this app.
A Go-based HTTP API server that integrates with the Bluesky AT Protocol. It exposes REST endpoints for creating posts or threaded replies, optionally attaching media and URLs. API key middleware secures every request, and structured logging provides visibility into request flow.
Before running the service, make sure you have:
- Go 1.21+
- A Bluesky handle (e.g.,
username.bsky.social) - A Bluesky App Password generated in Bluesky settings
- A strong
SERVER_API_KEYthat clients must send via headers .env.exampleas a reference for all environment variables
-
Clone the repository:
-
Install dependencies:
go mod tidy
-
Create a
.envfile:cp .env.example .env
Then populate the required variables:
BLUESKY_HANDLE=your_handle.bsky.social BLUESKY_APP_PASSWORD=your_app_password SERVER_API_KEY=your_server_api_key SERVER_PORT=8080 LOG_LEVEL=infoUse a dedicated Bluesky App Password (not your main password) and a unique API key.
-
Run the server:
go run cmd/server/main.go
Optional production build:
go build -o bluesky-connector cmd/server/main.go ./bluesky-connector
The server listens on
http://localhost:8080unlessSERVER_PORToverrides it.
All post-creation endpoints require the X-API-Key header containing your SERVER_API_KEY value.
| Header | Type | Required | Description |
|---|---|---|---|
X-API-Key |
string | Yes | API key defined in the .env file |
Error Response (401 Unauthorized):
{
"detail": "Invalid or missing API key"
}Checks the service status and returns a timestamped heartbeat.
curl -X GET http://localhost:8080/bluesky/api/health{
"status": "ok",
"timestamp": "2024-01-01T12:00:00Z"
}Creates a Bluesky post (or thread if the text exceeds the configured limit), with optional media and URL reply.
Content-Type: multipart/form-data
| Parameter | Type | Required | Description |
|---|---|---|---|
text |
string | Yes | Main post content. Long input is split into a numbered thread automatically |
url |
string | No | A URL appended as the final reply in the thread |
image |
file | No | Image attached to the first post in the thread |
Simple post:
curl -X POST "http://localhost:8080/bluesky/api/posts/create" \
-H "X-API-Key: your_api_key" \
-F "text=Hello, Bluesky!"Post with image:
curl -X POST "http://localhost:8080/bluesky/api/posts/create" \
-H "X-API-Key: your_api_key" \
-F "text=Check out this snapshot!" \
-F "image=@/path/to/image.jpg"Post with URL reply:
curl -X POST "http://localhost:8080/bluesky/api/posts/create" \
-H "X-API-Key: your_api_key" \
-F "text=Interesting article on federation" \
-F "url=https://example.com/article"Full request (text + image + URL):
curl -X POST "http://localhost:8080/bluesky/api/posts/create" \
-H "X-API-Key: your_api_key" \
-F "text=Deep dive into decentralized social" \
-F "url=https://example.com/deep-dive" \
-F "image=@/path/to/image.jpg"{
"posts": [
{
"uri": "at://did:plc:example/app.bsky.feed.post/3knx123",
"cid": "bafyreigexample",
"text": "Hello, Bluesky!"
}
]
}Threaded response:
{
"posts": [
{
"text": "🧵 0/2 First part of a long update..."
},
{
"text": "🧵 1/2 Second part..."
},
{
"text": "🧵 2/2 Final thoughts..."
}
]
}Error:
{
"error": "Invalid payload"
}Publishes a fixed text post ("test") to verify authentication and connectivity.
curl -X POST "http://localhost:8080/bluesky/api/test/posts/create" \
-H "X-API-Key: your_api_key"{
"posts": [
{
"text": "test"
}
]
}When the supplied text exceeds the maximum length (default 265 characters):
- The content is split at word boundaries into multiple posts.
- Each post is prefixed with thread counters (e.g.,
🧵 0/3). - Every part is published as a reply to the previous one to form a thread.
- The optional image is attached only to the first post in the sequence.
- If a
urlis provided, it becomes the final reply in the thread.
This project is licensed under the MIT License. See the LICENSE file for details.