Файловый сервер Blossom
Хостинг медиа на отдельном имени хоста: загрузки с адресацией по содержимому, локальное или S3-совместимое хранилище и авторизация kind-24242 для вашего Nostr-релея.
Обзор
nostrfy может работать как сервер блобов Blossom: клиенты загружают файлы, адресуемые по их SHA-256-хэшу, а релей отдаёт их обратно. Как и REST API, он работает на выделенном имени хоста на том же порту.
Конфигурация
[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, если хост не является loopback (например, локальный MinIO для тестирования).
Структура хранилища
Оба бэкенда используют иерархию <npub1...> с ключом по SHA-256 файла:
- local — файлы в
<local_path>/<npub1...>/<sha256> - s3 / R2 — объекты
<npub1...>/<sha256>в настроенном бакете
Байты блобов никогда не попадают в базу данных релея — LMDB хранит только отображение sha256 → владелец и список разрешённых загрузок.
Конечные точки
| Метод | Путь | Авторизация | Описание |
|---|---|---|---|
GET | / | — | Информация о сервере Blossom |
GET / HEAD | /<sha256>[.ext] | — | Получение / проверка блоба (байтовые диапазоны, 206) |
PUT | /upload | kind 24242 (t=upload, x=sha256, expiration) | Загрузка блоба — 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) | Блобы, загруженные запрашивающим ключом (курсор + limit) |
DELETE | /<sha256> | kind 24242 (t=delete, x=sha256, expiration) | Удаление блоба (только загрузивший) |
Заметки о безопасности
- Загруженные пользователями байты отдаются с
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 и сохранённым типом содержимого.
- Pubkey, заблокированный через NIP-86
banpubkey, отклоняется на каждой конечной точке.
Пример
# Информация о сервере
curl https://media.example.com/
# Загрузка (auth-событие из вашего Blossom-клиента, например через nak или blossom-хелпер nostr-tools)
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>
# Список своих загрузок (auth-событие с t=list; pubkey в пути должен быть вашим)
curl -H "Authorization: Nostr <auth>" https://media.example.com/list/<pubkey-hex>
# Удаление (auth-событие с t=delete и x=<sha256>)
curl -X DELETE -H "Authorization: Nostr <auth>" https://media.example.com/<sha256>Ограничение загрузок
Установите restrict_uploads = true в секции [blossom]:
[blossom]
host = "media.example.com"
restrict_uploads = trueСписок разрешённых хранится в базе данных релея (LMDB) и управляется специальными командами — перезапуск не нужен, демон перезагружается автоматически:
nostrfy blossom allow npub1... # разрешить ключ (npub1... или hex)
nostrfy blossom deny npub1... # отозвать ключ
nostrfy blossom list # показать список и restrict_uploadsЗагрузки от ключей вне списка отклоняются с кодом 403.
Резервные копии и миграция
Создавайте резервные копии как настроенного хранилища блобов, так и database.path, чтобы сохранить полную
инвентаризацию и состояние авторизации. Отображение sha256 → владелец сохраняется в LMDB, поэтому перезапуски
мгновенны и не требуют индекса в памяти или сканирования при старте — поиск читает отображение напрямую из
базы данных. Автоматическая одноразовая миграция перестраивает отображение из устаревших блобов при первом
запуске после обновления; маркер пропускает последующие перезапуски.