要約

  • IMAPのメッセージシーケンス番号は現在の並び順であり、前のメールがexpungeされれば後続の番号はずれる。UIDはセッションをまたいで残るが、特定のメールボックスとUIDVALIDITY世代の中でしか有効ではない。
  • UIDVALIDITYの変更は、連続性の保証を取り下げる信号である。クライアントは古いキャッシュとUID宛ての操作を破棄し、同じ数値を得た別のメールにユーザーの意思を移してはならない。

正しい番号が、別のメールを指していた

出張前にノートPCが受信箱を同期し、UID 4821を請求書として保存したとする。機内で利用者はそのメールを削除対象にした。その間にサーバー側ではストレージ移行が行われ、本文は移せたものの、旧システムのUID対応表は維持できなかった。新しい受信箱にもUID 4821があるが、それは別のメールである。

再接続後の操作には何も不自然なところがない。本人がログインし、暗号化された接続で、正しいIMAPコマンドを送る。それでも旧UIDVALIDITYを残したままなら、サーバーは失われた連続性を存在するものとして扱い、削除は誤った対象に届く。

IMAPが選んだのは、再同期という目に見える損失だった。サーバーがUIDVALIDITYを更新し、クライアントはそのメールボックスのキャッシュと保留操作を捨てる。古い意思に新しい対象を勝手に与えないためである。

順番は便利だが、身元にはならない

IMAPのシーケンス番号は、選択中のメールボックスにおける相対位置だ。15通なら1から15まであり、範囲取得や件数の計算に向いている。接続中であれば、サーバーから届く変化を使ってクライアントは対応表を直せる。

しかし3番目のメールを完全に削除すると、旧4番は3番になり、後ろはすべて一つずつ詰まる。かつて別のメールが持っていた番号が再利用される。RFC 2060は1996年のIMAP4rev1でこの性質を明記し、現行の基礎仕様RFC 9051も維持している。

オフライン端末は、他の端末やサーバールールが行った追加、フラグ変更、expungeを途中で見ていない。戻った時の「3番」は現在の座標にすぎず、出発前に見た物体の証明ではない。

IMAP4は位置とは別の記憶を作った

1994年12月のRFC 1730は、遠隔のメールボックスをローカルのフォルダーのように扱い、切断後に状態を合わせ直すIMAP4を定義した。そこで各メールに与えられたのが32ビットUIDである。UIDはメールボックス内で単調に増え、欠番を許し、セッションを越えて持続する。

これにより、端末は毎回すべての本文を比較しなくても、既読状態や保留中の操作を以前のメールへ結び付けられた。UIDは切断利用を現実的にする記憶装置だった。

ただし、永続性はプロトコル文書の中だけでは成立しない。IMAP以前から使われていたメールストアにはUIDを保存する場所がないこともある。IMAP外の処理が物理順序を変えることも、削除したメールボックスと同名の箱を後で作ることも、復元時に本文だけ戻して識別メタデータを失うこともある。

すべての実装に不可能な永続性を要求するか、番号の無言の再利用を認めるか。IMAPはどちらも選ばず、永続性が壊れた事実を検出可能にした。

UIDVALIDITYはメールボックスIDではなく世代境界だった

各メールボックスはUIDVALIDITYを返す。過去のUIDを維持できなくなった場合、新しい値は以前より大きくなければならない。作成時刻や単調カウンターは実装例であり、意味の中心は「クライアントが保存したUID群と今のUID群が同じ世代か」にある。

ここには有名な誤解がある。RFC 2683は、UIDVALIDITYがメールボックスを識別する値ではないと強調する。二つのメールボックスが同じUIDVALIDITYを持ってもよい。UIDも箱の中でしか一意ではない。したがってUIDVALIDITYとUIDを連結しただけで、サーバー全体の64ビット識別子にはならない。

長期参照の範囲は、メールボックス名、UIDVALIDITY、UIDの三つで定まる。RFC 3501とRFC 9051は、この組み合わせが同じサーバー上の一つの不変メッセージを指し続けることを要求する。本文、エンベロープ、内部日付、サイズ、構造を別のメールへ差し替えてはならない。通常の操作で変わるフラグは別である。expunge後も、同じ世代でUIDを別のメールに再発行できない。

一意性は数値の性質ではない。どの範囲で、誰が再利用を禁じ、いつ効力が切れるかを含む運用上の約束である。

守れない約束には、見える例外が必要だった

UIDVALIDITYが変われば、多くの端末がキャッシュを作り直す。そのため仕様はUIDの永続化を強く勧める。一方、RFC 4315は、永続UIDを提供できない古いストアのためにUIDNOTSTICKYを定義している。ただし新設ストアで避けるべき状態とも明記する。

