cocoon

command module
v0.14.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 30, 2026 License: MIT Imports: 15 Imported by: 0

README

Cocoon

Cocoon is a PDS implementation in Go.

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
  1. Clone the repository

    git clone https://github.com/haileyok/cocoon.git
    cd cocoon
    
  2. Create your configuration file

    cp .env.example .env
    
  3. 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"
    
  4. 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
    

    For PostgreSQL deployment:

    # Add POSTGRES_PASSWORD to your .env file first!
    docker-compose -f docker-compose.postgres.yaml up -d
    
  5. 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.

  6. 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
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_dump or 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"

# Optional: CDN/public URL for blob redirects
# When set, com.atproto.sync.getBlob redirects to this URL instead of proxying
COCOON_S3_CDN_URL="https://cdn.example.com"

Blob Storage Options:

  • COCOON_S3_BLOBSTORE_ENABLED=false (default): Blobs stored in the database
  • COCOON_S3_BLOBSTORE_ENABLED=true: Blobs stored in S3 bucket under blobs/{did}/{cid}

Blob Serving Options:

  • Without COCOON_S3_CDN_URL: Blobs are proxied through the PDS server
  • With COCOON_S3_CDN_URL: getBlob returns a 302 redirect to {CDN_URL}/blobs/{did}/{cid}

Tip: For Cloudflare R2, you can use the public bucket URL as the CDN URL. For AWS S3, you can use CloudFront or the S3 bucket URL directly if public access is enabled.

PLC Directory

Cocoon uses the public PLC directory (https://plc.directory) to create, update, and resolve did:plc identities. You can point it at a different directory, such as a local PLC instance for integration testing, so that test accounts are never registered with the real directory:

COCOON_PLC_URL="http://localhost:2582"

Warning: Accounts created against a non-default PLC directory only exist in that directory and will not be resolvable by the wider network. Leave this unset for production deployments.

YubiKeys

Accounts can turn on two-factor sign-in with an authenticator app or a YubiKey from /account/2fa. To let people add a YubiKey just by touching it, get a free client ID and API key from Yubico and set:

COCOON_YUBICO_CLIENT_ID="12345"
COCOON_YUBICO_API_KEY="base64-secret-key-from-yubico"

YubiKey codes are then checked with Yubico's YubiCloud service at api.yubico.com. If it can't be reached, YubiKey sign-in fails until it's back; authenticator codes and backup codes still work.

Without these settings, a YubiKey must first have a slot programmed with a secret key that the server stores (the setup page gives the ykman command).

Alpine based image

The default image is based on Debian. You can use the Alpine-based image if you prefer.

[!NOTE] Currently, we do not have pre-built Alpine-based image on the GitHub Container Registry. You have to build them locally.

In the compose file, replace every dockerfile: Dockerfile by dockerfile: Dockerfile.alpine, e.g.

services:
  cocoon:
    build:
      context: .
      dockerfile: Dockerfile.alpine

You can also build the image locally with

docker build -f Dockerfile.alpine -t cocoon:alpine .
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
Spaces (permissioned data, alpha)

Cocoon implements the ATProto Spaces alpha. See docs/spaces.md.

  • com.atproto.space.createRecord, putRecord, deleteRecord, applyWrites
  • com.atproto.space.getRecord, listRecords, listRepoOps, getLatestCommit, getRepo, listSpaces
  • com.atproto.space.getBlob, listBlobs
  • com.atproto.space.getDelegationToken, getSpaceCredential, notifyCredentialRevoked
  • com.atproto.space.notifyWrite, registerNotify, unregisterNotify, listRepos
  • com.atproto.simplespace.createSpace, getSpace, updateSpace, deleteSpace
  • com.atproto.simplespace.putMember, removeMember, listMembers
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/.

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
cmd
cocoon command
internal
db
totp
Package totp implements RFC 6238 time-based one-time passwords with the parameters every mainstream authenticator app supports: HMAC-SHA1, 6 digits, 30 second period.
Package totp implements RFC 6238 time-based one-time passwords with the parameters every mainstream authenticator app supports: HMAC-SHA1, 6 digits, 30 second period.
yubicloud
Package yubicloud checks YubiKey OTPs with Yubico's YubiCloud service, so keys work as they come from the factory: the secret behind a factory YubiKey's OTPs is known only to Yubico.
Package yubicloud checks YubiKey OTPs with Yubico's YubiCloud service, so keys work as they come from the factory: the secret behind a factory YubiKey's OTPs is known only to Yubico.
yubicloud/yubicloudtest
Package yubicloudtest provides a fake YubiCloud validation server for tests.
Package yubicloudtest provides a fake YubiCloud validation server for tests.
yubiotp
Package yubiotp validates Yubico OTPs locally, without YubiCloud.
Package yubiotp validates Yubico OTPs locally, without YubiCloud.
scopes
Package scopes parses and validates atproto OAuth permission scopes (proposal 0011-auth-scopes).
Package scopes parses and validates atproto OAuth permission scopes (proposal 0011-auth-scopes).
Package space implements the primitives of ATProto Spaces (permissioned data), following @atproto/space in bluesky-social/atproto PR #5187 at 5b95b2f2: the set hash, signed space repo commits, space tokens, HTTP message signatures and the space repo CAR format.
Package space implements the primitives of ATProto Spaces (permissioned data), following @atproto/space in bluesky-social/atproto PR #5187 at 5b95b2f2: the set hash, signed space repo commits, space tokens, HTTP message signatures and the space repo CAR format.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL