An atproto PDS written in Go
1# Cocoon 2 3> [!WARNING] 4I migrated and have been running my main account on this PDS for months now without issue, however, I am still not responsible if things go awry, particularly during account migration. Please use caution. 5 6Cocoon is a PDS implementation in Go. It is highly experimental, and is not ready for any production use. 7 8## Quick Start with Docker Compose 9 10### Prerequisites 11 12- Docker and Docker Compose installed 13- A domain name pointing to your server (for automatic HTTPS) 14- Ports 80 and 443 open in i.e. UFW 15 16### Installation 17 181. **Clone the repository** 19 ```bash 20 git clone https://github.com/haileyok/cocoon.git 21 cd cocoon 22 ``` 23 242. **Create your configuration file** 25 ```bash 26 cp .env.example .env 27 ``` 28 293. **Edit `.env` with your settings** 30 31 Required settings: 32 ```bash 33 COCOON_DID="did:web:your-domain.com" 34 COCOON_HOSTNAME="your-domain.com" 35 COCOON_CONTACT_EMAIL="you@example.com" 36 COCOON_RELAYS="https://bsky.network" 37 38 # Generate with: openssl rand -hex 16 39 COCOON_ADMIN_PASSWORD="your-secure-password" 40 41 # Generate with: openssl rand -hex 32 42 COCOON_SESSION_SECRET="your-session-secret" 43 ``` 44 454. **Start the services** 46 ```bash 47 # Pull pre-built image from GitHub Container Registry 48 docker-compose pull 49 docker-compose up -d 50 ``` 51 52 Or build locally: 53 ```bash 54 docker-compose build 55 docker-compose up -d 56 ``` 57 58 **For PostgreSQL deployment:** 59 ```bash 60 # Add POSTGRES_PASSWORD to your .env file first! 61 docker-compose -f docker-compose.postgres.yaml up -d 62 ``` 63 645. **Get your invite code** 65 66 On first run, an invite code is automatically created. View it with: 67 ```bash 68 docker-compose logs create-invite 69 ``` 70 71 Or check the saved file: 72 ```bash 73 cat keys/initial-invite-code.txt 74 ``` 75 76 **IMPORTANT**: Save this invite code! You'll need it to create your first account. 77 786. **Monitor the services** 79 ```bash 80 docker-compose logs -f 81 ``` 82 83### What Gets Set Up 84 85The Docker Compose setup includes: 86 87- **init-keys**: Automatically generates cryptographic keys (rotation key and JWK) on first run 88- **cocoon**: The main PDS service running on port 8080 89- **create-invite**: Automatically creates an initial invite code after Cocoon starts (first run only) 90- **caddy**: Reverse proxy with automatic HTTPS via Let's Encrypt 91 92### Data Persistence 93 94The following directories will be created automatically: 95 96- `./keys/` - Cryptographic keys (generated automatically) 97 - `rotation.key` - PDS rotation key 98 - `jwk.key` - JWK private key 99 - `initial-invite-code.txt` - Your first invite code (first run only) 100- `./data/` - SQLite database and blockstore 101- Docker volumes for Caddy configuration and certificates 102 103### Optional Configuration 104 105#### Database Configuration 106 107By default, Cocoon uses SQLite which requires no additional setup. For production deployments with higher traffic, you can use PostgreSQL: 108 109```bash 110# Database type: sqlite (default) or postgres 111COCOON_DB_TYPE="postgres" 112 113# PostgreSQL connection string (required if db-type is postgres) 114# Format: postgres://user:password@host:port/database?sslmode=disable 115COCOON_DATABASE_URL="postgres://cocoon:password@localhost:5432/cocoon?sslmode=disable" 116 117# Or use the standard DATABASE_URL environment variable 118DATABASE_URL="postgres://cocoon:password@localhost:5432/cocoon?sslmode=disable" 119``` 120 121For SQLite (default): 122```bash 123COCOON_DB_TYPE="sqlite" 124COCOON_DB_NAME="/data/cocoon/cocoon.db" 125``` 126 127> **Note**: When using PostgreSQL, database backups to S3 are not handled by Cocoon. Use `pg_dump` or your database provider's backup solution instead. 128 129#### SMTP Email Settings 130```bash 131COCOON_SMTP_USER="your-smtp-username" 132COCOON_SMTP_PASS="your-smtp-password" 133COCOON_SMTP_HOST="smtp.example.com" 134COCOON_SMTP_PORT="587" 135COCOON_SMTP_EMAIL="noreply@example.com" 136COCOON_SMTP_NAME="Cocoon PDS" 137``` 138 139#### S3 Storage 140 141Cocoon supports S3-compatible storage for both database backups (SQLite only) and blob storage (images, videos, etc.): 142 143```bash 144# Enable S3 backups (SQLite databases only - hourly backups) 145COCOON_S3_BACKUPS_ENABLED=true 146 147# Enable S3 for blob storage (images, videos, etc.) 148# When enabled, blobs are stored in S3 instead of the database 149COCOON_S3_BLOBSTORE_ENABLED=true 150 151# S3 configuration (works with AWS S3, MinIO, Cloudflare R2, etc.) 152COCOON_S3_REGION="us-east-1" 153COCOON_S3_BUCKET="your-bucket" 154COCOON_S3_ENDPOINT="https://s3.amazonaws.com" 155COCOON_S3_ACCESS_KEY="your-access-key" 156COCOON_S3_SECRET_KEY="your-secret-key" 157``` 158 159**Blob Storage Options:** 160- `COCOON_S3_BLOBSTORE_ENABLED=false` (default): Blobs stored in the database 161- `COCOON_S3_BLOBSTORE_ENABLED=true`: Blobs stored in S3 bucket under `blobs/{did}/{cid}` 162 163### Management Commands 164 165Create an invite code: 166```bash 167docker exec cocoon-pds /cocoon create-invite-code --uses 1 168``` 169 170Reset a user's password: 171```bash 172docker exec cocoon-pds /cocoon reset-password --did "did:plc:xxx" 173``` 174 175### Updating 176 177```bash 178docker-compose pull 179docker-compose up -d 180``` 181 182## Implemented Endpoints 183 184> [!NOTE] 185Just because something is implemented doesn't mean it is finished. Tons of these are returning bad errors, don't do validation properly, etc. I'll make a "second pass" checklist at some point to do all of that. 186 187### Identity 188 189- [x] `com.atproto.identity.getRecommendedDidCredentials` 190- [x] `com.atproto.identity.requestPlcOperationSignature` 191- [x] `com.atproto.identity.resolveHandle` 192- [x] `com.atproto.identity.signPlcOperation` 193- [x] `com.atproto.identity.submitPlcOperation` 194- [x] `com.atproto.identity.updateHandle` 195 196### Repo 197 198- [x] `com.atproto.repo.applyWrites` 199- [x] `com.atproto.repo.createRecord` 200- [x] `com.atproto.repo.putRecord` 201- [x] `com.atproto.repo.deleteRecord` 202- [x] `com.atproto.repo.describeRepo` 203- [x] `com.atproto.repo.getRecord` 204- [x] `com.atproto.repo.importRepo` (Works "okay". Use with extreme caution.) 205- [x] `com.atproto.repo.listRecords` 206- [x] `com.atproto.repo.listMissingBlobs` (Not actually functional, but will return a response as if no blobs were missing) 207 208### Server 209 210- [x] `com.atproto.server.activateAccount` 211- [x] `com.atproto.server.checkAccountStatus` 212- [x] `com.atproto.server.confirmEmail` 213- [x] `com.atproto.server.createAccount` 214- [x] `com.atproto.server.createInviteCode` 215- [x] `com.atproto.server.createInviteCodes` 216- [x] `com.atproto.server.deactivateAccount` 217- [ ] `com.atproto.server.deleteAccount` 218- [x] `com.atproto.server.deleteSession` 219- [x] `com.atproto.server.describeServer` 220- [ ] `com.atproto.server.getAccountInviteCodes` 221- [ ] `com.atproto.server.getServiceAuth` 222- ~~[ ] `com.atproto.server.listAppPasswords`~~ - not going to add app passwords 223- [x] `com.atproto.server.refreshSession` 224- [ ] `com.atproto.server.requestAccountDelete` 225- [x] `com.atproto.server.requestEmailConfirmation` 226- [x] `com.atproto.server.requestEmailUpdate` 227- [x] `com.atproto.server.requestPasswordReset` 228- [ ] `com.atproto.server.reserveSigningKey` 229- [x] `com.atproto.server.resetPassword` 230- ~~[] `com.atproto.server.revokeAppPassword`~~ - not going to add app passwords 231- [x] `com.atproto.server.updateEmail` 232 233### Sync 234 235- [x] `com.atproto.sync.getBlob` 236- [x] `com.atproto.sync.getBlocks` 237- [x] `com.atproto.sync.getLatestCommit` 238- [x] `com.atproto.sync.getRecord` 239- [x] `com.atproto.sync.getRepoStatus` 240- [x] `com.atproto.sync.getRepo` 241- [x] `com.atproto.sync.listBlobs` 242- [x] `com.atproto.sync.listRepos` 243- ~~[ ] `com.atproto.sync.notifyOfUpdate`~~ - BGS doesn't even have this implemented lol 244- [x] `com.atproto.sync.requestCrawl` 245- [x] `com.atproto.sync.subscribeRepos` 246 247### Other 248 249- [ ] `com.atproto.label.queryLabels` 250- [x] `com.atproto.moderation.createReport` (Note: this should be handled by proxying, not actually implemented in the PDS) 251- [x] `app.bsky.actor.getPreferences` 252- [x] `app.bsky.actor.putPreferences` 253 254## License 255 256This project is licensed under MIT license. `server/static/pico.css` is also licensed under MIT license, available at [https://github.com/picocss/pico/](https://github.com/picocss/pico/).