🌟

NostrのNIP-96でm4aやmp4をアップロードする

に公開

当記事は、Nostr Advent Calendar 2025の13日目の記事です。
前日はHogusさん、翌日は淀川さんです。

https://adventar.org/calendars/12046

Nostrを始めた頃、音声や動画ファイルをアップロードするのに分散システムでどうしたらよいのか考えていて、IPFSを試してWebのゲートウェイを試したりしていたのですが、どうしても処理が遅いなどの問題があり断念したりしていました。

結局、Cloudflare R2にアップロードすることで落ち着いていたのですが、Blossomというものがあると聞いて興味が湧きました。

https://github.com/hzrd149/blossom

GPTにまとめてもらったところ、一言でいうと、Blossom は「ハッシュ指定のバイナリストレージ」で、BUD はその拡張仕様群です。とのことでした。

あー、GPT便利。以下はBUDの説明をまとめてもらったものです。

BUD (Blossom Upgrade Documents)

BUDはBlossom サーバーが追加で実装できる機能を定義した短い仕様書です。必須ではなく、対応はサーバーごとに異なります。

各 BUD の概要

  • BUD-00: BUD 自体の定義(基本文書)
  • BUD-01: サーバー要件と Blob の取得方法
  • BUD-02: Blob のアップロードと管理
  • BUD-03: ユーザーのサーバー一覧管理
  • BUD-04: Blob のミラーリング(複製)
  • BUD-05: メディア最適化(画像・動画など)
  • BUD-06: アップロード要件(制限・条件)
  • BUD-07: 有料アップロード/支払い要求
  • BUD-08: Nostr 用ファイルメタデータタグ
  • BUD-09: Blob の通報(レポート)機能
  • BUD-10: Blossom 独自 URI スキーマ

NIP-96 (HTTP File Storage Integration)

BUD-02でアップロードすることができるようですが、BUDとは別で NIP-96 というのがあり、今回はこれを使います。(BUD-02用のスクリプトをまだ書いていないため。)

https://github.com/nostr-protocol/nips/blob/master/96.md

NIP-96 は Nostr で画像・動画・音声・その他ファイルを扱うための標準仕様で以下を標準化します。

  • ファイルをどこに・どうやってアップロードするか
  • 誰がアップロードしたかをどう証明するか
  • 取得した URL をどう使うか

これはNostrのWebSocketを使うのではなく、HTTPのPOSTメソッドを使用してアップロードする仕組みとなっています。

認証イベントは HTTP Header の Authorization: Nostr <base64エンコードされたイベント> として表現し、ファイル本体は HTTP の Body で渡すというものです。

レスポンスは成功したかどうかとURLが返ってくるという単純なものです。

サーバーは BUD-01 準拠で管理

Blossomに対応しているサーバー、例えば nostr.build などに NIP-96 でアップロードするとレスポンスのURLが BUD-01 に対応したものとなるようです。BUD-02でHTTP PUTしなくてもとりあえずよさそうです。

例: https://image.nostr.build/<hash値>.m4a

またレスポンスには追加情報がいろいろとあるので必要に応じて使うこともできます。

実際にアップロードしてみる

以下の sign.sh というbashスクリプトを用意し、署名を行います。必要なコマンドはjqとnak。スクリプトの最後では別のスクリプト post.sh にファイルと署名とURLを渡す作りにしています。
あとnsechex.txtというファイルに秘密鍵をHEX形式で書いておく必要があります。

#!/usr/bin/env bash
FILE_PATH="$1"
if [ -z "$FILE_PATH" ]; then
  echo no file
  exit 1
fi

URL="https://nostr.build/api/v2/nip96/upload"

NOSTR_SECRET_KEY=$(cat nsechex.txt)

export SIGNED_EVENT="$(
  nak event \
    -k 27235 \
    --tag u=$URL \
    --tag method=POST \
    --sec $NOSTR_SECRET_KEY \
    -c '' \
)"

NOSTR_AUTH=$(echo "$SIGNED_EVENT" \
  | jq -c . \
  | base64 -w0 | tr '+/' '-_' | tr -d '=')

./post.sh $FILE_PATH $NOSTR_AUTH $URL > result.json
jq < result.json

post.sh は以下となります。

#!/usr/bin/env bash
FILE_PATH="$1"
NOSTR_AUTH="$2"
URL="$3"
if [ -z "$URL" ]; then
  echo no URL
  exit 1
fi

curl -sS \
  -H "Authorization: Nostr ${NOSTR_AUTH}" \
  -F "file=@${FILE_PATH}" \
  $URL

./sign.sh foo.m4aみたいな感じでアップロードできます。成功したらURLやその他のメタ情報がjqで整形されて表示されます。自分の場合はURLだけコピペしてNostrクライアントに貼り付けたりしています。

最後に

Blossomの説明がほとんどなくてすいません。これは私がまだあまり理解できていないところによるものです。ただ、Blossomに準拠しておけば冗長化などにも対応するための土台になりますのでとてもよいと思います。IPFSのような複雑なものではないため、サーバーを建てるのも比較的容易ではないかと思います。

追記 2025/12/13 14:23

  1. タイトル変えました。
  2. 最新のnakコマンドでBlossomに対応しているとmattnさんから教えていただきました。

nakでBlossom

アップロード

nak blossom --server https://blossom.band --sec $(cat nsechex.txt) upload a.m4a

リスト

nak blossom --server https://blossom.band --sec $(cat nsechex.txt) list

削除

nak blossom --server https://blossom.band --sec $(cat nsechex.txt) del sha256文字列

nostr.bandではなくblossom.bandにする必要があるのでご注意を。
あと、blossom.yakihonne.com も対応しているとカステラゴリラさんから教えていただきました。ありがとうございます。

Discussion