Blossom 文件服务器

运行在独立主机名上的媒体托管:内容寻址上传、本地或 S3 兼容存储,以及 kind-24242 认证。

概览

nostrfy 可以作为 Blossom blob 服务器:客户端上传以其 SHA-256 哈希寻址的文件, 中继将它们提供回去。与 REST API 一样,它运行在同一端口上的专用主机名。

配置

toml
[blossom]
host = "media.example.com"          # required — enables the feature
storage = "local"                   # "local" or "s3"
local_path = "./data/images"        # local storage root
max_upload_bytes = 20971520         # 20 MiB
min_free_bytes = 33554432           # refuse uploads when the disk has less free space
restrict_uploads = false            # only allow-listed pubkeys may upload

# For S3 / Cloudflare R2:
s3_endpoint = "https://<account>.r2.cloudflarestorage.com"
s3_region = "auto"
s3_bucket = "nostr-media"
s3_access_key = "..."
s3_secret_key = "..."

在反向代理中将 media.example.com 指向同一端口,然后重启。该主机上的 GET / 会返回 Blossom 服务器信息文档。使用 storage = "s3" 时, 端点必须是 HTTPS,除非主机是回环地址(例如用于测试的本地 MinIO)。

存储布局

两种后端都使用 <npub1...> 层级,以文件的 SHA-256 为键:

  • local — 文件位于 <local_path>/<npub1...>/<sha256>
  • s3 / R2 — 配置的存储桶中的对象 <npub1...>/<sha256>

Blob 字节从不进入中继数据库 — LMDB 只保存 sha256 → 所有者映射和上传 白名单。

端点

方法路径认证说明
GET/—Blossom 服务器信息
GET / HEAD/<sha256>[.ext]—获取 / 探测 blob(字节范围,206)
PUT/uploadkind 24242 (t=upload, x=sha256, expiration)上传 blob — 201 新建,200 已存在
HEAD/uploadkind 24242 (t=upload, x=sha256, expiration)BUD-06 预检 — 上传会被接受吗?
PUT/mediakind 24242 (t=media, x=sha256, expiration)BUD-05 媒体上传(原样存储)
HEAD/mediakind 24242 (t=media, x=sha256, expiration)BUD-05 预检 — 上传会被接受吗?
GET/list/<pubkey>kind 24242 (t=list, expiration)由请求公钥上传的 blob(游标 + limit)
DELETE/<sha256>kind 24242 (t=delete, x=sha256, expiration)删除 blob(仅上传者)

安全说明

  • 用户上传的字节以 X-Content-Type-Options: nosniff 提供。
  • HTML/SVG/XML/JavaScript 还会附加 Content-Disposition: attachment 和 沙箱 CSP,因此媒体源不能用于存储型 XSS。
  • 令牌接受规范中的 base64url(无填充)形式和带填充的标准形式 (BUD-11)。
  • X-SHA-256 头会与实际字节校验 — 不匹配返回 409。
  • 文件以 ETag、Cache-Control: immutable 和存储的内容类型提供。
  • 被 NIP-86 banpubkey 封禁的公钥在每个端点上都会被拒绝。

示例

sh
# Server info
curl https://media.example.com/

# Upload (auth event from your Blossom client, e.g. via nak or the nostr-tools blossom helper)
curl -X PUT -H "Authorization: Nostr <auth>" -H "Content-Type: image/png" --data-binary @photo.png https://media.example.com/upload

# Fetch
curl https://media.example.com/<sha256>

# List your own uploads (auth event with t=list; the path pubkey must be yours)
curl -H "Authorization: Nostr <auth>" https://media.example.com/list/<pubkey-hex>

# Delete (auth event with t=delete and x=<sha256>)
curl -X DELETE -H "Authorization: Nostr <auth>" https://media.example.com/<sha256>

限制上传

在 [blossom] 段中设置 restrict_uploads = true:

toml
[blossom]
host = "media.example.com"
restrict_uploads = true

白名单存储在中继数据库(LMDB)中,用专用命令管理 — 无需重启, 守护进程会自动重载:

sh
nostrfy blossom allow npub1...          # allow a pubkey (npub1... or hex)
nostrfy blossom deny npub1...           # revoke a pubkey
nostrfy blossom list                    # show the list and restrict_uploads

未列入白名单的公钥上传会被 403 拒绝。

备份与迁移

备份配置的 blob 存储和 database.path,以保留完整的 清单和授权状态。sha256 → 所有者映射持久化在 LMDB 中,因此重启是 瞬时的,无需内存索引或启动扫描 — 查找直接从数据库读取映射。升级后的首次启动会 自动执行一次性迁移,从旧 blob 重建映射;标记会跳过后续重启。