Skip to content

Transport

VirMesh の HTTP action を正しい envelope と署名ルールで送る方法を説明します。

ここで扱うルールは PlayerServer の共通 transport と、共有実装の handle route に対応しています。

HTTP action families

PlayerServer には 3 つの action family があります。

FamilyTransportCanonical path
publicHTTP GET or POST/public/:action
privateHTTP POST/private/:action
ws-syncWebSocket upgrade/ws-sync/:action

catalog は GET /public/, GET /private/, GET /ws-sync/ で取得できます。

Public actions

public action は action ごとに GET query か POST JSON のどちらかを使います。

  • GET action は query string を使います
  • POST action は JSON body を使います

たとえば me.virmesh.player.resolveProfileGET で、id または handle のどちらか一方だけを query に付けます。

text
GET /public/me.virmesh.player.resolveProfile?id=medi:player:ed25519:base64-public-key
GET /public/me.virmesh.player.resolveProfile?handle=alice@virmesh.me

一方、me.virmesh.register.startChallenge のような public POST action は top-level に payload を持つ JSON object を送ります。

json
{
  "payload": {
    "type": "passkey"
  }
}

me.virmesh.account.disableAccount のように top-level signature を追加で要求する public action もあります。

json
{
  "payload": {
    "accountId": "medi:player:ed25519:base64-public-key",
    "disabled_at": 1770000000
  },
  "signature": "base64-signature-of-payload"
}

この signature は canonical JSON of payload に対する本人鍵署名です。

Signed resolveProfile Response

me.virmesh.player.resolveProfile200 response は shared status envelope ではありません。 top-level の response 署名は持たず、対象 player 本人が handle と各 profile module を個別署名した shape を返します。

json
{
  "payload": {
    "handle": {
      "record": {
        "id": "medi:player:ed25519:base64-public-key",
        "primaryHandle": "alice@virmesh.me",
        "secondaryHandles": ["alice@example.com"],
        "playerServer": "https://ps.example.com/",
        "updated_at": 1770000100
      },
      "signature": "base64-signature-by-player-for-handle-record"
    },
    "modules": {
      "profile+me.virmesh.player.displayName": {
        "payload": {
          "module": "profile+me.virmesh.player.displayName",
          "id": "medi:player:ed25519:base64-public-key",
          "displayName": "Alice",
          "updated_at": 1770000200
        },
        "signature": "base64-signature-by-player-for-display-name-module"
      },
      "profile+me.virmesh.player.card": {
        "payload": {
          "module": "profile+me.virmesh.player.card",
          "id": "medi:player:ed25519:base64-public-key",
          "bio": "VR world builder",
          "image": {
            "assetId": "profimg_123",
            "contentType": "image/png",
            "hash": "sha256:base64url-hash",
            "width": 512,
            "height": 512,
            "size": 42000
          },
          "updated_at": 1770000300
        },
        "signature": "base64-signature-by-player-for-profile-card-module"
      }
    }
  }
}

クライアントは次を検証します。

  1. payload.handle.signature が canonical JSON of payload.handle.record に対する署名であること
  2. payload.modules.*.signature が対応する payload.modules.*.payload に対する署名であること
  3. payload.handle.record.idpayload.modules.*.payload.id が一致すること

Private Request Envelope

private action は通常 from, payload, signature を要求します。

json
{
  "from": "medi:player:ed25519:base64-public-key",
  "payload": {
    "primaryHandle": "alice@virmesh.me",
    "updated_at": 1770000100
  },
  "signature": "base64-signature"
}

通常の private action では、署名対象は payload 単体ではなく canonical JSON 化した次の object 全体です。

json
{
  "action": "me.virmesh.handle.updateHandle",
  "from": "medi:player:ed25519:base64-public-key",
  "payload": {
    "primaryHandle": "alice@virmesh.me",
    "updated_at": 1770000100
  }
}

updateProfile Exception

me.virmesh.player.updateProfile だけは署名意味論が少し違います。 request は private envelope ですが、signature は envelope ではなく patch 適用後に保存される module payload そのものに対する署名です。

json
{
  "from": "medi:player:ed25519:base64-public-key",
  "payload": {
    "module": "profile+me.virmesh.player.card",
    "set": {
      "bio": "VR world builder"
    },
    "updated_at": 1770000200
  },
  "signature": "base64-signature-for-resulting-module-payload"
}

この request に対して server が保存前に検証する canonical payload の例は次です。

json
{"bio":"VR world builder","id":"medi:player:ed25519:base64-public-key","module":"profile+me.virmesh.player.card","updated_at":1770000200}

Raw Profile Image Routes

profile image は action だけで完結せず、2 つの raw route も使います。

text
PUT /uploads/profile-images/:uploadToken
GET /profile-images/:assetId

詳細な flow は Profile Images を参照してください。

ws-sync

ws-sync action は GET /ws-sync/:action へ WebSocket upgrade して使います。

通常の HTTP GET で叩くと 426 を返します。

json
{
  "status": "status+me.virmesh.http.upgrade_required",
  "payload": {
    "message": "Use a WebSocket upgrade request for /ws-sync/:action."
  }
}

Canonical JSON

VirMesh の署名は stableStringify() による canonical JSON を前提にしています。

規則は次のとおりです。

  1. scalar は JSON.stringify() を使います。
  2. array は要素順を保持します。
  3. object は key を辞書順に並べ替えてから stringify します。
  4. 空白は入れません。

たとえば次の object:

json
{
  "payload": {
    "primaryHandle": "alice@virmesh.me",
    "updated_at": 1770000100
  },
  "from": "medi:player:ed25519:base64-public-key",
  "action": "me.virmesh.handle.updateHandle"
}

署名対象の文字列は次になります。

json
{"action":"me.virmesh.handle.updateHandle","from":"medi:player:ed25519:base64-public-key","payload":{"primaryHandle":"alice@virmesh.me","updated_at":1770000100}}

me.virmesh.player.resolveProfile の response 部分署名でも同じ key-sort ルールを使います。 たとえば payload.handle.record の canonical string は次です。

json
{"id":"medi:player:ed25519:base64-public-key","playerServer":"https://ps.example.com/","primaryHandle":"alice@virmesh.me","secondaryHandles":["alice@example.com"],"updated_at":1770000100}

payload.modules["profile+me.virmesh.player.displayName"].payload の canonical string は次です。

json
{"displayName":"Alice","id":"medi:player:ed25519:base64-public-key","module":"profile+me.virmesh.player.displayName","updated_at":1770000200}

Shared HTTP Error Envelope

共通 error response は次の形です。

json
{
  "status": "status+me.virmesh.json.invalid_json",
  "payload": {
    "message": "Public request must be valid JSON."
  }
}

statusstatus+me.virmesh.* 名前空間を使います。詳細は Errors を参照してください。

Next Steps

  • medi:player:... の書式と鍵の入力形式は Identity を参照してください。
  • profile module 更新、画像 upload、公開プロフィール取得は me.virmesh.player を参照してください。
  • ハンドル route は me.virmesh.handle を参照してください。
  • チャレンジ一覧取得・開始・ポーリング action は me.virmesh.register を参照してください。