A community based topic aggregation platform built on atproto
1# Aggregator Setup Scripts
2
3This directory contains scripts to help you set up and register your aggregator with Coves instances.
4
5## Overview
6
7Aggregators are automated services that post content to Coves communities. They are similar to Bluesky's feed generators and labelers. To use aggregators with Coves, you need to:
8
91. Create a PDS account for your aggregator (gets you a DID)
102. **(Optional)** Verify a custom domain via `.well-known/atproto-did`
113. Register with a Coves instance
124. Create a service declaration record
135. **Generate an API key** for authentication
14
15These scripts automate this process for you.
16
17### Handle Options
18
19You have two choices for your aggregator's handle:
20
211. **PDS-assigned handle** (simpler): Use the handle from your PDS, e.g., `my-aggregator.bsky.social`. No domain verification needed—skip steps 2-3.
22
232. **Custom domain handle** (branded): Use your own domain, e.g., `news.example.com`. Requires hosting a `.well-known/atproto-did` file on your domain.
24
25## Prerequisites
26
27- **Tools**: `curl`, `jq` (for JSON processing)
28- **Account**: Email address for creating the PDS account
29- **(For custom domain only)**: Domain ownership and ability to serve HTTPS files
30
31## Quick Start
32
33### Interactive Setup (Recommended)
34
35Run the scripts in order:
36
37```bash
38# Make scripts executable
39chmod +x *.sh
40
41# Step 1: Create PDS account
42./1-create-pds-account.sh
43
44# Steps 2-3: OPTIONAL - Only if you want a custom domain handle
45# ./2-setup-wellknown.sh
46# ./3-register-with-coves.sh (after uploading .well-known)
47
48# Step 4: Create service declaration
49./4-create-service-declaration.sh
50
51# Step 5: Generate API key (requires browser for OAuth)
52./5-create-api-key.sh
53```
54
55**Minimal setup** (PDS handle only): Steps 1, 4, 5
56**Custom domain**: Steps 1, 2, 3, 4, 5
57
58### Automated Setup Example
59
60For a reference implementation of automated setup, see the Kagi News aggregator at [aggregators/kagi-news/scripts/setup.sh](../../aggregators/kagi-news/scripts/setup.sh).
61
62The Kagi script shows how to automate all 4 steps (with the manual .well-known upload step in between).
63
64## Script Reference
65
66### 1-create-pds-account.sh
67
68**Purpose**: Creates a PDS account for your aggregator
69
70**Prompts for**:
71- PDS URL (default: https://bsky.social)
72- Handle (e.g., mynewsbot.bsky.social)
73- Email
74- Password
75
76**Outputs**:
77- `aggregator-config.env` - Configuration file with DID and credentials
78- Prints your DID and access tokens
79
80**Notes**:
81- Keep the config file secure! It contains your credentials
82- The PDS automatically generates a DID:PLC for you
83- You can use any PDS service, not just bsky.social
84
85### 2-setup-wellknown.sh
86
87**Purpose**: Generates the `.well-known/atproto-did` file for domain verification
88
89**Prompts for**:
90- Your domain (e.g., rss-bot.example.com)
91
92**Outputs**:
93- `.well-known/atproto-did` - File containing your DID
94- `nginx-example.conf` - Example nginx configuration
95- `apache-example.conf` - Example Apache configuration
96
97**Manual step required**:
98Upload the `.well-known` directory to your web server. The file must be accessible at:
99```
100https://yourdomain.com/.well-known/atproto-did
101```
102
103**Verify it works**:
104```bash
105curl https://yourdomain.com/.well-known/atproto-did
106# Should return your DID (e.g., did:plc:abc123...)
107```
108
109### 3-register-with-coves.sh
110
111**Purpose**: Registers your aggregator with a Coves instance
112
113**Prompts for**:
114- Coves instance URL (default: https://api.coves.social)
115
116**Prerequisites**:
117- `.well-known/atproto-did` must be accessible from your domain
118- Scripts 1 and 2 must be completed
119
120**What it does**:
1211. Verifies your `.well-known/atproto-did` is accessible
1222. Calls `social.coves.aggregator.register` XRPC endpoint
1233. Coves verifies domain ownership
1244. Inserts your aggregator into the `users` table
125
126**Outputs**:
127- Updates `aggregator-config.env` with Coves instance URL
128- Prints registration confirmation
129
130### 4-create-service-declaration.sh
131
132**Purpose**: Creates the service declaration record in your repository
133
134**Prompts for**:
135- Display name (e.g., "RSS News Aggregator")
136- Description
137- Source URL (GitHub repo, etc.)
138- Maintainer DID (optional)
139
140**What it does**:
1411. Creates a `social.coves.aggregator.service` record at `at://your-did/social.coves.aggregator.service/self`
1422. Jetstream consumer will index this into the `aggregators` table
1433. Communities can now discover and authorize your aggregator
144
145**Outputs**:
146- Updates `aggregator-config.env` with record URI and CID
147- Prints record details
148
149### 5-create-api-key.sh
150
151**Purpose**: Generates an API key for aggregator authentication
152
153**Prerequisites**:
154- Steps 1-4 completed
155- Aggregator indexed by Coves (usually takes a few seconds after step 4)
156- Web browser for OAuth login
157
158**What it does**:
1591. Guides you through OAuth login in your browser
1602. Provides the JavaScript to call the `createApiKey` endpoint
1613. Validates the API key format
1624. Saves the key to your config file
163
164**Outputs**:
165- Updates `aggregator-config.env` with `COVES_API_KEY`
166- Provides instructions for updating your `.env` file
167
168**Important Notes**:
169- The API key is shown **ONCE** and cannot be retrieved later
170- API keys replace password-based authentication
171- Keys can be revoked and regenerated at any time
172- Store securely - never commit to version control
173
174## Configuration File
175
176After running all scripts, you'll have an `aggregator-config.env` file with:
177
178```bash
179# Identity
180AGGREGATOR_DID="did:plc:..."
181AGGREGATOR_HANDLE="mynewsbot.example.com"
182AGGREGATOR_PDS_URL="https://bsky.social"
183AGGREGATOR_DOMAIN="mynewsbot.example.com"
184
185# Coves Instance
186COVES_INSTANCE_URL="https://coves.social"
187SERVICE_DECLARATION_URI="at://did:plc:.../social.coves.aggregator.service/self"
188SERVICE_DECLARATION_CID="..."
189
190# API Key (from Step 5)
191COVES_API_KEY="ckapi_..."
192```
193
194**For your aggregator's `.env` file, you only need:**
195
196```bash
197COVES_API_KEY=ckapi_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
198COVES_API_URL=https://coves.social
199```
200
201## What Happens Next?
202
203After completing all 5 steps:
204
2051. **Your aggregator is registered** in the Coves instance's `users` table
2062. **Your service declaration is indexed** in the `aggregators` table (takes a few seconds)
2073. **Your API key is stored** and can be used for authentication
2084. **Community moderators can authorize** your aggregator for their communities
2095. **Your aggregator can post** to authorized communities (or all if you're a trusted aggregator)
210
211## Creating an Authorization
212
213Authorizations are created by community moderators, not by aggregators. The moderator writes a `social.coves.aggregator.authorization` record to their community's repository.
214
215See `docs/aggregators/SETUP_GUIDE.md` for more information on the authorization process.
216
217## Posting to Communities
218
219Once authorized, your aggregator can post using your API key:
220
221```bash
222curl -X POST https://coves.social/xrpc/social.coves.community.post.create \
223 -H "Authorization: Bearer $COVES_API_KEY" \
224 -H "Content-Type: application/json" \
225 -d '{
226 "community": "c-worldnews.coves.social",
227 "content": "Your post content",
228 "facets": []
229 }'
230```
231
232The API key handles all authentication - no OAuth token refresh needed.
233
234## Troubleshooting
235
236### Error: "DomainVerificationFailed"
237
238- Verify `.well-known/atproto-did` is accessible: `curl https://yourdomain.com/.well-known/atproto-did`
239- Check the content matches your DID exactly (no extra whitespace)
240- Ensure HTTPS is working (not HTTP)
241- Check CORS headers if accessing from browser
242
243### Error: "AlreadyRegistered"
244
245- You've already registered this DID with this Coves instance
246- This is safe to ignore if you're re-running the setup
247
248### Error: "DIDResolutionFailed"
249
250- Your DID might be invalid or not found in the PLC directory
251- Verify your DID exists: `curl https://plc.directory/<your-did>`
252- Wait a few seconds and try again (PLC directory might be propagating)
253
254### Service declaration not appearing
255
256- Wait 5-10 seconds for Jetstream consumer to index it
257- Check the Jetstream logs for errors
258- Verify the record was created: Check your PDS at `at://your-did/social.coves.aggregator.service/self`
259
260## Example: Kagi News Aggregator
261
262For a complete reference implementation, see the Kagi News aggregator at `aggregators/kagi-news/`.
263
264The Kagi aggregator includes an automated setup script at [aggregators/kagi-news/scripts/setup.sh](../../aggregators/kagi-news/scripts/setup.sh) that demonstrates how to:
265
266- Automate the entire registration process
267- Use environment variables for configuration
268- Handle errors gracefully
269- Integrate the setup into your aggregator project
270
271This shows how you can package scripts 1-4 into a single automated flow for your specific aggregator.
272
273## Security Notes
274
275- **Never commit `aggregator-config.env`** to version control
276- Store credentials securely (use environment variables or secret management)
277- Rotate access tokens regularly
278- Use HTTPS for all API calls
279- Validate community authorization before posting
280
281## More Information
282
283- [Aggregator Setup Guide](../../docs/aggregators/SETUP_GUIDE.md)
284- [Aggregator PRD](../../docs/aggregators/PRD_AGGREGATORS.md)
285- [atProto Identity Guide](../../ATPROTO_GUIDE.md)
286- [Coves Communities PRD](../../docs/PRD_COMMUNITIES.md)
287
288## Support
289
290If you encounter issues:
291
2921. Check the troubleshooting section above
2932. Review the full documentation in `docs/aggregators/`
2943. Open an issue on GitHub with:
295 - Which script failed
296 - Error message
297 - Your domain (without credentials)