これは例外によって原則を曖昧にする設計ではない。永続化できないサーバーには、それを公に伝える出口を用意し、黙って同じ番号を使い回すことを許さない設計である。

RFC 2683は、古いUIDを信じたクライアントが誤ったメールを削除した事例上の危険を挙げる。削除だけでなく、移動、コピー、フラグ付け、取得の対象も変わり得る。オフライン操作を忠実に自動再生するほど、世代の確認は厳格でなければならない。

キャッシュ破棄は、過去の命令権を取り消す処理だった

RFC 4549は2006年、切断型クライアントの同期手順をまとめた。メールボックスを開いたら、返されたUIDVALIDITYをローカル記録と比較する。違っていれば、その箱のキャッシュを空にし、旧UIDを参照する保留操作を削除し、失敗として扱う。

件名、送信日、サイズが似たメールへ操作を付け替えるような救済は規定されない。それらは検索の手掛かりではあっても、以前の削除命令を別の対象へ移す根拠ではないからだ。新世代に入った時、失効するのはデータだけではない。キャッシュに保存されていたユーザー意思の宛先も失効する。

無効化は箱ごとに閉じる。RFC 2683はサーバー全体で一つのUIDカウンターを使う案にも慎重だ。32ビット空間を早く消費し、一度の枯渇が全メールボックスの世代変更に波及するためである。局所的な名前空間は障害範囲も局所化する。

保存されたURLにも古さを判定する材料が要る

1997年のRFC 2192は、IMAP URLにメールボックス、UIDVALIDITY、UIDを含められるようにした。URLを解釈するプログラムは、保存値と現在値を比べ、参照が古くなったか判断すべきだとされた。

UIDVALIDITYはメールの有効期限ではない。送信者を認証せず、アクセス権や暗号学的完全性も証明しない。以前発行された参照が、意味を与えられた識別世代にまだ属するかを知らせる。

長く使える参照は、場所だけでなく「いつ信じるのをやめるか」も持っている。

QRESYNCの高速化も、最初に世代を確認した

大容量メールボックスと不安定なモバイル回線では、再接続のたびに全状態を比べるのは重い。RFC 7162はCONDSTOREとQRESYNCを整備し、変更シーケンスでフラグ更新を追い、VANISHEDで消えたUIDを効率よく通知する。一往復で同期を進められる場合もある。

それでもQRESYNCに渡す最初の状態は、クライアントが最後に知ったUIDVALIDITYだ。一致しなければ、サーバーは後続の変更シーケンスや既知UID集合を無視する。差分が正確でも、別世代の対象についての差分だからである。

問いの順番が設計を守る。まず「同じ対象群か」を確認し、その後で「何が変わったか」を調べる。効率は妥当性の上にしか築けない。

UIDONLYは相対位置を外しても、世代を外さなかった

2024年5月、ExperimentalのRFC 9586がUIDONLYを定義した。有効化後はシーケンス番号をコマンドで使えず、応答にも返さない。UID形式とVANISHEDを使うことで、クライアントとサーバーは変動する位置とUIDの対応表に必要な資源を減らせる。

すべての導入を義務付ける文書ではなく、普及率も示していない。それでも、IMAP4から30年後も相対座標への依存を減らす作業が続いていることは分かる。

UIDONLYでもUIDはメールボックスとUIDVALIDITY世代の内側にある。位置番号を消すことは、識別子を無期限・全域の権利に変えることではない。

識別子は、終わりを宣言できるから長く使えた

IMAPは責任の所在を分けた。ストレージを管理するサーバーは、UIDの連続性が残ったかを知り、UIDVALIDITYで宣言する。キャッシュと遅延操作を持つクライアントは、世代が変わればそれらの効力を取り消す。便利そうだという理由で、どちらも連続性を作り出してはならない。

共通層は薄い。保存形式、移行手法、端末内データベースを一つにしない。その代わり、不変条件、破断の信号、安全な反応を共有する。実装の自由は残るが、旧番号が新しい対象への命令権を無言で得る自由はない。

UIDがセッションを越えられたのは、UIDVALIDITYがメールボックスの再生を越えられなかった瞬間を言えるからである。

出典と限界

1994年の導入はRFC 1730、シーケンス番号・UID・三要素の不変条件はRFC 2060、RFC 3501、RFC 9051に基づく。実装上の教訓はRFC 2683、URLはRFC 2192、UIDNOTSTICKYはRFC 4315、切断同期はRFC 4549、QRESYNCはRFC 7162、ExperimentalのUIDONLYはRFC 9586を参照した。これらは現在の導入率、特定製品の挙動、メールの真正性、暗号学的完全性、UIDVALIDITYの唯一の生成法を示すものではない。