AeroNyx Chat Relay 클라이언트 통합

AeroNyx2026년 6월 19일4분 읽기35회 조회

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_b64payload_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에는 onlinelast_seen_ts가 없고 reason=not_mutual_contact 또는 reason=presence_hidden만 반환합니다.

프로필 개인정보 보호 API

Client는 연결 후 profile privacy flags를 읽고 local UI를 backend enforcement와 일치시켜야 합니다. PATCH는 nested privacy와 하위 호환 top-level fields를 지원합니다.

http
GET /api/relay/profile/
PATCH /api/relay/profile/
Authorization: Relay <pubkey>:<timestamp>:<signature>
json
{
  "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를 추론하지 않습니다.

json
{
  "type": "presence_subscribe",
  "pubkeys": ["contact-pubkey-a", "contact-pubkey-b"]
}
json
{
  "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입니다.

json
{
  "type": "message_read",
  "msg_id": "message-id",
  "receiver_pubkey": "original-sender-pubkey",
  "timestamp": 1780000200
}
json
{
  "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가 유지합니다.

json
{
  "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로 보내지 않습니다.

json
{
  "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입니다.

http
POST /api/relay/blob/
Authorization: Relay <pubkey>:<timestamp>:<signature>
Content-Type: multipart/form-data
fieldrequiredvalue
filetrueciphertext
media_kindfalsevoice, image, video, file, avatar, other
media_typefalseMIME
ttl_daysfalse1..30
access_modefalsecapability, authenticated
allowed_downloadersfalseJSON 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. 업로드 세션 생성

http
POST /api/relay/blob/session/
Authorization: Relay <pubkey>:<timestamp>:<signature>
Content-Type: application/json
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. 청크 업로드

http
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. 네트워크 중단 후 재개

http
GET /api/relay/blob/session/{upload_id}/
Authorization: Relay <pubkey>:<timestamp>:<signature>

missing_chunks를 읽고 부족한 index만 upload합니다. Upload session은 24시간 유효합니다.

4. 업로드 완료

http
POST /api/relay/blob/session/{upload_id}/complete/
Authorization: Relay <pubkey>:<timestamp>:<signature>

Complete는 모든 chunks와 total bytes를 검증합니다. 안전한 retry는 기존 final blob을 반환하고 duplicate를 만들지 않습니다.

5. 업로드 취소

http
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됩니다.

http
GET /api/relay/blob/{blob_id}/
HTTPerror_code
400blob_id_invalid
401auth_required
403download_forbidden
404blob_not_found
410blob_expired
413blob_too_large, blob_total_size_too_large, chunk_too_large
409upload_incomplete

클라이언트 UX 지침

Small file은 single upload, max_bytes 초과 시 chunked upload를 사용합니다. 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합니다.

Node discovery와 verified encrypted relay delivery