Notariumドキュメント
ドキュメントのバージョン: latest

本番環境

このページは、インスタンスを本番環境へ出す話です。リバースプロキシの背後に正しく置く方法、シングルインスタンス不変条件が意味するもの、そしてバックアップの取り方を扱います。Notarium は単一のコンテナとしてデプロイされるので、本番の設定は以下の三点に尽きます。

リバースプロキシと転送ヘッダー

アプリの前段にはリバースプロキシ(nginx、Caddy、Traefik)を置きます。TLS を終端し、Notarium のポートへ中継する役目です。肝心なのは、外部アドレスを伝えるヘッダーをプロキシが必ず転送することです。

最も多いデプロイのミス

リバースプロキシは X-Forwarded-Host を送る(または Host を書き換えずにそのまま通す)必要があり、あわせて X-Forwarded-Proto: https も送らなければなりません。さもないと、画面からの cookie 認証による変更操作が cross-origin として拒否され、「見えているのに保存できない」という症状になります。X-Forwarded-Proto の転送は、セッション cookie に Secure フラグを付けるためにも必要です。

理由はこうです。変更操作に対する Origin チェックは、リクエストの送信元をブラウザが見ているアドレスと照合します。そのアドレスは転送ヘッダーで届きます。Bearer PAT によるエージェントの呼び出しはこのチェックの対象外です(cookie を持たない以上、CSRF の攻撃面がありません)。一方でプロキシ側は、クライアントから来た転送ヘッダーをそのまま素通しせず、自分の値で上書きしなければなりません。

エージェント向けの OAuth 認可を有効にするなら、プロキシの背後では PUBLIC_BASE_URL(例: https://notes.example.com)を設定してください。OAuth メタデータのための、変わらない外部アドレスになります。設定しない場合、アドレスは転送ヘッダーから導出されます。設定を参照してください。

クライアント IP をどこまで信じるか

アドレスとは別の軸が、クライアントの本当の IP です。これを基準に 2 つの制限が数えられます。ログインの試行と、新しい OAuth クライアントの受け入れです。プロキシの背後ではすべてのリクエストが同じアドレスから届くため、明示的に設定しなければ、これらの制限は「プロキシ単位」、つまり全員まとめて数えられてしまいます。

ここを制御するのが TRUST_PROXY です。直接つながっているプロキシの IP / CIDR をカンマ区切りで並べます。

# .env — 自分のプロキシコンテナやホストのアドレスに置き換えてください
TRUST_PROXY=172.18.0.0/16

安全なデフォルトは、この変数を設定しないままにすることです。そうすれば X-Forwarded-For は制限に一切影響せず、ヘッダーで他人の IP を騙ることもできません。自分のプロキシのアドレスが確実に分かっているときだけ設定し、リストは狭いままに保ってください。

ここに「全員」を書かないでください

真偽値、ホップ数、名前付きレンジ、そして全アドレスを含むレンジ(/0)は起動時に拒否されます。すべてのアドレスを信頼するということは、どのクライアントもヘッダーで自分の IP を勝手に名乗り、ログインの制限をすり抜けられるということです。

この設定は X-Forwarded-HostX-Forwarded-Proto の転送には影響しません。両者は独立した軸であり、前節の取り決めはそのまま有効です。

シングルインスタンス不変条件

Notarium は単一プロセスを前提に作られています。認証に関わる 2 つの状態がプロセスのメモリ上に置かれています。

  • ログインのレート制限 — 試行回数のカウンター。
  • SSE ソケットレジストリ — アクセスの取り消しが稼働中の接続を即座に切断するための仕組み。

共有ストレージを持たない複数インスタンスをロードバランサーの背後に置くと、これらの仕組みは破綻します。攻撃者は制限をインスタンスの数だけ稼げますし、あるインスタンスでアクセスを取り消しても、別のインスタンスで開いたままの SSE 接続は閉じられません。

ロードバランサーの背後に複数インスタンス

どちらの状態もプロセスのメモリ上にあるため、共有ストレージなしで複数インスタンスをロードバランサーの背後に置くと、これらの仕組みは機能しません。メタデータデータベースを Postgres へ移すと状態を共有できますが、それだけでは水平スケーリングには足りません。インスタンスは一つに保ってください。

バックアップ

正規のバックアップは、外からファイルをコピーすることではなく、イメージに組み込まれたコマンドです。notarium backup は検証済みの ZIP を組み立て、サービスを動かしたまま標準出力へストリーム出力します。

docker compose exec -T notarium backup > notarium-$(date -u +%Y%m%dT%H%M%SZ).zip
docker compose exec -T notarium backup verify < notarium-20260731.zip

検証は一度きりの儀式ではなく、定期ジョブに必ず含めるものです。何も書き換えませんし、アーカイブが本当に必要になる日より前に破損を見つけてくれます。ジョブそのものには、単純なリダイレクトだけでは足りません。安全な公開の手順(一時ファイル → ディスクへのフラッシュ → アトミックなハードリンク)を、ランブックのバックアップとリストアからそのまま持ってきてください。同じページで、まっさらなボリュームへのリストアと、このコマンドが適用できなくなる境界も扱っています。

稼働中の meta.db をコピーしないでください

cp /data/meta.db や、サービスを動かしたままボリュームをコピーする行為はバックアップではありません。メタデータデータベースは WAL モードで動くため、コミット済みの行がまだ meta.db-wal に残っていることがありますし、1 つずつコピーしたファイルの寄せ集めは同一時点のスナップショットにはなりません。

バックアップに何が入り、それはなぜかは次のとおりです。

対象役割
/data/spacesあなたの Markdown ファイル — 信頼できる唯一の情報源。
/data/meta.dbメタデータデータベース(履歴、ユーザー、アクセス権) — ファイルからは復元不可能
/data/jobsインポート / エクスポートジョブの成果物とアップロード。
/data/engineエンジンの派生インデックス。アーカイブには含まれません: ファイルから再構築されるためです。

メタデータデータベースを Postgres へ移していたり、ノートがデータルートの外に置かれている場合、組み込みコマンドは中途半端なアーカイブを作らずエラーで終了します。そのときはデータベースとマウント済みディレクトリを、各プロバイダーの標準的な手段でバックアップしてください。メタデータデータベースが正確に何を保持するかは、データベースのページを参照してください。