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 | /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
# 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 重建映射;标记会跳过后续重启。