すべての記事

約13分で読めます

CardanoWall API の上に構築する

CardanoWall の上に自分のプロダクトを構築する方法。CardanoWall 自身が稼働しているのと同じゲートウェイ API を通じて、Label 309 レコードの見積もり・アップロード・公開・読み取り・検証・監視を行います。

存在証明(Proof of Existence)の機能は、ユーザーを CardanoWall のウェブサイトに誘導しなくても、ご自分のプロダクトに組み込めます。CardanoWall はホスティングされたゲートウェイの上にあるユーザーインターフェースであり、そのゲートウェイはシンプルな HTTP API を公開しています。証明の見積もり、ストレージが必要なときのコンテンツのアップロード、Label 309 レコードの公開、ステータスの追跡、公開レコードの読み取り、残高の確認、そして webhook の受信が可能です。Cardano 上に記録される証明は標準の Label 309 メタデータであり、CardanoWall 独自の非公開フォーマットではありません。そのため、誰でも、どんなツールを使っても、後から検証できます。

これが重要な構造です。プロダクト体験はすべてご自分のものにできます。証明は独立して検証可能なまま保たれます。これは CardanoWall 自身のウェブアプリとワーカーが稼働しているのと同じ API です。専用の裏口も、内部だけが使える高速経路も存在しません。

この上に何を構築できるか

ワークフローの中で存在証明が必要となる場面なら、どこにでもこの API が当てはまります。

  • 顧客の文書にタイムスタンプを付与する SaaS プロダクト
  • リリースマニフェストをチェーン上に記録する CI/CD パイプライン(CI/CD での証明を参照)
  • 来歴レコードを大規模に公開する AI プラットフォーム
  • 日次のエビデンスバッチをコミットするコンプライアンスアーカイブ
  • 特定の受信者向けにエビデンスを封印する法務ツール
  • 統制スナップショットに署名する内部監査システム
  • 公開者のレコードを一覧する公開エクスプローラーやプロフィールページ

この API は、単にウェブサイトを便利に包んだだけのラッパーではありません。これは自動化のためのインターフェースであり、人が介在しないときに頼る存在です。

主要な公開フローとは

