AeroNyx Chat Relay クライアント統合
Blind relay messaging、相互 contact presence、reciprocal read receipt、encrypted reaction、offline queue、resumable encrypted media の client contract。
このページは AeroNyx 互換 chat client を実装する App、frontend、backend、AI coding agent 向けの正式な frame / media API contract です。Relay は ciphertext だけを route し、ユーザー内容を理解しません。
妥協できないプライバシー不変条件
Relay は chat plaintext、reaction 内容、voice/media plaintext、decrypt key、nonce、waveform、filename、transcript、MemChain plaintext、packet payload、DNS、destination、URL、history、wallet traffic、private identity seed を解析・保存・推論してはいけません。内容は client を離れる前に E2E 暗号化します。
E2E content は payload_b64 と payload_sig に入ります。Relay-visible metadata は type、IDs、receiver/group、bounded timestamp、delivery state、blob size/expiry、access mode、aggregate counters に限定します。
プレゼンスのプライバシー不変条件
Online と last seen を購読できるのは、削除されていない相互 P2PContact のみです。Backend は両方向を確認し、public key scan による online status 収集を防ぎます。ユーザーは presence と exact last seen を別々に無効化できます。
Hidden result は online と last_seen_ts を含まず、reason=not_mutual_contact または reason=presence_hidden だけを返します。
プロフィールのプライバシー API
Client は接続後に profile privacy flags を読み、local UI を backend enforcement と一致させます。PATCH は nested privacy と後方互換の top-level fields を受け付けます。
GET /api/relay/profile/
PATCH /api/relay/profile/
Authorization: Relay <pubkey>:<timestamp>:<signature>
{
"privacy": {
"presence_enabled": true,
"last_seen_enabled": false,
"read_receipts_enabled": false
}
}
プレゼンスフレーム
presence_subscribe は contact list にだけ送ります。last_seen_enabled=false なら UI は「recently」のような曖昧表示か非表示にし、別の signal から exact status を推測しません。
{
"type": "presence_subscribe",
"pubkeys": ["contact-pubkey-a", "contact-pubkey-b"]
}
{
"type": "presence_subscribe_ack",
"updates": [{
"pubkey": "contact-pubkey-a",
"visible": true,
"presence_visible": true,
"last_seen_visible": true,
"online": true,
"last_seen_ts": 1780000000,
"reason": "allowed"
}],
"server_ts": 1780000001
}
既読通知の相互ルール
Read receipt は reciprocal です。無効化した client は message_read を送らず、peer read も表示しません。どちらかが無効、または mutual contact でなければ backend が suppress します。Frame は metadata only です。
{
"type": "message_read",
"msg_id": "message-id",
"receiver_pubkey": "original-sender-pubkey",
"timestamp": 1780000200
}
{
"type": "message_read_ack",
"msg_id": "message-id",
"delivered": false,
"suppressed": true,
"reason": "receiver_read_receipts_disabled"
}
Suppression reason は client_disabled、not_mutual_contact、reader_read_receipts_disabled、receiver_read_receipts_disabled。Offline pull にも同じ gate を適用します。
絵文字リアクション
Reaction も E2E ciphertext です。Relay は receiver または group membership に基づいて route し、reaction_id で deduplicate、offline 時は store-and-forward します。Reaction aggregate state は client が保持します。
{
"type": "message_reaction",
"msg_id": "target-message-id",
"receiver_pubkey": "peer-pubkey",
"reaction_id": "unique-reaction-event-id",
"timestamp": 1780000300,
"payload_b64": "e2e-ciphertext",
"payload_sig": "ed25519-signature"
}
Signature discriminant は 12。reaction_id は idempotency / offline ACK key、ACK は message_reaction_ack。Group は group_message_reaction、group_id、key_version を使います。
暗号化メディア blob モデル
Voice、large image、video、file は client で暗号化してから ciphertext を upload します。blob_id、key、nonce、duration、waveform、display filename、preview metadata は relay_send.payload_b64 内に置き、blob API の plaintext field にしません。
{
"kind": "voice",
"blob_id": "blob-uuid",
"key_b64": "inside-e2e-envelope",
"nonce_b64": "inside-e2e-envelope",
"duration_ms": 43000,
"waveform": [0, 3, 8, 6, 2],
"media_type": "audio/ogg; codecs=opus",
"file_size": 7340032
}
シンプルな暗号化 blob アップロード
Short voice と small image は multipart single upload を使えます。Server は encrypted bytes だけを受け取り、TTL は default 7 日・policy 1〜30 日です。Download は unguessable capability または P2P public key 指定 authenticated mode です。
POST /api/relay/blob/
Authorization: Relay <pubkey>:<timestamp>:<signature>
Content-Type: multipart/form-data
| field | required | value |
|---|---|---|
file | true | ciphertext |
media_kind | false | voice, image, video, file, avatar, other |
media_type | false | MIME |
ttl_days | false | 1..30 |
access_mode | false | capability, authenticated |
allowed_downloaders | false | JSON P2P pubkey array |
Single limit は 10 MB。超過時は HTTP 413、error_code=blob_too_large、chunked_max_bytes=104857600 を返します。
再開可能な暗号化 blob アップロード
Single limit を超える場合は chunk session を使います。Ciphertext total は 100 MB まで。同じ chunk index は安全に retry でき、client は upload_id、chunk_size、completed indexes を local に保存します。
1. アップロードセッションの作成
POST /api/relay/blob/session/
Authorization: Relay <pubkey>:<timestamp>:<signature>
Content-Type: application/json
{
"total_size": 7340032,
"chunk_size": 1048576,
"media_type": "audio/ogg",
"media_kind": "voice",
"ttl_days": 7,
"access_mode": "authenticated",
"allowed_downloaders": ["receiver-pubkey"]
}
2. チャンクのアップロード
PUT /api/relay/blob/session/{upload_id}/chunk/{chunk_index}/
Authorization: Relay <pubkey>:<timestamp>:<signature>
Content-Type: application/octet-stream
Default chunk は 1 MB、maximum は 4 MB。同じ index の retry は既存 chunk を置換する idempotent operation です。
3. 通信中断後の再開
GET /api/relay/blob/session/{upload_id}/
Authorization: Relay <pubkey>:<timestamp>:<signature>
missing_chunks を読み、不足 index だけを upload します。Upload session は 24 時間有効です。
4. アップロードの完了
POST /api/relay/blob/session/{upload_id}/complete/
Authorization: Relay <pubkey>:<timestamp>:<signature>
Complete は全 chunks と total bytes を検証します。安全な retry は既存 final blob を返し、duplicate を作りません。
5. アップロードのキャンセル
DELETE /api/relay/blob/session/{upload_id}/
Authorization: Relay <pubkey>:<timestamp>:<signature>
暗号化 blob のダウンロード
Capability mode は UUID 自体が bearer capability なので RelayAuth 不要です。Authenticated mode は署名が必要で、uploader または allowed_downloaders だけが取得できます。Expired blob は access 時に lazy cleanup されます。
GET /api/relay/blob/{blob_id}/
| HTTP | error_code |
|---|---|
| 400 | blob_id_invalid |
| 401 | auth_required |
| 403 | download_forbidden |
| 404 | blob_not_found |
| 410 | blob_expired |
| 413 | blob_too_large, blob_total_size_too_large, chunk_too_large |
| 409 | upload_incomplete |
クライアント UX ガイド
Small file は single upload、max_bytes 超過時だけ chunked を使用します。App restart 後は missing_chunks を取得し、不足分だけ送ります。Media secret は E2E payload 内に残し、410 blob_expired は resend flow として扱います。
AI エージェントの統合順序
AI coding agent は RelayAuth、profile privacy、presence UI、reciprocal read receipt、reaction idempotency、single blob、chunk resume、encrypted relay_send reference の順で実装します。Relay は plaintext を検索できないため server-side chat search を追加しません。
検証済みの 2 ホップ配信
Eligible authenticated ChatRelay は network-diverse two-hop path を選べます。Source は expected terminal の signed receipt を検証してから delivery を数えます。Middle node は ciphertext だけを route します。