Blossom 文件服务器
运行在独立主机名上的媒体托管:内容寻址上传、本地或 S3 兼容存储,以及 kind-24242 认证。
概览
nostrfy 可以作为 Blossom blob 服务器:客户端上传以其 SHA-256 哈希寻址的文件, 中继将它们提供回去。与 REST API 一样,它运行在同一端口上的专用主机名。
配置
toml
[blossom]
host = "media.example.com" # 必需 — 启用该功能
storage = "local" # "local" 或 "s3"
local_path = "./data/images" # 本地存储根目录
max_upload_bytes = 20971520 # 即 20 MiB
min_free_bytes = 33554432 # 磁盘剩余空间不足时拒绝上传
restrict_uploads = false # 仅白名单公钥可上传
# 适用于 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 | /upload | kind 24242 (t=upload, x=sha256, expiration) | 上传 blob — 201 新建,200 已存在 |
HEAD | /upload | kind 24242 (t=upload, x=sha256, expiration) | BUD-06 预检 — 上传会被接受吗? |
PUT | /media | kind 24242 (t=media, x=sha256, expiration) | BUD-05 媒体上传(原样存储) |
HEAD | /media | kind 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
# 服务器信息
curl https://media.example.com/
# 上传(来自 Blossom 客户端的认证事件,例如通过 nak 或 nostr-tools 的 blossom 辅助工具)
curl -X PUT -H "Authorization: Nostr <auth>" -H "Content-Type: image/png" --data-binary @photo.png https://media.example.com/upload
# 获取
curl https://media.example.com/<sha256>
# 列出自己的上传(携带 t=list 的认证事件;路径中的公钥必须是你的)
curl -H "Authorization: Nostr <auth>" https://media.example.com/list/<pubkey-hex>
# 删除(携带 t=delete 和 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... # 允许某个公钥(npub1... 或 hex)
nostrfy blossom deny npub1... # 撤销某个公钥
nostrfy blossom list # 显示列表和 restrict_uploads未列入白名单的公钥上传会被 403 拒绝。
备份与迁移
备份配置的 blob 存储和 database.path,以保留完整的
清单和授权状态。sha256 → 所有者映射持久化在 LMDB 中,因此重启是
瞬时的,无需内存索引或启动扫描 — 查找直接从数据库读取映射。升级后的首次启动会
自动执行一次性迁移,从旧 blob 重建映射;标记会跳过后续重启。