Panduan Integrasi AeroNyx Chat API
Kirim dan terima pesan AeroNyx terenkripsi ujung ke ujung melalui HTTPS: penandatanganan permintaan, mulai cepat yang dapat dijalankan, format amplop tersegel, kode kesalahan, dan vektor uji.
AeroNyx Chat API memungkinkan server, bot, atau aplikasi mengirim dan menerima pesan satu-ke-satu terenkripsi ujung ke ujung dengan pengguna AeroNyx melalui HTTPS biasa. API ini tidak menggunakan AeroNyx Privacy Network, VPN, onion routing, atau kotak surat anonim.
Pengiriman berlangsung seketika: jika App penerima sedang terhubung, pesan Anda langsung sampai. Penerimaan melalui API ini bekerja dengan polling: GET /inbox/ mengembalikan apa pun yang sedang menunggu dan tidak pernah menahan permintaan tetap terbuka. Untuk menerima secara real-time, hubungkan identitas yang sama ke WebSocket Chat Relay (wss://api.aeronyx.network/ws/relay/). Lihat bagian "Pengiriman real-time" di bawah.
URL dasar: https://api.aeronyx.network/api/relay/developer/v1
Cara kerjanya
- Buat identitas. Pasangan kunci Ed25519 adalah identitas chat Anda. Kunci publik 32 byte, yang ditulis sebagai 64 karakter heksadesimal huruf kecil, adalah alamat chat Anda. Akun AeroNyx tidak diperlukan.
- Tandatangani setiap permintaan. Setiap panggilan membawa header
Authorizationyang ditandatangani dengan kunci Anda (Autentikasi). - Segel setiap pesan. Enkripsi dan tandatangani pesan di sisi Anda, lalu kirim hanya amplop yang dihasilkan (Amplop pesan).
- Kirim dengan
POST /messages/. - Terima dengan
GET /inbox/, dekripsi dan simpan setiap pesan, lalu konfirmasikan denganPOST /acks/.
| Endpoint | Autentikasi | Tujuan |
|---|---|---|
GET /capabilities/ | tidak ada | Batas layanan dan fitur yang didukung |
POST /messages/ | wajib | Mengirim pesan tersegel |
GET /inbox/ | wajib | Mengambil pesan yang belum dikonfirmasi |
POST /acks/ | wajib | Mengonfirmasi pesan yang sudah Anda simpan |
Mulai cepat
Skrip ini mengirim pesan dari satu identitas baru ke identitas lain, membacanya
kembali, mendekripsinya, lalu mengonfirmasinya. Simpan
implementasi referensi di bawah ini sebagai
aeronyx_chat.py, pasang dependensinya dengan pip install pynacl requests,
lalu jalankan.
from __future__ import annotations
import hashlib, json, os, time, uuid
import requests
from nacl.signing import SigningKey
from aeronyx_chat import open_, seal
BASE = "https://api.aeronyx.network"
PREFIX = "/api/relay/developer/v1"
def call(key: SigningKey, method: str, path: str, body: dict | None = None):
"""Signs and sends one request. `path` is the full path after the host."""
raw = b"" if body is None else json.dumps(body, separators=(",", ":")).encode()
seconds = str(int(time.time()))
nonce = os.urandom(16)
digest = hashlib.sha256(
b"AeroNyx/DeveloperChatRequestV1\0" + method.encode() + b"\0"
+ path.encode() + b"\0" + seconds.encode() + b"\0"
+ nonce + hashlib.sha256(raw).digest()
).digest()
signature = key.sign(digest).signature.hex()
pub = key.verify_key.encode().hex()
headers = {
"Authorization": f"AeroNyx-Chat-V1 {pub}:{seconds}:{nonce.hex()}:{signature}",
"Content-Type": "application/json",
}
# Send exactly the bytes that were signed.
return requests.request(method, BASE + path, data=raw or None,
headers=headers, timeout=20)
alice, bob = SigningKey.generate(), SigningKey.generate()
bob_address = bob.verify_key.encode().hex()
# 1. Alice seals and sends a message to Bob.
message_id = uuid.uuid4()
now = int(time.time())
sent = call(alice, "POST", PREFIX + "/messages/", {
"receiver_pubkey": bob_address,
"message_id": str(message_id),
"discriminant": 11,
"sent_at": now,
"payload_b64": seal(alice, bob.verify_key.encode(), "Hello Bob", message_id, now),
})
print("send:", sent.status_code, sent.json())
# 2. Bob fetches his inbox, decrypts, and then acknowledges.
inbox = call(bob, "GET", PREFIX + "/inbox/?limit=50").json()
for item in inbox["messages"]:
opened = open_(bob, item["payload_b64"])
print("received:", opened["plaintext"], "from", opened["sender"][:8])
# Store the message durably here, then acknowledge it.
ack = call(bob, "POST", PREFIX + "/acks/", {"message_ids": [item["msg_id"]]})
print("ack:", ack.status_code, ack.json())
Autentikasi
Setiap endpoint kecuali GET /capabilities/ memerlukan header ini:
Authorization: AeroNyx-Chat-V1 <public-key-hex>:<unix-seconds>:<nonce-hex>:<signature-hex>
public-key-hexadalah alamat chat Anda yang terdiri dari 64 karakter.unix-secondsharus berada dalam selisih 60 detik dari jam server.nonce-hexadalah 16 byte acak yang ditulis sebagai 32 karakter heksadesimal huruf kecil. Setiap nonce hanya dapat dipakai sekali.signature-hexadalah tanda tangan Ed25519, ditulis sebagai 128 karakter heksadesimal, atas digest 32 byte berikut:
SHA256(
"AeroNyx/DeveloperChatRequestV1\0" ||
UPPERCASE_HTTP_METHOD || "\0" ||
PATH_WITH_QUERY || "\0" ||
ASCII_UNIX_SECONDS || "\0" ||
NONCE_BYTES ||
SHA256(EXACT_HTTP_BODY_BYTES)
)
PATH_WITH_QUERY adalah path permintaan lengkap sebagaimana dikirim, termasuk
awalan /api/relay/developer/v1 dan string kueri apa pun. Contoh:
| Permintaan | PATH_WITH_QUERY yang ditandatangani |
|---|---|
POST https://api.aeronyx.network/api/relay/developer/v1/messages/ | /api/relay/developer/v1/messages/ |
GET https://api.aeronyx.network/api/relay/developer/v1/inbox/?limit=50 | /api/relay/developer/v1/inbox/?limit=50 |
EXACT_HTTP_BODY_BYTES adalah byte isi permintaan persis seperti yang dikirim.
Karena itu spasi putih JSON dan urutan kunci berpengaruh: serialisasikan sekali,
tandatangani byte tersebut, dan kirim byte yang sama. Permintaan tanpa isi
menandatangani SHA-256 dari string kosong.
Vektor uji autentikasi
Gunakan ini untuk memeriksa kode penandatanganan Anda sebelum memanggil API. Kunci ini hanya untuk pengujian; jangan pernah memakainya untuk pesan sungguhan.
{
"seed_hex": "0303030303030303030303030303030303030303030303030303030303030303",
"public_key_hex": "ed4928c628d1c2c6eae90338905995612959273a5c63f93636c14614ac8737d1",
"method": "POST",
"path_with_query": "/api/relay/developer/v1/messages/",
"unix_seconds": "1780000000",
"nonce_hex": "000102030405060708090a0b0c0d0e0f",
"body": "{\"receiver_pubkey\":\"8139770ea87d175f56a35466c34c7ecccb8d8a91b4ee37a25df60f5b8fc9b394\",\"message_id\":\"018f5164-c1b2-7e2b-9d8a-2c60597f4c2f\",\"discriminant\":11,\"sent_at\":1780000000,\"payload_b64\":\"AAAA\"}",
"body_sha256_hex": "1971a2ef86b2a085e033fd65313981c8668204d49cedbe530875036d1f45c9cc",
"digest_hex": "6f4d0ca6dcd1221780b2dae45dd94e4bae7e79f266e8bb69f2d50c20de08b276",
"signature_hex": "14ed2108e1ecf8ce5d04299e59af6d00dec218d62592de127e6a640f235ea4e9703004561fc08e929d0159f92fc51d35a881eec7d0eb16f19d94270c6262cb0c",
"authorization": "AeroNyx-Chat-V1 ed4928c628d1c2c6eae90338905995612959273a5c63f93636c14614ac8737d1:1780000000:000102030405060708090a0b0c0d0e0f:14ed2108e1ecf8ce5d04299e59af6d00dec218d62592de127e6a640f235ea4e9703004561fc08e929d0159f92fc51d35a881eec7d0eb16f19d94270c6262cb0c"
}
Tanda tangan Ed25519 bersifat deterministik, sehingga masukan yang sama harus
menghasilkan signature_hex persis seperti ini. Jika body_sha256_hex Anda
berbeda, berarti byte isi Anda berbeda.
Pemecahan masalah 401
API menjawab setiap kegagalan autentikasi dengan 401 yang sama, jadi periksa
hal-hal berikut secara berurutan:
- Jam:
unix-secondsberada dalam selisih 60 detik dari waktu sebenarnya. - Nonce baru untuk setiap permintaan, sepanjang 16 byte, ditulis sebagai 32 karakter heksadesimal huruf kecil.
- Path yang ditandatangani menyertakan
/api/relay/developer/v1dan string kueri. - Metode yang ditandatangani menggunakan huruf kapital dan cocok dengan permintaan.
- Byte isi yang ditandatangani adalah byte yang Anda kirim. Jangan serialisasikan ulang JSON setelah penandatanganan.
- Digest dan tanda tangan Anda cocok dengan vektor uji di atas.
Endpoint
Informasi layanan
GET /capabilities/ tidak memerlukan autentikasi dan mengembalikan batas saat
ini serta fitur yang didukung.
Mengirim pesan
POST /messages/
{
"receiver_pubkey": "<64 lowercase hex characters>",
"message_id": "018f5164-c1b2-7e2b-9d8a-2c60597f4c2f",
"discriminant": 11,
"sent_at": 1780000000,
"payload_b64": "<sealed envelope, Base64>"
}
Kelima kolom wajib diisi dan tidak ada kolom lain yang diizinkan.
receiver_pubkeyadalah alamat chat penerima. Nilainya harus berbeda dari alamat Anda sendiri.message_idadalah UUID. Gunakan UUID acak baru untuk setiap pesan dan simpan isi permintaan yang persis sama untuk percobaan ulang. Mengirimmessage_idyang sama dengan kolom yang berbeda mengembalikan409 idempotency_conflict.discriminantbernilai11untuk pesan satu-ke-satu yang harus dilihat pengguna AeroNyx App.sent_atadalah detik Unix dan harus berada dalam selisih 300 detik dari waktu server.payload_b64adalah amplop tersegel yang dijelaskan di Amplop pesan.
Relay hanya memeriksa bahwa payload_b64 adalah Base64 yang valid berukuran 1
byte hingga 191 KiB. Relay tidak dapat membedakan amplop dari data lain,
sehingga payload yang bukan amplop valid diterima di sini dan dibuang secara
diam-diam oleh App penerima. Uji amplop Anda terhadap
vektor emas.
Permintaan yang berhasil mengembalikan HTTP 202:
{
"message_id": "018f5164-c1b2-7e2b-9d8a-2c60597f4c2f",
"accepted": true,
"delivered_live": false
}
accepted berarti relay telah menyimpan ciphertext secara permanen. delivered_live bernilai true ketika relay telah menyerahkan pesan ke pengiriman langsung tanpa galat. Nilainya tetap true meskipun penerima tidak memiliki koneksi terbuka, jadi field ini tidak memberi tahu apakah seseorang sedang online. Tidak satu pun dari kedua field berarti penerima telah mendekripsi atau menampilkan pesan; yang menunjukkan hal itu adalah tanda terima pengiriman di kotak masuk Anda (lihat "Peristiwa di kotak masuk").
Mengambil kotak masuk
GET /inbox/?limit=50
Mengembalikan hingga 100 peristiwa yang belum dikonfirmasi. Peristiwa yang sama dapat muncul lagi setelah batas waktu habis atau setelah restart.
{
"messages": [{
"sender_pubkey": "<hex>",
"discriminant": 11,
"payload_b64": "<ciphertext>",
"timestamp": 1780000000,
"msg_id": "018f5164-c1b2-7e2b-9d8a-2c60597f4c2f"
}],
"has_more": false
}
Jika has_more bernilai true, konfirmasikan pesan yang sudah Anda simpan lalu
ambil lagi.
Peristiwa di kotak masuk
Kotak masuk berisi semua yang telah diantrekan relay untuk identitas Anda, bukan hanya pesan baru. Item tanpa control_type adalah pesan: buka payload_b64 seperti dijelaskan di "Amplop pesan". Item dengan control_type adalah peristiwa tentang sebuah pesan. Periksa control_type sebelum mendekripsi apa pun: tanda terima tidak memiliki payload_b64.
control_type | Arti | msg_id |
|---|---|---|
message_receipt | App penerima telah mendekripsi dan menyimpan pesan yang Anda kirim. | message_id pesan Anda |
message_read | Penerima telah membaca pesan Anda. Hanya dikirim jika tanda terima baca mereka aktif. | message_id pesan Anda |
message_edit | Pengirim mengedit sebuah pesan (target_msg_id, ID yang sama). | ID pesan yang diedit |
message_revoke | Pengirim menarik sebuah pesan. | ID pesan yang ditarik |
message_reaction | Reaksi terhadap target_msg_id ditambahkan atau dihapus. | ID reaksi |
Edit, penarikan, dan reaksi ditandatangani (payload_sig); formatnya dijelaskan di Integrasi Klien AeroNyx Chat Relay. Mengonfirmasi sebuah ID menghapus semua item antrean yang membawanya, jadi proses satu halaman penuh sebelum mengonfirmasi. Konfirmasi juga jenis peristiwa yang tidak Anda tangani; jika tidak, item tersebut akan dikembalikan lagi.
Mengonfirmasi pesan
POST /acks/
{
"message_ids": ["018f5164-c1b2-7e2b-9d8a-2c60597f4c2f"]
}
Konfirmasikan sebuah pesan hanya setelah Anda memvalidasinya, mendekripsinya,
dan menuliskannya ke penyimpanan yang tahan lama. ID yang tidak dikenal tidak
dimasukkan ke dalam daftar acknowledged pada respons.
Model pengiriman
- Pengiriman bersifat setidaknya sekali. Hilangkan duplikat berdasarkan
control_typebersamamsg_id: tanda terima membawa ID pesan yang dikonfirmasinya. - Ciphertext yang belum terkirim disimpan selama 72 jam. Jangan anggap layanan ini sebagai riwayat permanen.
- Ulangi pengiriman yang gagal dengan isi permintaan yang persis sama dan nonce autentikasi yang baru.
Pengiriman real-time
Mengirim
POST /messages/ menyerahkan pesan Anda ke setiap koneksi yang sedang dibuka penerima dan, pada saat yang sama, menyimpannya dalam antrean sampai App penerima mengonfirmasinya. Penerima yang App-nya terhubung langsung menerimanya; penerima yang offline menerimanya saat App terhubung berikutnya, dalam 72 jam.
Pesan yang dikirim melalui API ini tidak memicu notifikasi push. Penerima yang App-nya tertutup tidak diberi tahu dan akan melihat pesan saat membuka App berikutnya.
Menerima dengan polling
GET /inbox/ langsung kembali, ada atau tidak ada yang menunggu. Pesan baru sampai kepada Anda pada panggilan berikutnya, jadi jeda yang Anda lihat sama dengan interval polling Anda. Untuk percakapan, terimalah melalui WebSocket.
Menerima secara real-time
Hubungkan identitas yang sama ke WebSocket Chat Relay. Tidak perlu akun atau pendaftaran.
- Buka
wss://api.aeronyx.network/ws/relay/dan login dengan kunci Ed25519 yang sama, seperti dijelaskan di bagian login pada Integrasi Klien AeroNyx Chat Relay. - Setelah login, relay terlebih dahulu mengirim ulang semua yang masih antre untuk Anda (
from_offlinebernilaitrue), lalu mengirim setiap item baru segera setelah tiba. Pesan tiba sebagai framerelay_envelopedengan field yang sama seperti pesan di kotak masuk; tanda terima, edit, penarikan, dan reaksi tiba sebagai frame dengantypeberupacontrol_typepada tabel di atas. - Setelah menyimpan sebuah item, konfirmasikan:
{"type": "relay_offline_ack", "msg_id": "018f5164-c1b2-7e2b-9d8a-2c60597f4c2f"}
WebSocket dan GET /inbox/ membaca antrean yang sama: item yang dikonfirmasi di satu sisi hilang dari sisi lain, dan POST /acks/ berlaku untuk keduanya. Anda dapat tetap mengirim melalui HTTPS sambil menerima melalui WebSocket.
Batas dan kesalahan
| Batas | Nilai |
|---|---|
| Payload tersegel | 191 KiB (permintaan JSON Base64 tetap di bawah batas permintaan) |
| Isi permintaan | 256 KiB |
| Halaman kotak masuk dan kumpulan konfirmasi | 100 item |
| Retensi offline | 72 jam |
| Status | Arti |
|---|---|
400 invalid_json | Isi permintaan bukan objek JSON. |
400 invalid_request | Isi permintaan tidak memuat tepat lima kolom, receiver_pubkey bukan 64 karakter heksadesimal huruf kecil atau sama dengan alamat Anda sendiri, discriminant di luar rentang 1-255, message_id bukan UUID, atau payload_b64 bukan Base64 yang valid. |
400 invalid_payload | Payload yang didekode kosong atau lebih besar dari 191 KiB. |
400 message_expired | sent_at berbeda dari waktu server lebih dari 300 detik. |
401 | Autentikasi tidak ada, kedaluwarsa, tidak valid, atau diputar ulang. Lihat Pemecahan masalah 401. |
403 | Kebijakan penerima menolak pengirim ini. |
409 | Konflik idempotensi, atau permintaan yang identik masih diproses. |
429 | Batas laju pengirim tercapai: 50 permintaan per detik dan 100.000 per hari UTC untuk setiap identitas. Tunggu selama waktu yang ditunjukkan di header Retry-After. |
503 | Pengiriman tahan lama atau autentikasi untuk sementara tidak tersedia. Ulangi dengan isi permintaan yang persis sama dan nonce baru. |
Yang tidak dicakup panduan ini
Panduan ini mencakup pesan satu lawan satu melalui HTTPS. Untuk hal lain, hubungkan identitas yang sama ke WebSocket relay chat dan lihat Integrasi Klien AeroNyx Chat Relay:
- Pesan grup, status kehadiran, dan indikator mengetik.
- Lampiran terenkripsi, termasuk pesan suara dan berkas video.
- Panggilan suara dan video. Frame sinyal tercantum di artikel tersebut, tetapi panduan integrasi panggilan yang lengkap (token media, tanda tangan undangan, dan enkripsi panggilan) belum dipublikasikan.
Privasi dan visibilitas
Isi pesan dienkripsi ujung ke ujung: pesan disegel di sisi Anda dengan kunci yang diturunkan dari identitas pengirim dan penerima, yang tidak pernah dipegang AeroNyx, dan API hanya menyimpan serta meneruskan ciphertext. Untuk mengirimkan pesan, layanan dapat melihat kunci publik pengirim dan penerima, waktu pesan, ukuran, status pengiriman, dan alamat IP setiap permintaan. HTTPS tidak menyembunyikan metadata ini dari layanan.
Amplop pesan
Pesan yang harus dilihat pengguna AeroNyx App menggunakan discriminant: 11
dan amplop tersegel ujung ke ujung di payload_b64. Relay tidak pernah melihat
kunci maupun plaintext. Semua bilangan bulat berformat little-endian.
1. Kunci bersama
Kedua pihak menurunkan kunci 32 byte yang sama dari identitas chat Ed25519 mereka:
my_x25519_secret = clamp(SHA-512(my_ed25519_seed)[0..32])
peer_x25519 = Edwards-to-Montgomery(peer_ed25519_public_key)
raw = X25519(my_x25519_secret, peer_x25519)
key = HKDF-SHA256(salt = empty, ikm = raw, info = "AERONYX-P2P-KEY", length = 32)
Dengan libsodium, ini adalah crypto_sign_ed25519_sk_to_curve25519,
crypto_sign_ed25519_pk_to_curve25519, dan crypto_scalarmult, diikuti HKDF.
Pengirim selalu menggunakan kunci HKDF. Versi App yang sangat lama mengirim
langsung dengan raw, sehingga penerima dapat mencoba raw jika kunci HKDF
gagal mendekripsi.
2. Plaintext
Pesan teks adalah teks UTF-8-nya. Pesan dari App yang membawa lampiran, balasan,
atau teruskan berupa objek JSON dengan "type": "aeronyx_message" dan kolom
text; tampilkan text jika Anda tidak mendukung bagian lainnya.
3. Enkripsi
XChaCha20-Poly1305 dengan nonce acak baru sepanjang 24 byte dan tanpa data
terkait. ciphertext di bawah adalah keluaran cipher termasuk tag 16 byte-nya.
4. Tanda tangan
Pengirim menandatangani 121 byte berikut dengan Ed25519:
sender_pubkey 32 bytes
message_id 16 bytes (the raw bytes of the UUID sent as message_id)
receiver_pubkey 32 bytes
timestamp 8 bytes (u64, Unix seconds; use the same value as sent_at)
content_type 1 byte (0 = text)
SHA-256(ciphertext) 32 bytes
5. Amplop
message_id 16 bytes
sender_pubkey 32 bytes
receiver_pubkey 32 bytes
timestamp 8 bytes (u64)
ciphertext_len 8 bytes (u64)
ciphertext ciphertext_len bytes
nonce 24 bytes
content_type 4 bytes (u32, 0 = text)
signature 64 bytes
payload_b64 adalah Base64 standar dari byte-byte ini. Perhatikan bahwa
content_type berukuran 1 byte di dalam data yang ditandatangani tetapi 4 byte
di dalam amplop.
Menerima
Urai amplop, periksa bahwa receiver_pubkey adalah identitas Anda, verifikasi
tanda tangan terhadap sender_pubkey pada amplop, lalu dekripsi dengan kunci
yang diturunkan dari pengirim tersebut. Buang pesan jika ada langkah yang
gagal. App membuang pesan langsung yang timestamp-nya berselisih lebih dari
300 detik dari jamnya.
Implementasi referensi (Python)
"""AeroNyx 1:1 chat envelope (discriminant 11). Requires PyNaCl."""
from __future__ import annotations
import base64
import hashlib
import hmac
import os
import struct
import uuid
from nacl.bindings import (
crypto_aead_xchacha20poly1305_ietf_decrypt,
crypto_aead_xchacha20poly1305_ietf_encrypt,
crypto_scalarmult,
crypto_sign_ed25519_pk_to_curve25519,
crypto_sign_ed25519_sk_to_curve25519,
)
from nacl.signing import SigningKey, VerifyKey
CONTENT_TEXT = 0
_HKDF_INFO = b"AERONYX-P2P-KEY"
def _hkdf_sha256(ikm: bytes, info: bytes, length: int = 32) -> bytes:
# RFC 5869 with an empty salt (= 32 zero bytes).
prk = hmac.new(b"\x00" * 32, ikm, hashlib.sha256).digest()
okm, block, counter = b"", b"", 1
while len(okm) < length:
block = hmac.new(prk, block + info + bytes([counter]), hashlib.sha256).digest()
okm += block
counter += 1
return okm[:length]
def chat_key(my_key: SigningKey, peer_public: bytes, hkdf: bool = True) -> bytes:
"""X25519 over the Ed25519 identities, then HKDF-SHA256."""
my_secret = bytes(my_key) + my_key.verify_key.encode() # libsodium 64-byte sk
raw = crypto_scalarmult(
crypto_sign_ed25519_sk_to_curve25519(my_secret),
crypto_sign_ed25519_pk_to_curve25519(peer_public),
)
return _hkdf_sha256(raw, _HKDF_INFO) if hkdf else raw
def _sign_data(sender, message_id, receiver, timestamp, content_type, ciphertext):
return (
sender
+ message_id
+ receiver
+ struct.pack("<Q", timestamp)
+ bytes([content_type]) # one byte here ...
+ hashlib.sha256(ciphertext).digest()
)
def seal(my_key: SigningKey, receiver: bytes, text: str, message_id: uuid.UUID,
timestamp: int, nonce: bytes | None = None) -> str:
"""Returns payload_b64 for POST /messages/ (discriminant 11)."""
nonce = nonce if nonce is not None else os.urandom(24)
key = chat_key(my_key, receiver)
ciphertext = crypto_aead_xchacha20poly1305_ietf_encrypt(
text.encode("utf-8"), None, nonce, key) # ciphertext || 16-byte tag, no AAD
sender = my_key.verify_key.encode()
mid = message_id.bytes
signature = my_key.sign(
_sign_data(sender, mid, receiver, timestamp, CONTENT_TEXT, ciphertext)
).signature
envelope = (
mid
+ sender
+ receiver
+ struct.pack("<Q", timestamp)
+ struct.pack("<Q", len(ciphertext))
+ ciphertext
+ nonce
+ struct.pack("<I", CONTENT_TEXT) # ... four bytes here
+ signature
)
return base64.b64encode(envelope).decode("ascii")
def open_(my_key: SigningKey, payload_b64: str) -> dict:
"""Verifies and decrypts an inbox payload addressed to my_key."""
env = base64.b64decode(payload_b64, validate=True)
if len(env) < 16 + 32 + 32 + 8 + 8 + 16 + 24 + 4 + 64:
raise ValueError("envelope too short")
mid, sender, receiver = env[0:16], env[16:48], env[48:80]
timestamp, ct_len = struct.unpack("<QQ", env[80:96])
if len(env) != 96 + ct_len + 24 + 4 + 64:
raise ValueError("envelope length mismatch")
ciphertext = env[96:96 + ct_len]
nonce = env[96 + ct_len:120 + ct_len]
(content_type,) = struct.unpack("<I", env[120 + ct_len:124 + ct_len])
signature = env[124 + ct_len:]
if receiver != my_key.verify_key.encode():
raise ValueError("envelope is addressed to another identity")
VerifyKey(sender).verify(
_sign_data(sender, mid, receiver, timestamp, content_type, ciphertext), signature)
# Current App versions use the HKDF key; very old ones sent with the raw key.
for hkdf in (True, False):
try:
plaintext = crypto_aead_xchacha20poly1305_ietf_decrypt(
ciphertext, None, nonce, chat_key(my_key, sender, hkdf))
break
except Exception: # noqa: BLE001 - try the legacy key next
plaintext = None
if plaintext is None:
raise ValueError("decryption failed")
return {
"message_id": str(uuid.UUID(bytes=mid)),
"sender": sender.hex(),
"timestamp": timestamp,
"content_type": content_type,
"plaintext": plaintext.decode("utf-8"),
}
Vektor emas
Kunci, nonce, ID, dan waktu yang tetap, hanya untuk pengujian. Jangan pernah memakai kunci-kunci ini.
{
"sender_seed_hex": "0101010101010101010101010101010101010101010101010101010101010101",
"sender_pubkey": "8a88e3dd7409f195fd52db2d3cba5d72ca6709bf1d94121bf3748801b40f6f5c",
"receiver_seed_hex": "0202020202020202020202020202020202020202020202020202020202020202",
"receiver_pubkey": "8139770ea87d175f56a35466c34c7ecccb8d8a91b4ee37a25df60f5b8fc9b394",
"message_id": "018f5164-c1b2-7e2b-9d8a-2c60597f4c2f",
"timestamp": 1780000000,
"nonce_hex": "000102030405060708090a0b0c0d0e0f1011121314151617",
"plaintext": "Hello from AeroNyx",
"shared_key_hex": "dd08f8bb45a43c9bc186fdaf436d47e8e05b0aca44fa2bb9473769da0407652f",
"payload_b64": "AY9RZMGyfiudiixgWX9ML4qI4910CfGV/VLbLTy6XXLKZwm/HZQSG/N0iAG0D29cgTl3Dqh9F19Wo1Rmw0x+zMuNipG07jeiXfYPW4/Js5QApRhqAAAAACIAAAAAAAAA1vVyzdTx1kAdPEI7AGO12ppwLwaME0K9PJKd+u7OgWq0rwABAgMEBQYHCAkKCwwNDg8QERITFBUWFwAAAACAlpInhB5A9cD4LqKAT0OE0OpbMqT5w/xB8IiHrzuyg9LPjF9gDHdOekPNoHNxDMeu3Ift0UB69OGSofdfwKIN"
}
seal(alice, bob_public, "Hello from AeroNyx", message_id, 1780000000, nonce=bytes(range(24))) harus menghasilkan payload_b64 persis seperti ini,
dan open_(bob, payload_b64) harus mengembalikan plaintext. Vektor ini telah
diverifikasi terhadap kesepakatan kunci, penanda tangan, dan cipher milik
AeroNyx App sendiri, serta terhadap dekoder amplop ketat milik node.
Pertanyaan yang sering diajukan
Apa itu AeroNyx Chat API?
API HTTPS berversi untuk bertukar pesan satu-ke-satu terenkripsi ujung ke ujung dengan pengguna AeroNyx dari server, bot, atau aplikasi. API ini memiliki empat endpoint: capabilities, kirim, kotak masuk, dan konfirmasi. API ini hanya membawa amplop tersegel; isinya dienkripsi di sisi Anda.
Apakah saya memerlukan akun AeroNyx untuk menggunakan API ini?
Tidak. Pasangan kunci Ed25519 adalah identitasnya. Buat secara lokal dan tandatangani setiap permintaan dengannya; kunci publik adalah alamat chat Anda.
Apakah saya menandatangani /messages/ atau path lengkap?
Path lengkap sebagaimana dikirim, dimulai dengan /api/relay/developer/v1, ditambah string kueri jika ada. Menandatangani hanya /messages/ menghasilkan 401. Vektor uji autentikasi di halaman ini menunjukkan masukan yang tepat.
Mengapa saya mendapat 401?
Setiap kegagalan autentikasi mengembalikan 401 yang sama. Penyebab yang umum adalah jam yang selisihnya lebih dari 60 detik, nonce yang dipakai ulang, path atau isi permintaan yang ditandatangani berbeda dari yang dikirim, atau metode HTTP huruf kecil pada data yang ditandatangani. Telusuri daftar periksa pada Pemecahan masalah 401 dan bandingkan digest Anda dengan vektor uji.
Mengapa API mengembalikan 202 tetapi pesan tidak pernah muncul di AeroNyx App?
Relay menerima payload Base64 yang valid apa pun, tetapi App hanya menampilkan amplop tersegel yang dapat diverifikasi dan didekripsinya. Periksa payload_b64 Anda terhadap vektor emas di halaman ini, pastikan receiver_pubkey pada amplop adalah penerima, dan jaga agar stempel waktu amplop berada dalam selisih 300 detik dari waktu saat ini. Mengirim teks biasa yang disandikan sebagai Base64 diterima tetapi tidak pernah ditampilkan.
Mengapa API mengembalikan invalid_request?
Penyebab yang paling umum adalah mengirim ke kunci publik Anda sendiri, isi permintaan yang tidak memuat tepat lima kolom yang didokumentasikan, message_id yang bukan UUID, atau discriminant di luar rentang 1-255. Lihat tabel kesalahan pada Batas dan kesalahan.
Dapatkah AeroNyx membaca pesan yang dikirim melalui API ini?
Tidak. Amplop dienkripsi dengan kunci yang diturunkan dari identitas pengirim dan penerima, yang tidak pernah dipegang AeroNyx. Layanan dapat melihat metadata pengiriman seperti kunci publik, waktu, ukuran, dan alamat IP.
Berapa lama pesan yang belum terkirim disimpan?
Hingga 72 jam. Konfirmasikan setiap pesan hanya setelah Anda mendekripsi dan menyimpannya; pesan yang belum dikonfirmasi dapat dikirim ulang.
Bahasa pemrograman apa yang dapat saya gunakan?
Bahasa apa pun yang memiliki Ed25519, X25519, HKDF-SHA256, dan XChaCha20-Poly1305, termasuk binding libsodium untuk sebagian besar bahasa. Halaman ini menyertakan implementasi referensi Python yang lengkap dan mulai cepat yang dapat dijalankan.
Apakah AeroNyx Chat API bersifat real-time?
Pengiriman ya: penerima yang App-nya terhubung langsung mendapat pesan Anda. Penerimaan melalui HTTPS menggunakan polling, jadi Anda melihat pesan baru pada panggilan GET /inbox/ berikutnya. Untuk menerima secara real-time, hubungkan identitas yang sama ke WebSocket Chat Relay di wss://api.aeronyx.network/ws/relay/; keduanya membaca antrean yang sama.
Apakah pengguna AeroNyx mendapat notifikasi untuk pesan yang saya kirim?
Saat ini tidak. Pesan yang dikirim melalui API ini tidak memicu notifikasi push, sehingga pengguna yang App-nya tertutup akan melihat pesan Anda saat membukanya berikutnya. Pengguna yang App-nya terhubung langsung melihatnya.
<!-- faq:end -->