新月プロトコル 0.7 ドラフト#1
2007-01-15
はじめに
新月は複数のノードが協調して動作しており、 そこには何らかのプロトコルが存在すると考えられる。 これを第1次のプロトコルと呼ぶ。 この文書は第1次のプロトコルを明文化して再定義することを試みるものであるが、 残念ながら未だ完全なものではない。
この文書で定義する新月プトロコルのバージョンは0.6である。 プロトコルは次の4つの層から成る。
通信層
新月ネットワークの構成
新月のネットワークはノード間の接続によって構成される。 各々のノードはネットワーク上で一意な名前を持つ。 この一意な名前をノード名という。 ネットワーク上の各々のノードは、 他のノードと通信を行うために他のノード名を保持する。 あるノードがネットワークの維持のために保持している 他のノード名のことを隣接ノードと呼ぶ。 隣接ノード同士は互いに相手のノード名を保持していることが望ましいが、 保証される必要はない。
ノード間の通信はHTTP/1.0またはHTTP/1.1の仕様に従って行う。 ノード間のリクエスト・レスポンスのメッセージは GETメソッドの規約に従わなければならない。
ノードのレスポンスは便宜的に 文字エンコーディングがUTF-8であるプレインテキストとみなす。 厳密にはレスポンスは構造化されており、 各部分について文字エンコーディングが定義されている。
新月のノード間の通信コマンドは最終的にURLとして構築される。 他のノードに送信するコマンドはURL の仕様に従わなければならない。
ノード
新月プロトコルを実装しているプログラム・コンピュータの1単位をノードという。 新月ネットワークに接続するノードは 新月プロトコルを満たすよう正確に実装しなければならない。 プログラムの新月プロトコルの実装にあたっては、 HTTP、URL の仕様も満たさなければならない。
ノードはネットワーク上で一意の名前を持つ。これをノード名といった。 ノード名の形式は次のように定義する:
ホスト名:ポート番号/パス名
ここでホスト名はDNS名またはIPv4のIPアドレスとする。 ポート番号は10進表現の整数である。
新月プロトコルは、ノード名の構成要素であるポート番号を指定しない。 各々のノードはノード名に使用するポート番号を自由に設定することができる。 ノード名に使用するポート番号が1024未満の場合は、 ウェルノウンポート番号に従うべきである。
メッセージ
ノード間でリクエスト・レスポンス時に交換する情報をメッセージという。 メッセージの内容は多岐にわたる。 例えば、/ping コマンドに対するレスポンスである 通信相手のノードのIP アドレスがある。 /getコマンドで交換するファイルもある。
通信相手ノードにリクエストメッセージを送信し、 レスポンスメッセージを受信するノードは、 圧縮されていない通常のメッセージに加えて、 gzip 形式で圧縮しているメッセージも解釈できることが望ましい。 ただし、gzip 形式で圧縮したメッセージをノードに送信する場合、 通信相手ノードはリクエストのメッセージヘッダを解析し、 ノードがgzip形式で圧縮されたメッセージを解釈できるかどうか 判断しなければならない。 もし、判断後ノードがgzip形式で圧縮されたメッセージが解釈可能であれば、 gzip形式で圧縮したメッセージを送信する。 圧縮されているメッセージを解釈できるノードは、 できるだけ通信相手ノードにそれが可能な旨を通知すべきである。 メッセージの圧縮を行うことにより、メッセージを圧縮することができ、 圧縮通信の利点を享受できる。
時刻
新月プロトコルで用いる基点時刻は 「1970年1月1日午前0時(グリニッジ標準時)」である。 ある時刻は基点時刻を基準として表される整数値の秒で表す。
ノード間のプロトコルコマンド
ノード間の通信はプロトコルコマンドを送信することによって行う。 プロトコルコマンドはノード名から生成したURLのパス部の末尾に付加する。 プロトコルコマンドを付加したURLの例を示す:
http://example.com:8000/server.cgi/ping
次に、ノード間で通信を確立するためのプロトコルコマンドと、 それを受けたノードの振舞いを示す。 \nは改行コード(0x0a)を表わす。
- /ping
- ノードは「PONG\n相手ノードのIPアドレス」を返す。
- /node
- ノードはノード自身が接続しているノードを1つ選択し、 そのノード名を返す。
- /join/ノード名
- 相手を隣接ノードに加えるかどうかを判断し、 加えるのであれば「WELCOME」または「WELCOME\n別のノードのノード名」 を返す。 判断には相手ノードが指定するノード名が有効であることを /pingによって確かめること、 相手ノードのノード名のDNS名部分が、 接続しようとしている相手ノード自身を示しているかどうかの検査 などが考えられるが、ここでは定義しない。 相手ノードのノード名のホスト名は省略することができる。 ノード名のパスは/を+ に置き換えたものとする。 相手ノードにはノード保持リストに加えてもらい、 レスポンスで指定したノードに接続することを期待する。
- /bye/ノード名
- 隣接ノードリストから相手ノードを削除し、「BYEBYE」を返す。 判断には相手ノードのノード名のDNS名部分が、 接続しようとしている相手ノード自身を示しているかどうかの検査 などが考えられるが、ここでは定義しない。
- /have/ファイル名
- ファイル名のファイルを持っていれば「YES」、 そうでなければ「NO」を返す。
- /get/ファイル名/時刻引数
- ファイル名で指定したファイルの、時刻引数を満たすレコードを返す。
時刻引数は次のうちどれかひとつである:
- 「時刻」指定した時刻のレコード
- 「-時刻」指定した時刻以前のレコードすべて
- 「時刻-」指定した時刻以降のレコードすべて
- 「時刻-時刻」指定した時刻の間のレコードすべて
- 「時刻/識別子」指定した時刻と識別子のレコード
- /head/ファイル名/時刻引数
- /get と同様のレスポンスである。 ただし、各レコードの時刻と識別子のみを返す。
- /update/ファイル名/時刻/識別子/ノード名
- ノードにファイルが更新されたことを知らせる。 もしすでにファイルの更新に関する処理をしているのなら何もしない。 自分の持っているファイルならば、それを更新したのち、 ノード名を自ノードのノード名に書き換えて接続しているノードにも通知する。 そうでなければそのまま、 送信されてきたプロトコルコマンドを接続しているノードに転送する。 ノード名のパスは/を+に置き換えたものとする。 /joinと同様にホスト名を省略できる。
- /recent/時刻引数
- 時刻引数で指定した範囲の/update命令のあったレコード名を
擬似的なファイルとして返す。
時刻引数は次のうちどれかひとつである:
- 「時刻」指定した時刻のレコード
- 「-時刻」指定した時刻以前のレコードすべて
- 「時刻-」指定した時刻以降のレコードすべて
- 「時刻-時刻」指定した時刻の間のレコードすべて
タイムスタンプ<>識別子<>ファイル名
各行にはタグと呼ばれる文字列を加えることができ、 その場合の書式は次のようになる。タイムスタンプ<>識別子<>ファイル名<>tag:タグ群
タグ群とはタグをスペースで区切ったものである。 タグにはスペース, <, >, & を使うことができない。 - /
- ノード固有のメッセージを表示する。 通信には使わないので、何を出力してもよい。
ファイルの伝播
ノードは、ファイルの内容に変更が生じたときに、 変更された事実を/updateコマンドを使用して他のノードへ通知することができる。 ファイルの変更があったことを通知するかどうかの判断の処理は、 実装によって異なる。
ファイル
1つの掲示板を構成する、0個以上のレコードの集合をファイルと呼ぶ。 それらのレコードを\nで接続した書式を持つ。 ファイルは無限の長さを持っており、 実際には1つのファイルの全体を目にすることはできない。 ノードが扱うのは常にファイルの一部である。
レコード
レコードとはファイルを構成する要素のことである。 レコードの形式は、次のように定義する:
タイムスタンプ<>識別子<>本文
識別子は本文のMD5値である。 レコードの要素は<>で区切る。 タイムスタンプは基準時刻から経過した整数値の秒数を表す。
中間層
ファイル名
ファイル名は prefix_basename という形式である。 prefixに使える文字は半角英数字、 basenameに使える文字は半角英数字とアンダースコア(_)である。
prefixはファイルの種類を表す。 basenameはファイルの「名前」などを表わすことになるだろう。 ファイル名を定義するのはアプリケーションである。
本文の書式
本文を構成する要素は「<>」で区切る。 本文は複数の名前付きフィールドで構成される。 名前付きフィールドの形式は「名前:値」である。 名前付きフィールドの名前に使える文字は半角英数字とアンダースコア(_)である。 名前付きフィールドの名前は重複してはならない。 重複した場合の例外処理は実装依存である。 名前付きフィールドのstamp, id は予約されており、 それぞれタイムスタンプ、識別子を表わす。 本文での名前付きフィールドの出現順番は問わない。 名前付きフィールドのフィールド値には「<」「>」が含まれてはならない。 例外として「<文字列>」(タグと呼ぶ)を含むことができる (文字列は長さが1 文字以上であって、「<」「>」が含まれてはならない)。 どのようなタグが使えるのかはアプリケーションが定める。
署名
署名の定義は新月プロトコル0.5 ドラフト#2 PDFを参照せよ。
署名に関連する名前付きフィールド
- pubkey
- 署名の公開鍵を記す。
- sign
- 署名を記す。
- target
- どのフィールドを署名するか「,」で区切って並べる。
削除通知に関連する名前付きフィールド
- remove_stamp
- 同じファイル中で削除するstamp を指定する。
- remove_id
- 同じファイル中で削除するid を指定する。
通信層を共有し、別の中間層を持った実装を作ることができる。 そのような実装は「新月0.6と通信層が互換」と呼ばれる。
アプリケーション層
プラグインとアプリケーション
プラグインはアプリケーションを含む概念だが、まだ定義できない。 通信、署名、削除通知以外の部分がプラグインということになるだろうか。
プラグインのうち、 ファイルの書式を定義するものをアプリケーションと呼ぶ。 新月プロトコル0.6の定義するアプリケーションはthreadの1種類である。 かつては他に8種類のアプリケーションが存在した。 なおアプリケーションとは実装のことではない。
実装ではthread以外にもアプリケーションを定義することができる。 そのような実装は「新月0.6と中間層以下が互換」と呼ばれる。
threadアプリケーション
いわゆる掲示板のアプリケーションである。
ファイル名
ファイル名は「thread_エンコードされた掲示板タイトル」という形式である。 エンコードされた掲示板タイトルとは 掲示板タイトルをUTF-8で表現し、16進数表現(0-9A-F)に変換した文字列である。
名前付きフィールド
- name
- 投稿者の名前
- body
- 書き込みの本文。 タグは改行を表わす「<br>」のみが使える。 「<BR>」や「<br />」は不可。
- attach
- 添付付ファイルをbase64エンコードしたもの
- suffix
- 添付ファイルの拡張子
- 投稿者のメールアドレス
- name
- 投稿者の名前
アプリケーショングループ
複数のアプリケーションが相互に連携するための規格である。
Wiki式名前空間アプリケーショングループ
現在はthreadのみが定義されているが、 次のような規格に従うアプリケーションを作れば threadと連携ができる。
ファイル名
ファイル名は「アプリケーション名_エンコードされた掲示板タイトル」 という形式である。 エンコードされた掲示板タイトルとは 掲示板タイトルをUTF-8で表現し、16進数表現(0-9A-F)に変換した文字列である。
ブラケットリンク
アプリケーションは、ファイルの内容をなんらかのデータ形式に変換し、 結果を出力する場合において、 本文に次に述べるようなブラケットリンクがあるときは、 文字列が指定するリンクを作成しなければならない。
アプリケーションtypeが存在するとする。 標準的なブラケットリンクの形式は[[/type/文字列]] である。 文字列の形式はアプリケーション(type) が定義する。 /typeは省略することができる。 /typeを省略したブラケットリンクの形式である[[文字列]]は、 本文が記録されているファイル形式のtypeが省略されていると考える。
threadのブラケットリンクの作成例を次に示す。
- [[すれっど]]
- 本文が記録されているファイル形式がthreadなら、 threadの「すれっど」へリンクする。
- [[すれっど/7889e7db]]
- 本文が記録されているファイル形式がthreadなら、 thread「すれっど」の識別子「7889e7db」を持つレコードへリンクする。
- [[/thread/すれっど/7889e7db]]
- thread「すれっど」の識別子「7889e7db」を持つレコードへリンクする。
コメントください: [[新月の開発]]