バージョン0.24.0
本文書は以下の条項のもと、パブリックドメインに置かれます。
Creative Commons CC0 1.0 Universal Public Domain Dedication
本文書ではファイル転送のためのGeminiプロトコルが規定されます。HTTP [RFC7230] の削ぎ落としというより、Gopher [RFC1436] の累積的な改善と見ることができます。TCP [STD7] ポート1965で走り、TLS [RFC8446] が提供する暗号化を伴う、単純な要求と応答のトランザクションです。所定のMIME種別 [RFC2045] 付きで任意のデジタル内容を送れますが、最もよく使われている軽量ハイパーテキスト文書があり、関連して別途規定する形式が使われます。
Geminiハイパーテキスト形式(別名「gemtext」)の仕様も参照
本文書中のキーワード「しなければならない (MUST)」「してはならない (MUST NOT)」「求められる (REQUIRED)」「することになっている (SHALL)」「しないことになっている (SHALL NOT)」「すべき (SHOULD)」「すべきでない (SHOULD NOT)」「勧める (RECOMMENDED)」「してもよい (MAY)」「省ける (OPTIONAL)」は [BCP14] 中に記載されている通りに解釈されます。
Geminiの第一の目標は、実装が容易(1日2日掛ければ、高々数百行でサーバーやクライアントができる)でありつつも、有用で単純なプロトコルを提供することです。
Geminiで送り届ける際、TCP上の、既定ではポート番号1965上で(最初の有人のGeminiミッションであるGemini 3は1965年3月に飛行)、暗号化されたトランザクションを提供するためにTLSを使います。サーバーとクライアントはTLS 1.2以上に対応しなければなりません。使用されるTLS証明書の種類(CA基盤ないし自己署名)はまだ議論中のため「ベストプラクティス」文書に規定されます。既定のポート番号1965はホストシステムでの非特権ポートであり、サービスを走らせる上で管理者アカウントを使う必要がありません。
GeminiのアドレスはURI [STD66] に基づきますが、以下の変更点があります。
1. 使われるスキームは「gemini」です
2. URIの利用者情報の部分は使ってはなりません
3. 空パス構成要素と「/」のパス構成要素は等価で、サーバーは両方をリダイレクトに送ることなく対応しなければなりません。
4. 指定しなければ、ポート番号は既定で1965です。
5. 認可節でのIPアドレスは使うべきではありません。
本文書が押さえる範囲は、クライアントとサーバーがしなければいけないことを負託するプロトコルのみです。しかし、中核プロトコルの外側の仕様にあり、ここでは触れないGeminiの別の側面もあります。クライアントとサーバー両方の実装者は、Geminiプロトコルのベストプラクティスの手引きに従うことをお勧めします。
クライアントはサーバーに接続し、要求を送ります。要求は、絶対URIにCR(文字13)とLF(文字10)が続いたものからなります。拡張BNF [STD68] で表すと以下です。
request = absolute-URI CRLF ; absolute-URIは [STD66] より ; CRLFは [STD68] より
要求するとき、URIは1024バイトを超えてはなりません。またサーバーでは、URIがこの制限を超える要求を拒絶しなければなりません。サーバーは利用者情報付きの要求を拒絶しなければなりません。クライアントでは、要求の一部としてフラグメントを送ってはなりません。サーバーはこの要求も拒絶しなければなりません。クライアントが空パスで要求するとき、クライアントは要求の末尾に「/」を加えるべきです。しかしサーバーでは、空のパスを扱えなければなりません。
要求があったとき、サーバーは、状態と、要求が成功した場合はクライアントが要求した内容も、送り返すことになります。状態は2桁の応答コードがあり、(送る応答により)CRとLFに続けて情報を加えられます。拡張BNFでは以下の通りです。
reply = input / success / redirect / tempfail / permfail / auth input = "1" DIGIT SP prompt CRLF success = "2" DIGIT SP mimetype CRLF body redirect = "3" DIGIT SP URI-reference CRLF ; 補足:[STD66] は "" を有効なURI参照として許しています。 ; リダイレクトの場合には有効とされません。 tempfail = "4" DIGIT [SP errormsg] CRLF permfail = "5" DIGIT [SP errormsg] CRLF auth = "6" DIGIT [SP errormsg] CRLF prompt = 1*(SP / VCHAR) mimetype = type "/" subtype *(";" parameter) errormsg = 1*(SP / VCHAR) body = *OCTET VCHAR =/ UTF8-2v / UTF8-3 / UTF8-4 UTF8-2v = %xC2 %xA0-BF UTF8-tail ; C1制御集合はありません / %xC3-DF UTF8-tail ; URI-referenceは[STD66]より ; ; type は[RFC2045]より ; subtype は[RFC2045]より ; parameter は[RFC2045]より ; ; CRLF は[STD68]より ; DIGIT は[STD68]より ; SP は[STD68]より ; VCHAR は[STD68]より ; OCTET は[STD68]より ; WSP は[STD68]より ; ; UTF8-3 は[STD63]より ; UTF8-4 は[STD63]より ; UTF8-tail は[STD63]より
[STD68]のVCHAR規則は、ユニコードの非制御コードポイントを含めるべく拡張されます(またUTF-8 [STD63] で符号化されます)。本文の種類はここでは指定しません。送り届ける内容のMIME種別によるためです。完了の応答(内容を含められます)を送るにあたって、サーバーは接続を閉じます。このときTLSのclose_notifyの仕組みを使って、これ以上データは送られないことを、クライアントに伝えなければなりません。
状態値は10から69の範囲にあり、両端を含みます。ただし現在、全ての値が定義されてはいません。クライアントが1桁目を使って応答を扱ってもよいよう、グループ化されています。ただし2桁目は状態を更に明らかにするためにあり、クライアントでは何をすべきか決める際に、追加の桁を使うことをお勧めします。サーバーでは、定義されていない状態コードを送ってはなりません。
6グループの状態コードがあります。
クライアントでは、「10」より小さいか「69」より大きい状態コードを拒絶し、利用者に警告しなければなりません。クライアントでは、「10」から「69」の間の未定義状態コードを、1ビット目の既定の動作として扱うべきです。そのため、状態「14」ではあたかもクライアントで「10」を受け取ったかのように、状態「22」ではクライアントであたかも「20」を受け取ったかのように、それぞれ動作するべきです。
サーバーでは、クライアントの利用者の入力を待ちます。状態コードの後に送られる追加情報はテキストです。クライアントは利用者に情報を求めるべく、プロンプトを出すときに使わなければなりません。また入力情報は、問い合わせ部分として同じURIに送り返します。空白は「%20」に符号化しなければなりません。クライアントでは複数行からなる入力項目を受けても構いません。またその場合、改行は「%0A」として符号化するべきです。サーバーでは、「%0A」と「%0D%0A」の両方を改行として認識すべきです。現在この分類には2つの状態コードが定義されています。
input = "1" DIGIT SP prompt CRLF prompt = 1*(SP / VCHAR)
クライアントで既に問い合わせ文字列を含むURIで1xの応答を受け取っているとき、クライアントでは問い合わせ文字列を利用者の入力で置き換えなければなりません。例えば与えられたURIの結果が次のように応答10であったとします。
gemini://example.net/search?hello
クライアントからは次の要求を送ります。
gemini://example.net/search?the%20user%20input
基本の入力状態コードです。クライアントでは、利用者に入力のプロンプトを出さなければなりません。入力は [STD66] によりURI符号化して、この応答を生成した同じURIに、問い合わせを送るべきです。
状態コード10に従いますが、パスワードのような機密性のある入力に使います。クライアントは状態コード10に従って質問を表示すべきです。ただし利用者の入力は「肩越しに覗いて」読み取られることを防ぐため、画面に表示すべきではありません。
要求が扱われ、サーバーからクライアントに内容を送りました。追加情報は内容のMIME種別で、 [RFC2045] により指定されます。クライアントでは、理解できないMIME引数は、単に無視して扱わなければなりません。
応答本文は単なる生の内容であり、gopher [RFC1436] よろしく、テキストないしバイナリです。圧縮や断片化、内容や転送の符号化といった類の対応はありません。サーバーでは最終バイトの後に接続を閉じ、「応答末尾」信号はありません。
インターネットメディア種別は正統な形式で登録されたものです。Geminiを介して転送される内容は、転送に先立って適切且つ正統な形式で表現されなければなりません。ただし次の段落で定義されるように、「text」種別は例外です。
正統な形式において、「text」のメディア副種別では、テキスト行の改行としてCRLFが使われます。Geminiはこの要件を緩め、LF単独のテキストメディアの転送を許します(ただしCR単独は許しません)。ただし、応答本文全体で一貫性を持って改行されている場合に限ります。Geminiのクライアントでは、Geminiを介して受け取ったテキストメディア中のCRLFとLFを、改行の表現として受け付けなければなりません。
クライアントは、文字集合がUTF-8のtext/geminiと、(構造的にUTF-8の部分集合である)US-ASCII [STD80] ないしUTF-8の文字集合のtext/plainのMIME種別に対応しなければなりません。クライアントは他の文字集合のtext/plainに対応しても構いません。指定された文字集合がなく、text/*のMIME種別が示されているとき、クライアントでは文字集合がUTF-8と仮定すべきです。ディスクに保存されるものであったり、他のプログラムに渡されるものであったりしても、クライアントは他のMIME種別も扱うべきです。
text/geminiの仕様は別途定められます。
この分類に定義された状態は20だけです。
success = "2" DIGIT SP mimetype CRLF body mimetype = type "/" subtype *(";" parameter) body = *OCTET
サーバーでは要求が正常に解析して理解され、与えられたMIME種別の内容を送り届けます。
サーバーからクライアントに、内容のある新しい場所を送ります。追加情報は絶対または相対のURIです。問い合わせ文字列つきの要求に対して、サーバーから応答でリダイレクトが送られたとき、クライアントでは問い合わせ文字列を新しい場所に適用してはなりません。問い合わせ文字列が新しい場所で重要であれば、サーバーでは問い合わせをリダイレクトの一部として含めても構いません。サーバーでは、リダイレクトにフラグメントを含めるべきではありません。しかしこれが与えられているとき、かつクライアントに(元のURIから)適用できるフラグメントがあるとき、どちらのフラグメントを適用するかはクライアントに委ねられます。クライアントでは、従うリダイレクトの数を5回に制限しなければなりません。この分類には2つの定義されたコードがあります。
redirect = "3" DIGIT SP URI-reference CRLF ; 補足:RFC-3987/3987では "" を有効なURI参照にできます。 ; しかしリダイレクトの場合に有効な意味はありません。
基本のリダイレクトコード。リダイレクトは一時的で、クライアントでは元のURIで内容を要求し続けるべきです。
内容の位置は永続的に新しい場所に移りました。クライアントでは以後、新しい場所を使って与えられた内容を取得すべきです。
要求が失敗しました。応答本文はありません。失敗の性質は一時的なものです。つまり将来的に同一の要求は成功する可能性があります。伝文で失敗の追加情報を提供して構いませんが、省けます。もしあるなら、クライアントでは人間の利用者に表示すべきです。この分類の下には5つの状態コードがあります。
tempfail = "4" DIGIT [SP errormsg] CRLF errormsg = 1*(SP / VCHAR)
サーバーに不特定の条件が存在し、クライアントに送り届けかねています。しかし、クライアントは改めて内容の獲得を試みることができます。
オーバーロードやサーバーの保守作業のため利用できません(HTTP 503と関連)。
CGIやそれに似た動的な内容を生成するシステムの処理が、予期せず異常終了したり、時間切れになったりしました。
サーバーで正常にリモートホストとのトランザクションを完了できなかったため、プロキシの要求が失敗しました(HTTP 502、504と関連)。
サーバーから、クライアントに要求を失速させるよう要求されています。指数的バックオフを使うべきです。要求間の遅延させる長さは、この状態が返されなくなるまで二倍されていきます。
要求が失敗しました。応答本文はありません。失敗の性質は永久であって、この内容への更なる要求には同じ状態が返され、クライアントは同じ要求をするべきではありません。伝文で失敗についての追加情報を提供しても良いですが、省けます。情報があるときは、クライアントでは利用者に表示すべきです。この分類には5つの状態コードがあります。
permfail = "5" DIGIT [SP errormsg] CRLF errormsg = 1*(SP / VCHAR)
汎用の永続的な失敗コードです。
要求された資源は見つからず(エリア51では何も見つかりません)これ以上の情報はありません。将来は存在するかもしれませんし、そうでないかもしれません。誰に分かるものですか。
要求された資源は最早入手できず、再び手に入ることもありません。検索エンジンや類するツールでは、この資源を索引から削除すべきです。内容収集器では資源の要求を止め、人間の利用者に購読していた資源が散逸したことを伝えるべきです(HTTP 410が関連)。
ドメインの資源への要求がサーバーから送られず、サーバーでプロキシの要求が受け付けられません。
サーバーでクライアントの要求を解析できませんでした。不正な要求か、「要求」節に挙げられた制限に違反する要求と見られます。
要求された資源にアクセスするには、クライアント証明書が必要です。必要となる理由の例として、アクセス制御やアプリケーションで、サーバー側の状態の維持に活用することがあります。証明書なく要求された場合、やり取りは1度限りにすべきです。要求が証明書付きでされた場合、サーバーでは受け付けず、別の証明書で要求し直すべきです。追加情報には、証明書が必要な理由や、拒絶した理由についての詳細を含められます。サーバーはそうした情報を含めるべきであり、クライアントでは利用者に表示すべきです。この分類には3つの状態コードが定義されています。
auth = "6" DIGIT [SP errormsg] CRLF errormsg = 1*(SP / VCHAR)
内容を得るには、クライアント証明書が必要です。内容を利用するためにクライアントから証明書を提供しなければなりません。証明書なくそれ以上要求し直すべきではありません。この状態コードで応答するときに生成された証明書のスコープには、一定の制限を掛けるべきです。この状態コードを受けたホストとポート番号、元の要求のURLのパス、その下にある全てのパスが、そのスコープです。サーバーでは、ホストとポート番号の異なるパスに、違う証明書を必要としても構いません。サーバーでは、与えられたパスに沿う任意の内容に対して、同じ証明書が使えるようにすべきです。以下に例を示します。
gemini://example.com/private/ -- 証明書Aを要求 gemini://example.com/private/r1 -- 証明書Aを要求 gemini://example.com/private/r2/r3 -- 証明書Aを要求 gemini://example.com/other/ -- 証明書Bを要求 gemini://example.com/other/r1 -- 証明書Bを要求 gemini://example.com/other/r2/r3 -- 証明書Bを要求 gemini://example.com/random -- 証明書は不要
クライアントでは、クライアント証明書を自動生成してはなりません。利用者の主体的な関与なく、その証明書を使って要求を繰り返してはなりません。クライアントでは、利用者に指示されない限り、異なるホスト、異なるポート番号、元の要求のURLのパスより上の同じホストとポート番号のパスに対する要求で、この状態コードへの応答で生成されたクライアント証明書を使ってはなりません。
与えられたクライアント証明書には、要求された資源にアクセスする権限がありません。問題が証明書自体になく、他の資源に対しては権限がある可能性があります。
与えられたクライアント証明書は不当であるため、受け付けられませんでした。証明書の内容やそれ自体に問題があることを示しており、要求された資源については考慮されません。最もありそうな原因は、証明書が妥当とされる期間の開始日が未来になっているか、期限日を過ぎているかです。不当な署名やX509標準要件の違反を示している可能性もあります。
執筆時点 (2021) で、既存のTLSライブラリは必ずしもTLS 1.3に対応しているわけではありませんが、多く(全て?)がTLS 1.2に対応しています。そのためTLS 1.2が最小バージョン要件となります。実装者は、TLS 1.2ではプロトコルの暗号化の交渉段階の一部で、サーバー名と(もし使われる場合は)クライアント証明書を平文で送ることにご留意ください。クライアントではTLS 1.2接続が確立されたとき、警告しても構いません。また、クライアント証明書がTLS 1.2を介して転送されたとき、利用者に警告すべきです。
Geminiサーバーでは、接続を閉じるためにTLSのclose_notifyの実装を使わなければなりません。クライアントでは、既定では接続を閉じるべきではありませんが、利用者により設定された制限を超えた内容の場合は構いません。クライアントとサーバーの両方とも、TLSのclose_notifyの仕組みが使われなかったとき(例えば適切にTLS接続を終了させることなくソケットを閉じた、低層のソケットエラー)を扱うべきです。クライアントでは、そうした場合に利用者に通知すべきです。サーバーでは、そうした場合にログに出しても構いません。
クライアントとサーバーの実装は、TLSサーバー名表示 (Server Name Indication; SNI) に対応しなければなりません。クライアントでは、権威節がホスト名であるURLへの要求時に、ホスト名の情報を含めなければなりません。
Geminiプロトコル仕様では、クライアントが受け取ったサーバー証明書を検証する上で使わなければいけない手段を規定しません。Geminiの実装では、オペレーティングシステムやウェブブラウザで提供される、予め信頼された認証局の一覧に依存する、広く使われる手法以外の「代替」検証スキームについて、プロトコルの起源以来、非公式に試みてきました。こうした手法の正式な仕様は、実践を積み重ねていく中で進展していくかもしれません。
クライアントではTrust On First Useを使うことが強く推奨されます。「TOFU」とも呼ばれる、証明書をピン留めする仕組みであり、検証の仕組みの原則として、自己署名証明書を不当として拒絶しません。そのような仕組みにおいては、まずGeminiクライアントがサーバーに接続したとき、何であれ提示された証明書を受け付けます。その証明書の指紋と期限日は永続的なデータベースに保存され、サーバーのホスト名とポート番号に紐付けます。以降そのホスト名とポート番号への接続では毎回、受け取った証明書の指紋が計算され、データベースに保管されているものと比較されます。指紋が合致しないが、以前の証明書の期限日が過ぎていなければ、中間者攻撃の証拠の可能性があると考えられます。クライアントでは、この基本的な仕組みを、追加のセキュリティ対策で拡張しても構いません。例えば、複数のネットワークの視点で受け取った証明書を比較したり、DNS Based Authentication of Named Entities (DANE) [RFC6698] を使ったりするなどです。
資源を要求したいGeminiクライアントは通常、資源のあるURL gemini://の、権威部分で示されるサーバーに、適切な要求を送ることが期待されます。しかしクライアントでは、まず中継サーバーに送るよう構成しても構いません。そしてそのサーバーから元々送ることになっていたサーバーに接続し、応答を中継するのです。そうした中継器(以下「プロキシ」)もまた、他のプロキシを使っても構いません。こうして、ユーザーエージェントと元のサーバーの間に、プロキシの連鎖が形成されます。
Geminiプロキシで提供される補助機能には以下がありまれますが、この限りではありません。
Geminiプロキシについて、プロトコル水準に特別なことはありません。クライアントがそれらに接続し要求を送ると、上で詳述した通り、「普通の」Geminiサーバーに接続するのと全く同じ方法で応答を解釈します。もちろんGeminiプロキシもまたGeminiサーバーです(ただし全てのサーバーがプロキシとは限りません)。Geminiプロキシの他とは違う特徴は、「所有していない」資源への要求を扱えることです。プロキシではそうした要求だけを扱う必要はありません。同じマシンで走る同じプログラムでも、要求にプロキシとして応答したりしなかったりして構いません。
Geminiプロキシから元のサーバーに接続し、標準的なGeminiの要求と応答のやり取りが完了したとき、かつ元のサーバーから受け取った応答の状態コードがエラーコードであるとき(つまり1桁目が4か5)、プロキシは同じ応答ヘッダをユーザーエージェントに送るべきです。
状態コード43「プロキシエラー」は、Geminiプロキシが遠隔ホストとのやり取りを正常に完了できなかったときに使うことを目的としています。ここでの「正常に」の意味は、応答コード20「成功」を受け取ることではありません。プロキシから状態コード43の応答を返すべき状況には以下がありますが、この限りではありません。
プロキシは、大元のサーバーとのGeminiトランザクションを完了し、クライアント証明書の要求を受けたとき、状態コード43を使っても構いません。ただしこのとき、プロキシには事前にクライアント証明書が構成されておらず、関連する資源に使うための、紐付く私有鍵ががないものとします。ユーザーエージェントではこれらの証明書を生成し、大元のサーバーとの接続で再利用するためにプロキシに渡す、直感的なインバンド管理の方法はありません。
Geminiプロキシはユーザーエージェントと元のサーバーの間の「中間者」でもあり、プライバシーとセキュリティに影響します。Geminiプロキシはユーザーエージェントと元のサーバーの間のエンドツーエンドのTLS暗号が機能しません(このためにプロキシと呼ばれ、トンネルとは呼ばれません)。Geminiプロキシには、応答と要求の両方を好きに変更したり、永久に履歴に控えたりする能力があります。
したがって、Geminiプロキシは信頼された集団を念頭に置いています。期待されているよくある用途は、ユーザーエージェントと同じマシンでローカルで走っているか、同じローカルネットワークにあり、同じ利用者により運用されている違うマシンで走っているプロキシです。サードパーティにより運用されている遠隔Geminiプロキシを使うには、そのサードパーティへの高度な信頼を要します。利用者と遠隔プロキシの運用者の両方がTLS証明書の指紋を注意深く確認すれば、内輪で高度に信頼している小さなコミュニティ内でプロキシの共有を活用できます。
上の考慮事項を平易に言うと、以下となります。
以下の例では、サーバーとクライアントの2つの陣営があります。それぞれの動作は角括弧「[]」で囲まれており、文字コード13と10を示す「CRLF」のような幾つかの終端記号や、引用符で囲まれたテキスト表記があります(ただし引用符は入力に含まれません)。「mime種別」は [RFC2045] によるMIME種別を表し、「内容……」は要求された内容の意味です。
以下の例では、サーバーが利用者の入力を要求し、クライアントで収集し、利用者の入力込みで要求を再提出しています。
Client: [接続を開く] Client: "gemini://example.net/search" CRLF Server: "10 検索する言葉を入力してください" CRLF Server: [接続を閉じる] Client: [利用者にプロンプトが出て入力が得られます] Client: [接続を開く] Client: "gemini://example.net/search?gemini%20search%20engines" CRLF Server: "20 " mime種別 CRLF 内容…… Server: [接続を閉じる]
クライアントから内容を要求します。この例では画像ファイルです。
Client: [接続を開く] Client: "gemini://example.net/image.jpg" CRLF Server: "20 image/jpeg" CRLF <binary data of JPEG image> Server: [接続を閉じる]
この例では、サーバーによりクライアントが資源の新しい場所へリダイレクトされています。
Client: [接続を開く] Client: "gemini://example.net/current" CRLF Server: "30 /new" CRLF Server: [接続を閉じる] Client: [接続を開く] Client: "gemini://example.net/new" CRLF Server: "20 " mime種別 CRLF 内容…… Server: [接続を閉じる]
以下では、サーバーがクライアント証明書を要求し、続く要求でクライアントから証明書を提供しています。
Client: [接続を開く。クライアント証明書は送られていない] Client: "gemini://example.net/protected/" CRLF Server: "60 この資源にアクセスするには証明書が必要です" CRLF Server: [接続を閉じる] Client: [アプリケーションが証明書を得るために何らかの動作をする] Client: [接続を開く。クライアントから証明書を送る] Client: "gemini://example.net/protected/" CRLF Server: "20 " mime種別 CRLF 内容…… Server: [接続を閉じる]
この例では、クライアント証明書と利用者の入力を束ねて、利用者毎のサーバー側の状態を維持し、2つの数字を加算しています。
Client: [接続を開く。クライアント証明書は送られていない] Client: "gemini://example.net/application/" CRLF Server: "60 サーバー側の状態を保つため、証明書が必要です" CRLF Server: [接続を閉じる] Client: [アプリケーションで、証明書を得るために何らかの動作をする] Client: [接続を開く。クライアント証明書が送られる] Client: "gemini://example.net/application/" CRLF Server: "10 0から9000までの数値を入れてください" CRLF Server: [接続を閉じる] Client: [利用者にプロンプトを出し、入力を得る] Client: [接続を開き、クライアント証明書が送られる] Client: "gemini://example.net/application/?42" CRLF Server: [クライアント証明書を認識し、証明書に紐付くメモリないしディスクに値「42」を保管] Server: "10 0と9000の間の別の数値を入れてください" CRLF Server: [接続を閉じる] Client: [利用者のプロンプトを出し、入力を得る] Client: [接続を開く。クライアント証明書が送られる] Client: "gemini://example.net/application/?1923" CRLF Server: [クライアント証明書を認識し、保管された値「42」を取り出し、受け取った値「1923」に加える] Server: "20 text/plain" CRLF "42足す1923は1965です。ごきげんよう" Server: [接続を閉じる]
この例では、エラーを説明する追加テキスト付きでサーバーから一時的な失敗を送ります。
Client: [接続を開く] Client: "gemini://example.net/data" CRLF Server: "41 現在は保守作業中" CRLF Server: [接続を閉じる]
最後の例は、それ以上の説明のない永続的な失敗のものです。
Client: [接続を開く] Client: "gemini://example.net/data" CRLF Server: "50" CRLF Server: [接続を閉じる]