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
.envwith your settingsRequired 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 -dOr build locally:
docker-compose build docker-compose up -dFor PostgreSQL deployment:
# Add POSTGRES_PASSWORD to your .env file first! docker-compose -f docker-compose.postgres.yaml up -d -
Get your invite code
On first run, an invite code is automatically created. View it with:
docker-compose logs create-inviteOr check the saved file:
cat keys/initial-invite-code.txtIMPORTANT: 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 keyjwk.key- JWK private keyinitial-invite-code.txt- Your first invite code (first run only)
./data/- SQLite database and blockstore- Docker volumes for Caddy configuration and certificates
Optional Configuration#
Database Configuration#
By default, Cocoon uses SQLite which requires no additional setup. For production deployments with higher traffic, you can use PostgreSQL:
# Database type: sqlite (default) or postgres
COCOON_DB_TYPE="postgres"
# PostgreSQL connection string (required if db-type is postgres)
# Format: postgres://user:password@host:port/database?sslmode=disable
COCOON_DATABASE_URL="postgres://cocoon:password@localhost:5432/cocoon?sslmode=disable"
# Or use the standard DATABASE_URL environment variable
DATABASE_URL="postgres://cocoon:password@localhost:5432/cocoon?sslmode=disable"
For SQLite (default):
COCOON_DB_TYPE="sqlite"
COCOON_DB_NAME="/data/cocoon/cocoon.db"
Note: When using PostgreSQL, database backups to S3 are not handled by Cocoon. Use
pg_dumpor your database provider's backup solution instead.
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 supports S3-compatible storage for both database backups (SQLite only) and blob storage (images, videos, etc.):
# Enable S3 backups (SQLite databases only - hourly backups)
COCOON_S3_BACKUPS_ENABLED=true
# Enable S3 for blob storage (images, videos, etc.)
# When enabled, blobs are stored in S3 instead of the database
COCOON_S3_BLOBSTORE_ENABLED=true
# S3 configuration (works with AWS S3, MinIO, Cloudflare R2, etc.)
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"
Blob Storage Options:
COCOON_S3_BLOBSTORE_ENABLED=false(default): Blobs stored in the databaseCOCOON_S3_BLOBSTORE_ENABLED=true: Blobs stored in S3 bucket underblobs/{did}/{cid}
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(Not actually functional, but will return a response as if no blobs were missing)
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 [ ]- not going to add app passwordscom.atproto.server.listAppPasswords-
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 []- not going to add app passwordscom.atproto.server.revokeAppPassword-
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 [ ]- BGS doesn't even have this implemented lolcom.atproto.sync.notifyOfUpdate-
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/.