Cocoon
[!WARNING]
I 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.
Cocoon is a PDS implementation in Go. It is highly experimental, and is not ready for any production use.
Quick Start with Docker Compose
Prerequisites
- Docker and Docker Compose installed
- A domain name pointing to your server (for automatic HTTPS)
- Ports 80 and 443 open in i.e. UFW
Installation
-
Clone the repository
git clone https://github.com/haileyok/cocoon.git
cd cocoon
-
Create your configuration file
cp .env.example .env
-
Edit .env with your settings
Required settings:
COCOON_DID="did:web:your-domain.com"
COCOON_HOSTNAME="your-domain.com"
COCOON_CONTACT_EMAIL="you@example.com"
COCOON_RELAYS="https://bsky.network"
# Generate with: openssl rand -hex 16
COCOON_ADMIN_PASSWORD="your-secure-password"
# Generate with: openssl rand -hex 32
COCOON_SESSION_SECRET="your-session-secret"
-
Start the services
# Pull pre-built image from GitHub Container Registry
docker-compose pull
docker-compose up -d
Or build locally:
docker-compose build
docker-compose up -d
-
Get your invite code
On first run, an invite code is automatically created. View it with:
docker-compose logs create-invite
Or check the saved file:
cat keys/initial-invite-code.txt
IMPORTANT: Save this invite code! You'll need it to create your first account.
-
Monitor the services
docker-compose logs -f
What Gets Set Up
The Docker Compose setup includes:
- init-keys: Automatically generates cryptographic keys (rotation key and JWK) on first run
- cocoon: The main PDS service running on port 8080
- create-invite: Automatically creates an initial invite code after Cocoon starts (first run only)
- caddy: Reverse proxy with automatic HTTPS via Let's Encrypt
Data Persistence
The following directories will be created automatically:
./keys/ - Cryptographic keys (generated automatically)
rotation.key - PDS rotation key
jwk.key - JWK private key
initial-invite-code.txt - Your first invite code (first run only)
./data/ - SQLite database and blockstore
- Docker volumes for Caddy configuration and certificates
Optional Configuration
SMTP Email Settings
COCOON_SMTP_USER="your-smtp-username"
COCOON_SMTP_PASS="your-smtp-password"
COCOON_SMTP_HOST="smtp.example.com"
COCOON_SMTP_PORT="587"
COCOON_SMTP_EMAIL="noreply@example.com"
COCOON_SMTP_NAME="Cocoon PDS"
S3 Storage
COCOON_S3_BACKUPS_ENABLED=true
COCOON_S3_BLOBSTORE_ENABLED=true
COCOON_S3_REGION="us-east-1"
COCOON_S3_BUCKET="your-bucket"
COCOON_S3_ENDPOINT="https://s3.amazonaws.com"
COCOON_S3_ACCESS_KEY="your-access-key"
COCOON_S3_SECRET_KEY="your-secret-key"
Management Commands
Create an invite code:
docker exec cocoon-pds /cocoon create-invite-code --uses 1
Reset a user's password:
docker exec cocoon-pds /cocoon reset-password --did "did:plc:xxx"
Updating
docker-compose pull
docker-compose up -d
Implemented Endpoints
[!NOTE]
Just 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.
Identity
-
com.atproto.identity.getRecommendedDidCredentials
-
com.atproto.identity.requestPlcOperationSignature
-
com.atproto.identity.resolveHandle
-
com.atproto.identity.signPlcOperation
-
com.atproto.identity.submitPlcOperation
-
com.atproto.identity.updateHandle
Repo
-
com.atproto.repo.applyWrites
-
com.atproto.repo.createRecord
-
com.atproto.repo.putRecord
-
com.atproto.repo.deleteRecord
-
com.atproto.repo.describeRepo
-
com.atproto.repo.getRecord
-
com.atproto.repo.importRepo (Works "okay". Use with extreme caution.)
-
com.atproto.repo.listRecords
-
com.atproto.repo.listMissingBlobs
Server
-
com.atproto.server.activateAccount
-
com.atproto.server.checkAccountStatus
-
com.atproto.server.confirmEmail
-
com.atproto.server.createAccount
-
com.atproto.server.createInviteCode
-
com.atproto.server.createInviteCodes
-
com.atproto.server.deactivateAccount
-
com.atproto.server.deleteAccount
-
com.atproto.server.deleteSession
-
com.atproto.server.describeServer
-
com.atproto.server.getAccountInviteCodes
-
com.atproto.server.getServiceAuth
[ ] com.atproto.server.listAppPasswords - not going to add app passwords
-
com.atproto.server.refreshSession
-
com.atproto.server.requestAccountDelete
-
com.atproto.server.requestEmailConfirmation
-
com.atproto.server.requestEmailUpdate
-
com.atproto.server.requestPasswordReset
-
com.atproto.server.reserveSigningKey
-
com.atproto.server.resetPassword
[] com.atproto.server.revokeAppPassword - not going to add app passwords
-
com.atproto.server.updateEmail
Sync
-
com.atproto.sync.getBlob
-
com.atproto.sync.getBlocks
-
com.atproto.sync.getLatestCommit
-
com.atproto.sync.getRecord
-
com.atproto.sync.getRepoStatus
-
com.atproto.sync.getRepo
-
com.atproto.sync.listBlobs
-
com.atproto.sync.listRepos
[ ] com.atproto.sync.notifyOfUpdate - BGS doesn't even have this implemented lol
-
com.atproto.sync.requestCrawl
-
com.atproto.sync.subscribeRepos
Other
-
com.atproto.label.queryLabels
-
com.atproto.moderation.createReport (Note: this should be handled by proxying, not actually implemented in the PDS)
-
app.bsky.actor.getPreferences
-
app.bsky.actor.putPreferences
License
This project is licensed under MIT license. server/static/pico.css is also licensed under MIT license, available at https://github.com/picocss/pico/.