A Dart implementation of a Blossom server following the Blossom specification for storing and serving blobs with Nostr authentication.
- SHA-256 Content Addressing: All blobs are stored and accessed using their SHA-256 hash
- Nostr Authentication: Upload and delete operations require valid Nostr event signatures
- Whitelist System: Access control based on public key whitelisting
- RESTful HTTP API: Standard HTTP endpoints for blob storage and retrieval
- SQLite Database: Persistent storage for whitelist and metadata
- File Size Limits: Configurable upload size limits (default: 600MB)
- CLI Management: Command-line tools for managing whitelisted pubkeys
- Install Dart SDK (3.0 or later)
- Clone and setup:
git clone <repository-url> cd blossomd dart pub get
This implementation uses the following key dependencies:
- bip340: For Nostr signature verification (BIP-340 Schnorr signatures)
- shelf: HTTP server framework
- sqlite3: Database storage for whitelist management
- crypto: SHA-256 hashing for content addressing
- dotenv: For loading configuration from .env files
Configure the server using a .env
file or environment variables:
-
Copy the example configuration file:
cp env.example .env
-
Edit
.env
with your settings:# Base directory for data storage (database and blob files) WORKING_DIR=./data # HTTP server port PORT=3334 # Public server URL (used for generating blob URLs in responses) SERVER_URL=http://localhost:3334
WORKING_DIR
: Base directory for data storage (default:./data
)PORT
: HTTP server port (default:3334
)SERVER_URL
: Public server URL (default:http://localhost:<PORT>
)
The server loads configuration in this order:
- Values from
.env
file (if it exists) - Environment variables
- Default values
# First, set up your configuration (one-time setup)
cp env.example .env
# Edit .env with your settings
# Start with .env file configuration
dart run bin/blossomd.dart
# Alternative: Start with environment variables
WORKING_DIR=/var/blossom PORT=8080 dart run bin/blossomd.dart
The server includes built-in CLI commands for managing whitelisted pubkeys:
dart run bin/blossomd.dart whitelist add <pubkey>
Example:
dart run bin/blossomd.dart whitelist add 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef
dart run bin/blossomd.dart whitelist list
dart run bin/blossomd.dart whitelist remove <pubkey>
Pubkeys must be exactly 64 characters long and contain only hexadecimal characters (0-9, a-f, A-F).
# Show help
dart run bin/blossomd.dart --help
# Show version
dart run bin/blossomd.dart --version
# Show whitelist help
dart run bin/blossomd.dart whitelist --help
GET /<sha256>
HEAD /<sha256>
PUT /upload
Authorization: Nostr <base64-encoded-event>
Content-Type: application/octet-stream
<binary-data>
GET /list/<pubkey>
DELETE /<sha256>
Authorization: Nostr <base64-encoded-event>
HEAD /upload
Upload and delete operations require a Nostr authorization header with a signed event:
{
"kind": 24242,
"created_at": <timestamp>,
"tags": [
["t", "upload"],
["x", "<sha256-hash>"],
["m", "<mime-type>"],
["expiration", "<timestamp>"]
],
"content": "",
"pubkey": "<user-pubkey>",
"id": "<event-id>",
"sig": "<signature>"
}
The event must be base64-encoded and included in the Authorization header:
Authorization: Nostr <base64-encoded-event>
Signature Verification: The server performs proper BIP-340 signature verification on all Nostr events. Invalid signatures will be rejected with appropriate error messages.
The server uses SQLite for storing whitelist information. The database is automatically created at <WORKING_DIR>/database.sqlite
.
You can directly access the SQLite database:
sqlite3 data/database.sqlite
-- View all whitelisted pubkeys
SELECT * FROM whitelist;
-- Add a pubkey manually
INSERT OR REPLACE INTO whitelist (pubkey) VALUES ('pubkey_hex');
-- Remove a pubkey manually
DELETE FROM whitelist WHERE pubkey = 'pubkey_hex';
Blobs are stored in a hierarchical directory structure:
data/
├── database.sqlite
└── blobs/
├── ab/
│ └── abcd1234...
├── cd/
│ └── cdef5678...
└── ...
# This requires implementing Nostr event signing in your client
curl -X PUT http://localhost:3334/upload \
-H "Authorization: Nostr <base64-event>" \
-H "Content-Type: application/octet-stream" \
--data-binary @myfile.jpg
curl http://localhost:3334/abc123def456...
Development setup:
# Copy the example
cp env.example .env
# Edit .env for development
WORKING_DIR=./data
PORT=3334
SERVER_URL=http://localhost:3334
Production setup:
# Edit .env for production
WORKING_DIR=/var/lib/blossom
PORT=8080
SERVER_URL=https://blossom.example.com
# Add a user to whitelist
dart run bin/blossomd.dart whitelist add abc123def456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
# Add another user
dart run bin/blossomd.dart whitelist add def456abc789012cdef456abc789012cdef456abc789012cdef456abc789012cdef
# List all users
dart run bin/blossomd.dart whitelist list
# Remove user
dart run bin/blossomd.dart whitelist remove def456abc789012cdef456abc789012cdef456abc789012cdef456abc789012cdef
- ✅ Nostr Signature Verification: Proper BIP-340 signature verification using the bip340 library
- ✅ Whitelist-based Access Control: Binary permission system (whitelisted = allowed, not whitelisted = denied)
- ✅ Event Expiration Checking: Time-based authorization validation
- ✅ File Hash Verification: SHA-256 content integrity verification
- ✅ Upload Size Limits: Configurable file size restrictions
- ✅ Event Structure Validation: Proper Nostr event format validation
- Use HTTPS in production
- Regular database backups
- Monitor disk space usage
- Implement rate limiting
- Log monitoring and alerting
- Consider adding request throttling
- Implement proper error handling and recovery
- Blob Ownership: The
/list/<pubkey>
endpoint returns empty results (not yet implemented) - Metrics: No built-in metrics or monitoring endpoints
- Clustering: Single-instance deployment only
- Rate Limiting: No built-in rate limiting (should be added for production)
dart test
dart analyze
dart compile exe bin/blossomd.dart -o blossomd
This project is open source. See LICENSE file for details.
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests if applicable
- Submit a pull request
For issues and questions, please use the GitHub issue tracker.