公開には 3 つの段階があります。見積もりアップロード(バイト列をストレージに保存する必要があるときだけ)、そして公開です。ゲートウェイのデータプレーン(/api/v1/*)では、これは次のようにマッピングされます。

  • POST /api/v1/poe/quote — 短い時間枠だけ価格を固定します。
  • POST /api/v1/poe/uploads — コンテンツを保存し、コンテンツアドレス指定の URI を返します。
  • POST /api/v1/poe/uploads/sessions とそのチャンクルート — 大きなファイル向けの再開可能な経路です。
  • POST /api/v1/poe/publish — 確定したレコードを送信します。
  • POST /api/v1/poe/publish-batch — 1 回の呼び出しで多数のレコードを送信します。
  • GET /api/v1/poe/events/{poe_id} — Server-Sent Events でステータス更新をストリーミングします。

正確なリクエストとレスポンスの本体は、すべてのデプロイメントが配信するゲートウェイの OpenAPI ドキュメントに記載されています。ブログのスニペットではなく、そちらに合わせて実装してください。安定して変わらないのは考え方のモデルです。まず価格を固定し、次に必要なコンテンツを保存し、それから確定した正規 CBOR のレコードバイト列を見積もり ID とともに送信します。

なぜ見積もりが先に来るのか

公開は有料の操作であり、その価格は事前には分からないからです。

ゲートウェイはお客様に代わって実際のコストを支払います。Cardano のトランザクション手数料、コンテンツが添付されている場合の Arweave ストレージ、USD とこれらのネットワークとの間の為替変動リスク、そしてオペレーターのマージンです。見積もりは、公開の呼び出しが何かを消費する前に、そのコストを明示します。(有料公開の理由については、公開に価格がある理由を参照してください。)

見積もりのレスポンスには、quote_id、価格の内訳(ネットワーク・ストレージ・サービスの各構成要素)、適用されたマージン、為替レートスナップショットの鮮度、そして有効期限のタイムスタンプが含まれます。価格が固定されるのは限られた時間枠だけで、おおよそ 15 分です。その後は新しい見積もりを要求します。

これにより、アプリケーションは次のことを判断できます。

  • アカウントがその公開を支払えるかどうか
  • 為替レートスナップショットがご自分のポリシーにとって十分に新しいかどうか
  • その操作にユーザーの確認が必要かどうか
  • 大きなバッチを分割すべきかどうか
  • 古い見積もりが失効したときに新しい見積もりで再試行するかどうか

ゲートウェイがその操作に費用がかからないと確認するまでは、「無料」のラベルを表示しないでください。ストレージを伴わないハッシュのみのレコードは安価になり得ますが、価格を決めるのはゲートウェイであって、ご自分の UI ではありません。

アップロードのステップは何をするのか

アップロードは、どこかに保存しておく必要のあるバイト列を扱います。

ハッシュのみの証明は何もアップロードしません。すでにダイジェストを手元に持っているからです。コンテンツが添付されたレコード、封印された暗号文、あるいはリカバリー用の素材には、コンテンツアドレス指定ストレージが必要です。アップロードのエンドポイントはそうしたバイト列を保存し、ar://... のような URI を返します。この API は、マルチパートアップロード、ファイルごとの結果、アカウント内でのコンテンツハッシュによる重複排除、そして 1 回のリクエストで送信するには大きすぎるファイル向けの再開可能なセッションをサポートします。CardanoWall はアップロードサイズに固定の上限を設けていません。コンテンツはバイト単位で課金され、数ギガバイトのアップロードも想定されています。

アップロードは、それ自体が小さなライフサイクルだと捉えてください。

  1. バイト列をアップロードします。
  2. コンテンツアドレス指定の URI を受け取ります。
  3. その URI を使って Label 309 レコードを構築または確定します。
  4. レコードを公開します。

アップロードが進行中の試行 ID を返した場合は、やみくもに再アップロードするのではなく、その試行のステータスエンドポイントをポーリングしてください。レスポンスを取りこぼしたために数ギガバイトのファイルを再送するような失敗を防ぐためにこそ、試行 ID は存在します。

公開は何をするのか、そしてなぜ非同期なのか

公開はレコードを送信し、チェーンのパイプラインを開始します。

公開の呼び出しは、確定した正規 CBOR のレコードバイト列に加えて quote_id を受け取ります。ゲートウェイはそのレコードをメタデータラベル 309 を付けて Cardano 上に記録し、見積もりの金額を引き落とし、トランザクションがまだ送信と確認の途中にあるうちにゲートウェイのレコード ID(poe_... という値)を返します。

この非同期性は設計にとって重要です。呼び出しが返ってきた時点では、オンチェーンのトランザクションハッシュはまだ存在しないかもしれません。保留中の状態を表示し、更新を待ち受けてください。API が報告するステータスは、次の値の間を移っていきます。

  • submitting — トランザクションが構築され、ブロードキャストされています。
  • confirming — チェーン上にあるものの、確認しきい値を下回っています。
  • confirmed — そのしきい値を越え、確定しています。
  • failed — 公開は最終的に失敗し、オンチェーンには記録されません。

GET /api/v1/poe/events/{poe_id} のステータスストリームは、まさにこのために存在します。最終的な失敗が起きると、ゲートウェイ自身が引き落としを取り消します。そのため、ご自分のプロダクトは独自の照合処理を走らせなくても、ユーザーに対して「オンチェーンに記録された分だけしか課金されません」と正直に伝えられます。公開失敗に対するベンダー側の返金経路を作らないでください。二重返金になってしまいます。

API クライアントは何を保存すべきか

ワークフローを再接続できるだけの情報を保存してください。それ以上は不要です。最低限、次のものを保存します。

  • ご自分のユーザー ID またはアカウント ID
  • ゲートウェイの poe_id
  • 公開に使った見積もり ID
  • レコードのダイジェスト、またはレコードバイト列のハッシュ
  • 最終的な Cardano のトランザクションハッシュ(存在し次第)
  • ステータスとタイムスタンプ
  • アップロードの試行 ID
  • コンテンツアドレス指定の URI
  • 後で検証するために必要となるローカルの素材

ご自分のデータベースを証明そのものだと扱わないでください。プロダクトの読み取りモデルだと扱ってください。証明とは、オンチェーンの Label 309 レコードに、それを検証するために必要なコンテンツや鍵を加えたものです。そしてその組み合わせは、ご自分のサーバーから独立して、それ自体で成り立ちます。

レコードはどう読み取り、どう検証するのか

レコード関連のエンドポイントは、公開された匿名の読み取り向けに作られています。

  • GET /api/v1/records — オンチェーンのレコードフィードです。フィルターとページネーションに対応します。
  • GET /api/v1/records/count — そのフィードの件数です。
  • GET /api/v1/records/{tx_hash} — トランザクションハッシュで指定する単一のレコードです。
  • POST /api/v1/records/{tx_hash}/verify — サーバー側の検証レポートです。

一覧と取得のルートは匿名の呼び出し元にも応答します。公開の検証ページやエクスプローラーには認証情報が不要です。関連する場合には bearer トークンでアカウントの文脈を付け加えられますが、公開レコードの検証が CardanoWall の非公開セッションに依存することは決してあってはなりません。

これらのエンドポイントはプロダクトの利便性のために使ってください。高い保証が求められるチェックでは、それに加えて、スタンドアロン検証ツールを走らせてください。これは、それ自身が選んだ Cardano インフラを通じてチェーンのデータを取得し、独立したレポートを生成します。それが健全な役割分担です。API はアプリの構築を容易にしますが、証明はそれでもなお API の外側で成り立たなければなりません。オープンソースの SDK と CLI には、まさにその検証ツールが付属しています。それが端から端までどう動くかは、Label 309 レコードを検証するを参照してください。

認証はどう機能させるべきか

認証情報は範囲を絞り、オペレーターの認証情報はご自分のバックエンドに留めてください。

ゲートウェイは 2 つのプレーンを分けています。ユーザーはデータプレーン/api/v1/*)上で、ご自分のバックエンドがセッションごとに発行する短命のアカウントトークンまたは API キーを使って操作します。コントロールプレーン/control/v1/*)向けのオペレーター認証情報は、ご自分のバックエンドだけが保持します。これでアカウントのプロビジョニング、課金で実際に集金したときのクレジット付与、マージンの設定、そして先述のアカウントトークンの発行を行います。

データプレーンへのアクセスはスコープが設定されています。利用するスコープは次のとおりです。

  • poe:create — 見積もり、アップロード、公開、バッチ公開
  • poe:read — レコードの読み取り、verify ルート、そして bearer 付きで呼び出したときのステータスストリーム
  • account:read — 残高と台帳の読み取り
  • webhooks:readwebhooks:write — アカウントスコープの webhook 管理
  • billing:read — ベンダーの課金画面向けの予約スコープ

公開ページには poe:create が必要です。残高ウィジェットには account:read が必要です。どちらもそれ以上は必要ありません。オペレーターの認証情報は、ブラウザのバンドル、モバイルアプリ、パートナーのスクリプト、あるいは証明を公開するだけでよい CI ジョブに決して到達してはなりません。そうしたものは代わりに、自身のスコープ付きアカウントトークンを発行します。アカウントトークンが漏洩しても、それは 1 時間で済む問題であるべきで、インシデントであってはなりません。

再試行と冪等性はどう機能させるべきか

本番に入る前に再試行を設計してください。公開システムは、ありふれた形で失敗するからです。ネットワークの瞬断、失効した見積もり、残高不足、レート制限、進行中のままのアップロード、あるいは途切れたステータスストリームなどです。

しっかりした統合では、次のことを行うべきです。

  • API が対応している箇所では冪等性を使い、安定した発信元 ID をキーにする
  • 見積もりの失効を通常のこととして扱う。新しい見積もりを取得して続行する
  • 結果のはっきりしない失敗のあとは、やみくもに再送するのではなく、権威のある試行エンドポイントまたはレコードエンドポイントをポーリングする
  • 大きなファイルの二重アップロードを避ける
  • 残高の二重付与を避ける(クレジットは必ず支払い自身の ID をキーにする)
  • サポート用にリクエスト ID とゲートウェイ ID をログに残す
  • レポートの中で失敗した公開と検証不能なレコードを区別する

公開はレコードバイト列で重複排除され、アップロードのバッチは冪等な再実行に対応します。「もう一度試して祈る」というループの代わりに、これらの意味論に頼ってください。

webhook は何をすべきか

webhook は読み取りモデルを構築するための手段です。毎秒のポーリングで構築するものではなく、ましてやゲートウェイのデータベーステーブルを読んで構築するものでもありません。それらはエンジン内部のものであり、予告なく変わります。

webhook サブスクリプションを登録すると、ゲートウェイはライフサイクルイベントをプッシュします。poe_status_changedbalance_changed、返金の意図、アップロードの失敗などです。オペレーターはインスタンス全体に及ぶ完全なファイアホースを購読できます。アカウントスコープの webhook ルートもデータプレーンに存在します。「送信済みアイテム」のビュー、つまり各ユーザーが自分の公開したレコードをライブのステータスとともに見られる仕組みは、その代表的な用途です。poe_status_changed を消費し、それを自分のテーブルに投影し、そこから描画します。

受信側は、各配信の署名を検証し、少なくとも 1 回(at-least-once)の配信を受け入れ、投影をイベント ID または配信 ID でキーにし、再配信を何もしない操作(no-op)として扱うべきです。読み取りモデルはご自分のアプリのものです。正規の支出と公開の状態はゲートウェイのものです。描画のためにキャッシュしてもかまいませんが、支出に関わる判断には必ずゲートウェイを読んでください。

決してクライアントから出してはならないものとは

非公開のアイデンティティ素材です。ご自分のアプリケーションは、レコードを公開するために API を呼び出します。ユーザーの Identity Seed、署名用の秘密鍵、あるいは受信者の秘密鍵をゲートウェイに渡すことは決してあってはなりません。

  • 署名付きレコードでは、ローカルで署名するか、ホスト外署名を使ってください。SDK は、秘密鍵が KMS、HSM、あるいはエアギャップされたマシンの中に留まり、公開鍵と署名だけがネットワークを越えるフローを提供します。
  • 封印付きレコードでは、エンドツーエンドの機密性がユースケースで必要なとき、アップロードの前にローカルで暗号化してください。
  • 受信者による検証では、ローカルで復号してください。

この API は公開とライフサイクルの操作のためのものです。信頼の起点(root of trust)ではなく、そうである必要が決してないように設計されています。より広い原則については、鍵がデバイスから出ない理由を参照してください。

どこから始めるか

最速の経路は、github.com/cardanowall にあるオープンソースの SDK と CLI です。TypeScript SDK(@cardanowall/sdk-ts)、Python SDK(cardanowall-sdk)、Rust SDK(cardanowall)、そして cardanowall CLI があります。これらはすべてゲートウェイに依存しません。ベース URL と不透明な API キーを与えるだけです。そしてすべてに同じスタンドアロン検証ツールが付属します。まず手で API を動かしてみたい場合は、最初の証明を公開するが 1 件のレコードを端から端まで案内します。また、自動化で CLI を使うがスクリプト化した経路を扱います。ホスティングされたものを使うのではなく、ご自分でバックエンドを動かしたい場合は、ゲートウェイもオープンソースです。自分のゲートウェイを運用するを参照してください。

さらに読む

apidevelopersgateway