接続する
アプリは、バックエンドが署名したトークンか、リンクで Relay に接続します。接続は、プロジェクトのあるパスをルートにするセッションです。どこをルートにし、そこから何を出して何を読めるかは、トークンかリンクが決めます。
Relay の URL と転送方式
Relay は一つの URL で、WebTransport、生の QUIC、WebSocket(QMux)の上に MoQ を出し、どれでも同じ規則です。URL のパスは使いません。ルートはトークンかリンクから決まります。
| どこから | 転送方式 |
|---|---|
| ブラウザー | WebTransport。公式の MoQ のクライアントと同じく WebSocket に切り替わる: WebKit のブラウザー(Safari と iOS のすべてのブラウザー)は常に WebSocket、ほかのエンジンも WebTransport がないか 500 ms の競争に負けると WebSocket |
| Node | WebTransport |
| Rust | https:// の URL なら WebTransport、moqt:// か moql:// なら生の QUIC |
connection.transport は、使っているセッションの転送方式を webtransport、websocket、quic のどれかで示します。
接続の仕方
connect(options) はすぐに接続を返し、そのときの status は connecting です。Relay の URL と、次のどれか一つを渡します。
| 項目 | 意味 |
|---|---|
token |
トークン |
getToken |
トークンを返す関数。接続を試すたびに呼ばれる |
link, secret |
リンクの ID とシークレット。ブラウザーでは secret を省ける: SDK がブラウザーごとに一度だけ作り、localStorage(tablebox.secret)に保つ |
serverCertificateHashes |
認証局が署名していない Relay の証明書(ローカルスタックのものなど)の SHA-256(16 進) |
トークン
トークンは moq-auth 0.2.1 の形式の JWT で、プロジェクトのキーのシークレットで HS256、HS384、HS512 のどれかにより署名し、キーの ID をヘッダの kid にします。持つクレームは次のものだけです。
| クレーム | 型 | 意味 |
|---|---|---|
root |
パス。既定は空 | セッションのルート。プロジェクトのルートからの相対 |
publish, subscribe |
パターンの一覧。root からの相対 |
セッションが出せるもの、読めるもの。全体で少なくとも一つ |
sub |
文字列。必須 | 参加者の ID。取り外しはこれで指す |
iat |
秒。必須。Relay の時計より 60 秒先まで | 発行した時刻。取り外しと閉鎖はこれと比べる |
exp |
秒。必須 | セッションを受け入れるときに確かめる |
nbf |
秒。任意 | セッションを受け入れるときに確かめる |
{ "sub": "alice", "root": "rooms/42", "publish": ["alice/**"], "subscribe": ["**"], "iat": 1790000000, "exp": 1790000600 }このトークンは alice を rooms/42 で接続させます。alice は alice/ の下に出せて、rooms/42 の下をすべて読めます。alice/camera.hang のような alice のパスは、プロジェクトでは rooms/42/alice/camera.hang です。参加者が出すときの ID は発行する側が publish に書いたもので、それを保証するのは書く権利です。同じ sub の二つのセッションは、どちらも残ります。
パターンは moq-pattern に従います。/ で区切った段で、各段はそのままの文字列、*(段全体の一つ)、prefix*suffix(一つの段)、**(何段でも、一つのパターンに一度まで)のどれかです。alice/** は alice とその下のすべてに、** はすべてのパスに一致します。誤ったパターンは拒否されます。
セッションには、subscribe のパターンが覆うすべてのブロードキャストが知らされ、読んだトラックが届きます。読むだけの人はほかの誰にも何も増やさないので、出す人が少なく読む人がどれだけ多い範囲でも、各読み手の負担は変わりません。多くの人が出す範囲では、各人の subscribe を ["stage/**", ".aggregation/board"] のようにその人が読むものに絞り、誰がいるかは Aggregation の書き手ごとの一覧に置きます。そうすれば、出す人が何人でも、各人が受け取るものは変わりません。
リンク
リンクは、Console か管理 API で、ルートと publish と subscribe のパターンを付けて作ります。パターンの段全体が {id} なら、入る人の ID を表します。ID を持つ人は、A–Z a–z 0–9 - _ からなる 32〜128 文字のシークレットで接続し、次のように受け入れられます。
sub: 入る人の ID。シークレットの SHA-256 をパディングなしの base64url にした先頭 22 文字で、connection.identityで分かります。一つのシークレットからは常に同じ ID になり、ID からシークレットは分かりません。- リンクのルートと、
{id}をその ID に置き換えたパターン。 - 取り外しと閉鎖のための発行時刻として、リンクを作った時刻。
リンクは削除されるまで受け入れます。削除すると、そのリンクのセッションは invalid-token で閉じます。
状態と再接続
status は connecting(再接続中も)、connected、closed のどれかです。reason は、直前のセッションがコード付きで終わったときの理由で、closed になった後は閉じた理由です。どちらもシグナルで、peek() で読み、subscribe() で追います。
- ネットワークが切れたとき、Relay の GOAWAY、
unavailableの後は、1 秒から 30 秒まで倍にしながら待って(各待ちを最大半分までランダムに短くし、5 秒続いたセッションの後は 1 秒に戻す)再接続し、アプリが出しているブロードキャストをすべて出し直します。 expiredの後は、getTokenがあれば再接続します。invalid-token、removed、closed、limitの後は閉じたままです。
exp はセッションを受け入れるときに確かめるので、セッションはそれを過ぎても続きます。次の試みには getToken が新しいトークンを渡します。Relay はセッションが開いた後で判断するので、拒否された接続も一瞬 connected を示してから、コード付きで閉じることがあります。止まる Relay は各セッションに GOAWAY を送り、クライアントは別の Relay へ移ります。黙ったクライアントのセッションは 10 秒で終わり、そのブロードキャストも一緒に終わります。
コード
| コード | 名前 | いつ | SDK がすること |
|---|---|---|---|
| 64 | invalid-token |
トークンかリンクが誤っている・知られていない・署名が正しくない、ほかのクレームを持つ、sub・iat・exp がない、何も許さない。またはそのキーかリンクが削除された |
止まる |
| 65 | expired |
exp を過ぎた、または nbf がまだ来ていない |
getToken のトークンで再接続する |
| 66 | removed |
取り外しがセッションに当たる | 止まる |
| 67 | closed |
閉鎖がセッションに当たる、またはプロジェクトが削除された | 止まる |
| 68 | limit |
プロジェクトが stopped、または新しいセッションに対して full |
止まる |
| 69 | unavailable |
この Relay がまだプロジェクトの状態を読んでいない(セッションは最大 2 秒待つ) | やり直す |
Reason は code と name を持つ Error で、Reason.of(error) はコードの付いたセッションの終わりやトラックのエラーに名前を付けます。取り外し、閉鎖、上限はプロジェクトの管理に、プログラムなどパスを受け持つ側のコードはパスを受け持つにあります。