A Blossom media server for Nostr running on Fastly Compute, optimized for video content.
Fastly Compute (Rust) → Backblaze B2 (blobs) + Fastly KV (metadata)
→ GCP (async moderation)
- BUD-01: Blob retrieval (GET/HEAD)
- BUD-02: Upload/delete/list management
- BUD-03: User server list support
- Nostr auth: Kind 24242 signature validation
- Shadow restriction: Moderated content only visible to owner
- Range requests: Native video seeking support
- Free egress: B2 → Fastly bandwidth is free
- Fastly CLI
- Rust with wasm32-wasi target
- Backblaze B2 account
- Fastly account with Compute enabled
rustup target add wasm32-wasi- Create a Backblaze B2 bucket
- Create an application key with read/write access
- Set up Fastly stores:
# Create KV store
fastly kv-store create --name blossom_metadata
# Create config store
fastly config-store create --name blossom_config
fastly config-store-entry create --store-id <id> --key b2_bucket --value your-bucket-name
fastly config-store-entry create --store-id <id> --key b2_region --value us-west-004
# Create secret store
fastly secret-store create --name blossom_secrets
fastly secret-store-entry create --store-id <id> --key b2_key_id --value your-key-id
fastly secret-store-entry create --store-id <id> --key b2_app_key --value your-app-key# Copy the example config and fill in your credentials
cp fastly.toml.example fastly.toml
# Edit fastly.toml with your B2 credentials (this file is gitignored)
# Then run:
fastly compute serveNote: fastly.toml is gitignored to prevent accidentally committing secrets. The [local_server.secret_stores] section is only used for local testing.
fastly compute publish| Method | Path | Description |
|---|---|---|
GET |
/<sha256>[.ext] |
Retrieve blob |
HEAD |
/<sha256>[.ext] |
Check blob exists |
| Method | Path | Auth | Description |
|---|---|---|---|
PUT |
/upload |
Required | Upload blob |
HEAD |
/upload |
None | Get upload requirements |
DELETE |
/<sha256> |
Required | Delete blob |
GET |
/list/<pubkey> |
Optional | List user's blobs |
Uses Nostr kind 24242 events:
{
"kind": 24242,
"content": "Upload blob",
"tags": [
["t", "upload"],
["x", "<sha256>"],
["expiration", "<unix_timestamp>"]
]
}Send as: Authorization: Nostr <base64_encoded_signed_event>
MIT