Appearance
Transport
VirMesh の HTTP action を正しい envelope と署名ルールで送る方法を説明します。
ここで扱うルールは PlayerServer の共通 transport と、共有実装の handle route に対応しています。
HTTP action families
PlayerServer には 3 つの action family があります。
| Family | Transport | Canonical path |
|---|---|---|
public | HTTP GET or POST | /public/:action |
private | HTTP POST | /private/:action |
ws-sync | WebSocket upgrade | /ws-sync/:action |
catalog は GET /public/, GET /private/, GET /ws-sync/ で取得できます。
Public actions
public action は action ごとに GET query か POST JSON のどちらかを使います。
GETaction は query string を使いますPOSTaction は JSON body を使います
たとえば me.virmesh.player.resolveProfile は GET で、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.resolveProfile の 200 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"
}
}
}
}クライアントは次を検証します。
payload.handle.signatureが canonical JSON ofpayload.handle.recordに対する署名であることpayload.modules.*.signatureが対応するpayload.modules.*.payloadに対する署名であることpayload.handle.record.idとpayload.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 を前提にしています。
規則は次のとおりです。
- scalar は
JSON.stringify()を使います。 - array は要素順を保持します。
- object は key を辞書順に並べ替えてから stringify します。
- 空白は入れません。
たとえば次の 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."
}
}status は status+me.virmesh.* 名前空間を使います。詳細は Errors を参照してください。
Next Steps
medi:player:...の書式と鍵の入力形式は Identity を参照してください。- profile module 更新、画像 upload、公開プロフィール取得は me.virmesh.player を参照してください。
- ハンドル route は me.virmesh.handle を参照してください。
- チャレンジ一覧取得・開始・ポーリング action は me.virmesh.register を参照してください。