はじめに
ynotbit は、Bitmessage 用のデスクトップクライアントです。notbit をベースにしていて、UI は Qt 6.8 の Widgets で書いています(QML や Qt Quick は使っていません)。0.7.0 を公開したので、設計の要点をまとめます。
Bitmessage は、中央サーバーを持たない暗号化メッセージングのネットワークです。メッセージはネットワークオブジェクトとして全ノードに配られ、各ノードは自分の鍵で復号できるものだけを自分宛てとして受け取ります。送信側はスパム対策として、プルーフ・オブ・ワーク(PoW)を計算します。
開発中のソフトウェアです。独立したセキュリティ監査や、公開ネットワークでの相互運用性の認証は受けていません。
鍵・メール・リレーを分ける
ynotbit は、保存するものを 3 つに分けています。
| 役割 | 実体 | 中身 |
|---|---|---|
| 保管庫 | .bmvault |
ID の秘密鍵。Argon2id(64 MiB、3 パス)と XChaCha20-Poly1305 で暗号化し、鍵のメモリ領域は保護付きで確保します |
| メールボックス | .bmmail |
下書き、本文、アドレス、公開鍵、配信記録、受領確認のトークン、購読、チェックポイント。SQLCipher で暗号化します |
| リレー | 別プロセス(notbit ベース) | ネットワークオブジェクトを集めて objects.sqlite に保持します。保管庫やメールボックスの鍵は受け取りません |
ポイントは、リレーが鍵を持たないことです。ロックすると保管庫の鍵は解放されますが、リレーはネットワークオブジェクトを集め続けます。ロックを解除すると、保持していたオブジェクトを検査し、自分宛てのメールをメールボックスに保存します。
objects.sqlite はリレーが書き込み、保持期間(既定は 2 GiB / 90 日)に収まるよう刈り込みます。アプリ側は同じファイルを読むので、新しいオブジェクトは次の更新でメールボックスに届きます。保持期間は設定で変更できます。
保管庫を作るダイアログは 1 つで、保管庫と最初の ID を一緒に作ります。鍵はランダム生成か、秘密のフレーズ(PyBitmessage と同じ方法で導出)から作れて、短いアドレスを探すオプションもあります。
送信: PoW は CPU の全コア−1 と GPU で
Bitmessage の送信には PoW が必要です。ynotbit は、取り消し可能な PoW をバックグラウンドで、CPU の全コアから 1 つ引いた数のコアと、GPU(macOS は Metal、それ以外は OpenCL)で実行します。GPU への対応は 0.7.0 からです。使える GPU がないときや、環境変数 YNOTBIT_NO_GPU=1 を指定したときは、CPU だけで計算します。
配信の状態は、キュー待ち → 鍵の取得 → 受領確認の準備 → PoW → ピア待ち → 受領確認待ち → 受領確認済み、と進みます。ブロードキャストとチャンのメッセージには受信者の受領確認がないので、「公開済み」で終わります。受領確認は既読通知ではなく、ピアに提供したことも、すべてのピアや最終的な受信者に届いた証拠にはなりません。
0.7.0 の新機能: Bitmessage 上のチャット
0.7.0 で 1 対 1 のチャットを追加しました。仕組みは単純で、チャットのメッセージは、件名が空で、本文が小さな JSON の、普通の Bitmessage メッセージです。
- 手紙と同じ PoW、公開、受領確認の仕組みをそのまま使います
- 24 時間で削除します。受信トレイには出ず、バックアップにも含めません
- 本文は Markdown です。Enter で送信、Shift+Enter で改行します
- メッセージの下のチェックマークは、待機中、ネットワークへ送信済み、配信済み(受領確認)の順に進みます。既読通知ではありません
- リンクは確認してから開き、
httpsだけ開きます - チャットは「自分の ID と相手 1 人」の組で決まります。同じ相手でも ID が違えば別のチャットです
UI は Qt Widgets だけで作る
メッセージ一覧は自前で描画するリストで、保持するのは最大 3 ページ(1 ページ 100 件)分のメッセージ概要だけです。本文は、選択したメッセージの分だけ読み込みます。
作成画面は、ビジュアルな Markdown エディタです。段落の横のマーク(¶、H1〜H6、リスト、コード)から「Turn into」を開いて、段落の種類を変えられます。MarkText と同様の操作です。返信では、元の手紙を > で引用します。引用部分は編集できますが、引用のままです。
Bitmessage には添付ファイルがないので、画像は Markdown の data: URL として手紙の本文に入れ、1 通に収まるよう縮小します。読む側が読み込める形式は PNG、JPEG、GIF、WebP だけです。メールの表示には、ローカルファイルやリモート画像を取得しない、安全な Markdown ドキュメントを使っています。0.7.0 では、手紙が画像としてファイル名を指定すると、リーダーがディスク上のファイルを表示してしまう問題を修正しました。
UI の言語は、英語、簡体字・繁体字中国語、日本語、韓国語、ロシア語、ウクライナ語です。システムの言語に合わせて切り替わります。
ビルドとテスト
必要なものは、CMake 3.22 以上、C++20、Qt 6.8 以上、OpenSSL、libsodium、SQLCipher、pkg-config、Ninja です。リレーのループバックテストに Python 3 を使います。
scripts/build-dependencies.sh /absolute/scratch /absolute/deps
export PKG_CONFIG_PATH=/absolute/deps/lib/pkgconfig
cmake -S . -B build -G Ninja \
-DCMAKE_PREFIX_PATH=/absolute/Qt/6.8.3/macos \
-DCMAKE_BUILD_TYPE=Release
cmake --build build --parallel 4
ctest --test-dir build --output-on-failure
libsodium と SQLCipher は、scripts/build-dependencies.sh で、固定したバージョンをソースからビルドします。Windows では MSVC と Ninja を使い、これらを vcpkg で入れます。テストは一時的なドキュメントとループバックのピアだけを使い、公開ネットワークのメッセージには触れません。GitHub Actions は、v* タグで Linux、macOS、Windows をビルド、テスト、パッケージして、GitHub Release の下書きに 3 つのアーカイブを添付します。
入手方法
v0.7.0 のリリースページから、Linux (x86_64、AppImage)、macOS (Apple Silicon、macOS 15.6 以降)、Windows (x86_64) 向けの、ビルド済みで CI テスト済みのパッケージを入手できます。
macOS 版はアドホック署名のみで、公証を受けていません。ynotbit.app を /Applications に移動したあと、一度だけ次を実行して、隔離属性を外してください。
sudo xattr -cr /Applications/ynotbit.app
現在の制限
- 開発中のソフトウェアです。ロックすると保護された鍵領域は解放されますが、Qt や OS が持つ表示済みの平文のコピーが、メモリ、スワップ、クラッシュダンプ、スクリーンショットから消える保証はありません
- Windows には、ノードに正常終了を伝える手段がないため、終了時にノードを強制終了します。ストアがキューに入れただけでまだ書き込んでいない作業は失われます
- 手紙に入れる画像以外のファイル添付は未実装です
- 送信をキャンセルしても、すでにリレーされたオブジェクトは取り消せません
ライセンスと参考
ynotbit 自身のコードは MIT ライセンスです。取り込んだコンポーネントは、それぞれのライセンスに従います。
- yshurik/ynotbit(リポジトリ)
- bpeel/notbit(ベースにした C 製の Bitmessage クライアント)
- PyBitmessage(参照実装)
- Bitmessage Protocol specification

