AeroNyx Chat API Entegrasyon Kılavuzu

11 dk okuma

AeroNyx'in uçtan uca şifreli mesajlarını HTTPS üzerinden gönderin ve alın: istek imzalama, çalıştırılabilir bir hızlı başlangıç, mühürlü zarf biçimi, hata kodları ve test vektörleri.

AeroNyx Chat API, bir sunucunun, botun veya uygulamanın AeroNyx kullanıcılarıyla uçtan uca şifreli birebir mesajları sıradan HTTPS üzerinden göndermesini ve almasını sağlar. AeroNyx Gizlilik Ağı'nı, VPN'i, onion yönlendirmeyi veya anonim posta kutusunu kullanmaz.

Gönderim anında gerçekleşir: alıcının App'i bağlıysa mesajınız hemen ulaşır. Bu API üzerinden alım yoklama (polling) ile çalışır: GET /inbox/ bekleyen ne varsa onu döndürür ve isteği asla açık tutmaz. Gerçek zamanlı almak için aynı kimliği Chat Relay WebSocket'ine (wss://api.aeronyx.network/ws/relay/) bağlayın. Aşağıdaki "Gerçek zamanlı teslimat" bölümüne bakın.

Temel URL: https://api.aeronyx.network/api/relay/developer/v1

Nasıl çalışır

  1. Bir kimlik oluşturun. Bir Ed25519 anahtar çifti sizin sohbet kimliğinizdir. 64 küçük harfli onaltılık karakter olarak yazılan 32 baytlık genel anahtar, sizin sohbet adresinizdir. AeroNyx hesabı gerekmez.
  2. Her isteği imzalayın. Her çağrı, anahtarınızla imzalanmış bir Authorization üstbilgisi taşır (Kimlik doğrulama).
  3. Her mesajı mühürleyin. Mesajı kendi tarafınızda şifreleyip imzalayın ve yalnızca ortaya çıkan zarfı gönderin (Mesaj zarfı).
  4. POST /messages/ ile gönderin.
  5. GET /inbox/ ile alın, her mesajın şifresini çözüp saklayın, ardından POST /acks/ ile onaylayın.
Uç noktaKimlik doğrulamaAmaç
GET /capabilities/yokHizmet sınırları ve desteklenen özellikler
POST /messages/gerekliMühürlü bir mesaj gönderir
GET /inbox/gerekliOnaylanmamış mesajları getirir
POST /acks/gerekliSakladığınız mesajları onaylar

Hızlı başlangıç

Bu betik, yeni bir kimlikten diğerine mesaj gönderir, mesajı geri okur, şifresini çözer ve onaylar. Aşağıdaki referans uygulamayı (Python) aeronyx_chat.py olarak kaydedin, bağımlılıkları pip install pynacl requests ile kurun ve çalıştırın.

python
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())

Kimlik doğrulama

GET /capabilities/ dışındaki her uç nokta şu üstbilgiyi gerektirir:

text
Authorization: AeroNyx-Chat-V1 <public-key-hex>:<unix-seconds>:<nonce-hex>:<signature-hex>
  • public-key-hex, 64 karakterlik sohbet adresinizdir.
  • unix-seconds, sunucu saatinin 60 saniye içinde olmalıdır.
  • nonce-hex, 32 küçük harfli onaltılık karakter olarak yazılan 16 rastgele bayttır. Her nonce yalnızca bir kez kullanılabilir.
  • signature-hex, aşağıdaki 32 baytlık özet üzerinde alınmış, 128 onaltılık karakter olarak yazılan bir Ed25519 imzasıdır:
text
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, gönderildiği haliyle tam istek yoludur; /api/relay/developer/v1 önekini ve varsa sorgu dizesini içerir. Örneğin:

İstekİmzalanacak PATH_WITH_QUERY
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, istek gövdesinin tam olarak gönderildiği haliyle baytlarıdır. Bu nedenle JSON'daki boşluklar ve anahtar sırası önemlidir: bir kez serileştirin, o baytları imzalayın ve aynı baytları gönderin. Gövdesi olmayan bir istek, boş dizenin SHA-256 değerini imzalar.

Kimlik doğrulama test vektörü

İmzalama kodunuzu API'yi çağırmadan önce denetlemek için bunu kullanın. Anahtar yalnızca test içindir; gerçek mesajlar için asla kullanmayın.

json
{
  "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"
}

Ed25519 imzaları deterministiktir; bu nedenle aynı girdiler tam olarak bu signature_hex değerini vermelidir. body_sha256_hex değeriniz farklıysa gövde baytlarınız farklıdır.

401 hatası sorun giderme

API, her kimlik doğrulama hatasına aynı 401 yanıtını verir; bu yüzden şunları sırayla kontrol edin:

  1. Saat: unix-seconds gerçek zamanın 60 saniye içindedir.
  2. Nonce her istek için yenidir, 16 bayttır ve 32 küçük harfli onaltılık karakter olarak yazılmıştır.
  3. İmzalanan yol /api/relay/developer/v1 önekini ve sorgu dizesini içerir.
  4. İmzalanan yöntem büyük harflidir ve istekle eşleşir.
  5. İmzalanan gövde baytları, gönderdiğiniz baytlardır. İmzadan sonra JSON'u yeniden serileştirmeyin.
  6. Özetiniz ve imzanız yukarıdaki test vektörüyle eşleşir.

Uç noktalar

Hizmet bilgileri

GET /capabilities/ kimlik doğrulama gerektirmez ve geçerli sınırları ile desteklenen özellikleri döndürür.

Mesaj gönderme

POST /messages/

json
{
  "receiver_pubkey": "<64 lowercase hex characters>",
  "message_id": "018f5164-c1b2-7e2b-9d8a-2c60597f4c2f",
  "discriminant": 11,
  "sent_at": 1780000000,
  "payload_b64": "<sealed envelope, Base64>"
}

Beş alanın tümü zorunludur ve başka alan kabul edilmez.

  • receiver_pubkey, alıcının sohbet adresidir. Kendi adresinizden farklı olmalıdır.
  • message_id bir UUID'dir. Her mesaj için yeni bir rastgele UUID kullanın ve yeniden denemeler için gövdeyi aynen saklayın. Aynı message_id ile farklı alanlar göndermek 409 idempotency_conflict döndürür.
  • discriminant, bir AeroNyx App kullanıcısının görmesi gereken birebir mesaj için 11 değerindedir.
  • sent_at Unix saniyesidir ve sunucu saatinin 300 saniye içinde olmalıdır.
  • payload_b64, Mesaj zarfı bölümünde açıklanan mühürlü zarftır.

Aktarıcı yalnızca payload_b64 değerinin 1 bayt ile 191 KiB arasında geçerli bir Base64 olduğunu denetler. Bir zarfı diğer verilerden ayırt edemez; bu nedenle geçerli bir zarf olmayan yük burada kabul edilir ve alıcının App'i tarafından sessizce atılır. Zarfınızı altın vektöre karşı test edin.

Başarılı bir istek HTTP 202 döndürür:

json
{
  "message_id": "018f5164-c1b2-7e2b-9d8a-2c60597f4c2f",
  "accepted": true,
  "delivered_live": false
}

accepted, aktarıcının şifreli metni kalıcı olarak sakladığı anlamına gelir. delivered_live, aktarıcı mesajı canlı teslimata hatasız verdiğinde true olur. Alıcının hiç açık bağlantısı olmasa bile true olduğu için birinin çevrimiçi olup olmadığını göstermez. İki alan da alıcının mesajı çözdüğü veya görüntülediği anlamına gelmez; bunu gelen kutunuza düşen teslim alındısı gösterir ("Gelen kutusundaki olaylar" bölümüne bakın).

Gelen kutusunu getirme

GET /inbox/?limit=50

Onaylanmamış en fazla 100 olay döndürür. Aynı olay, bir zaman aşımından veya yeniden başlatmadan sonra yeniden görünebilir.

json
{
  "messages": [{
    "sender_pubkey": "<hex>",
    "discriminant": 11,
    "payload_b64": "<ciphertext>",
    "timestamp": 1780000000,
    "msg_id": "018f5164-c1b2-7e2b-9d8a-2c60597f4c2f"
  }],
  "has_more": false
}

has_more değeri true ise sakladıklarınızı onaylayın ve yeniden getirin.

Gelen kutusundaki olaylar

Gelen kutusu yalnızca yeni mesajları değil, aktarıcının kimliğiniz için kuyruğa aldığı her şeyi içerir. control_type içermeyen bir öğe mesajdır: payload_b64 alanını "Mesaj zarfı" bölümünde anlatıldığı gibi açın. control_type içeren bir öğe, bir mesajla ilgili bir olaydır. Herhangi bir şeyi çözmeden önce control_type alanını kontrol edin: alındıların payload_b64 alanı yoktur.

control_typeAnlamımsg_id
message_receiptAlıcının App'i gönderdiğiniz bir mesajı çözdü ve sakladı.Mesajınızın message_id değeri
message_readAlıcı mesajınızı okudu. Yalnızca okundu bilgisi açıksa gönderilir.Mesajınızın message_id değeri
message_editGönderen bir mesajı düzenledi (target_msg_id, aynı ID).Düzenlenen mesajın ID'si
message_revokeGönderen bir mesajı geri çekti.Geri çekilen mesajın ID'si
message_reactiontarget_msg_id mesajına bir tepki eklendi veya kaldırıldı.Tepkinin ID'si

Düzenlemeler, geri çekmeler ve tepkiler imzalıdır (payload_sig); biçimleri AeroNyx Sohbet Aktarıcısı İstemci Entegrasyonu içinde açıklanır. Bir ID'yi onaylamak, o ID'yi taşıyan tüm kuyruk öğelerini kaldırır; bu yüzden onaylamadan önce bir sayfanın tamamını işleyin. İşlemediğiniz olay türlerini de onaylayın; aksi hâlde yeniden döndürülürler.

Mesajları onaylama

POST /acks/

json
{
  "message_ids": ["018f5164-c1b2-7e2b-9d8a-2c60597f4c2f"]
}

Bir mesajı yalnızca doğruladıktan, şifresini çözdükten ve kalıcı depolamaya yazdıktan sonra onaylayın. Bilinmeyen kimlikler, yanıttaki acknowledged listesine dahil edilmez.

Teslimat modeli

  • Teslimat en az bir kez yapılır. Yinelenenleri control_type ile msg_id birlikte kullanarak ayıklayın: bir alındı, onayladığı mesajın ID'sini taşır.
  • Teslim edilmemiş şifreli metin 72 saat saklanır. Hizmeti kalıcı bir geçmiş olarak görmeyin.
  • Başarısız bir gönderimi, tamamen aynı gövde ve yeni bir kimlik doğrulama nonce'u ile yeniden deneyin.

Gerçek zamanlı teslimat

Gönderme

POST /messages/, mesajınızı alıcının açık olan her bağlantısına iletir ve aynı anda, alıcının App'i onaylayana kadar kuyrukta tutar. App'i bağlı olan alıcı mesajı hemen alır; çevrimdışı olan alıcı, App bir sonraki bağlandığında 72 saat içinde alır.

Bu API ile gönderilen mesajlar push bildirimi tetiklemez. App'i kapalı olan alıcıya uyarı gitmez; mesajı App'i bir sonraki açışında görür.

Yoklama ile alma

GET /inbox/, bekleyen bir şey olsun ya da olmasın hemen döner. Bir mesaj size ancak bir sonraki çağrınızda ulaşır; dolayısıyla gördüğünüz gecikme yoklama aralığınızdır. Sohbet amaçlı kullanımda WebSocket üzerinden alın.

Gerçek zamanlı alma

Aynı kimliği Chat Relay WebSocket'ine bağlayın. Hesap veya kayıt gerekmez.

  1. wss://api.aeronyx.network/ws/relay/ adresini açın ve aynı Ed25519 anahtarıyla, AeroNyx Sohbet Aktarıcısı İstemci Entegrasyonu içindeki oturum açma bölümünde anlatıldığı gibi oturum açın.
  2. Oturum açtıktan sonra aktarıcı önce sizin için kuyrukta kalan her şeyi yeniden gönderir (from_offline değeri true), ardından her yeni öğeyi geldiği anda gönderir. Mesajlar, gelen kutusundaki bir mesajla aynı alanlara sahip relay_envelope çerçeveleri olarak gelir; alındılar, düzenlemeler, geri çekmeler ve tepkiler ise type değeri yukarıdaki tablodaki control_type olan çerçeveler olarak gelir.
  3. Bir öğeyi sakladıktan sonra onaylayın:
json
{"type": "relay_offline_ack", "msg_id": "018f5164-c1b2-7e2b-9d8a-2c60597f4c2f"}

WebSocket ve GET /inbox/ aynı kuyruğu okur: birinde onaylanan öğe diğerinden de kaybolur ve POST /acks/ ikisi için de çalışır. WebSocket üzerinden alırken HTTPS ile göndermeye devam edebilirsiniz.

Sınırlar ve hatalar

SınırDeğer
Mühürlü yük191 KiB (Base64 JSON isteği istek sınırının altında kalır)
İstek gövdesi256 KiB
Gelen kutusu sayfası ve onay toplu işlemi100 öğe
Çevrimdışı saklama72 saat
DurumAnlamı
400 invalid_jsonGövde bir JSON nesnesi değildir.
400 invalid_requestGövde tam olarak beş alanı içermiyor, receiver_pubkey 64 küçük harfli onaltılık karakter değil veya kendi adresinize eşit, discriminant 1-255 aralığının dışında, message_id bir UUID değil ya da payload_b64 geçerli bir Base64 değil.
400 invalid_payloadÇözülmüş yük boştur veya 191 KiB'tan büyüktür.
400 message_expiredsent_at, sunucu saatinden 300 saniyeden fazla farklıdır.
401Kimlik doğrulama eksik, süresi dolmuş, geçersiz veya yeniden oynatılmış. Bkz. 401 hatası sorun giderme.
403Alıcının ilkesi bu göndericiyi reddetti.
409Idempotency çakışması veya aynı istek hâlâ işlenmekte.
429Gönderici hız sınırına ulaşıldı: her kimlik için saniyede 50 istek ve UTC gününde 100.000 istek. Retry-After başlığında belirtilen süre kadar bekleyin.
503Kalıcı teslimat veya kimlik doğrulama geçici olarak kullanılamıyor. Gövdeyi aynen, yeni bir nonce ile yeniden deneyin.

Bu kılavuzun kapsamadıkları

Bu kılavuz HTTPS üzerinden bire bir mesajları kapsar. Diğer her şey için aynı kimliği sohbet aktarıcısı WebSocket'ine bağlayın ve AeroNyx Sohbet Aktarıcısı İstemci Entegrasyonu makalesine bakın:

  • Grup mesajları, çevrimiçi durumu ve yazıyor göstergeleri.
  • Sesli mesajlar ve video dosyaları dahil şifreli ekler.
  • Sesli ve görüntülü aramalar. Sinyalleşme çerçeveleri bu makalede listelenmiştir, ancak eksiksiz bir arama entegrasyon kılavuzu (medya belirteci, davet imzası ve arama şifrelemesi) henüz yayımlanmadı.

Gizlilik ve görünürlük

Mesaj içeriği uçtan uca şifrelidir: AeroNyx'in hiçbir zaman elinde bulundurmadığı, göndericinin ve alıcının kimliklerinden türetilen bir anahtarla sizin tarafınızda mühürlenir ve API yalnızca şifreli metni saklar ve iletir. Mesajları teslim edebilmek için hizmet; gönderici ve alıcı genel anahtarlarını, mesaj zamanını, boyutunu, teslimat durumunu ve her isteğin IP adresini görebilir. HTTPS bu meta verileri hizmetten gizlemez.

Mesaj zarfı

Bir AeroNyx App kullanıcısının görmesi gereken mesajlar discriminant: 11 ve payload_b64 içinde uçtan uca mühürlü bir zarf kullanır. Aktarıcı anahtarı veya düz metni asla görmez. Tüm tamsayılar little-endian'dır.

1. Paylaşılan anahtar

Her iki taraf da Ed25519 sohbet kimliklerinden aynı 32 baytlık anahtarı türetir:

text
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)

libsodium ile bu, crypto_sign_ed25519_sk_to_curve25519, crypto_sign_ed25519_pk_to_curve25519 ve crypto_scalarmult ile ardından HKDF'dir. Göndericiler her zaman HKDF anahtarını kullanır. Çok eski App sürümleri doğrudan raw ile gönderiyordu; bu nedenle HKDF anahtarıyla şifre çözme başarısız olursa alıcı raw değerini deneyebilir.

2. Düz metin

Bir metin mesajı, kendi UTF-8 metnidir. App'ten gelen ve ek, yanıt veya iletme taşıyan mesajlar, "type": "aeronyx_message" içeren ve bir text alanı bulunan bir JSON nesnesidir; geri kalanını desteklemiyorsanız text alanını görüntüleyin.

3. Şifreleme

Taze rastgele 24 baytlık nonce ile ve ilişkili veri olmadan XChaCha20-Poly1305. Aşağıdaki ciphertext, 16 baytlık etiketi de içeren şifre çıktısıdır.

4. İmza

Gönderici şu 121 baytı Ed25519 ile imzalar:

text
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. Zarf

text
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, bu baytların standart Base64 kodlamasıdır. content_type alanının imzalanan veride 1 bayt, zarfta ise 4 bayt olduğuna dikkat edin.

Alma

Zarfı ayrıştırın, receiver_pubkey değerinin sizin kimliğiniz olduğunu denetleyin, imzayı zarfın sender_pubkey değerine karşı doğrulayın, ardından o göndericiden türetilen anahtarla şifreyi çözün. Herhangi bir adım başarısız olursa mesajı atın. App, timestamp değeri kendi saatinden 300 saniyeden fazla farklı olan canlı mesajları atar.

Referans uygulama (Python)

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"),
    }

Altın vektör

Sabit anahtarlar, nonce, kimlik ve zaman; yalnızca test içindir. Bu anahtarları asla kullanmayın.

json
{
  "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))) tam olarak bu payload_b64 değerini üretmeli ve open_(bob, payload_b64) düz metni döndürmelidir. Bu vektör, AeroNyx App'in kendi anahtar anlaşması, imzalayıcısı ve şifresine karşı ve düğümün sıkı zarf çözücüsüne karşı doğrulanmıştır.

<!-- faq:start -->

Sık sorulan sorular

AeroNyx Chat API nedir?

Bir sunucudan, bottan veya uygulamadan AeroNyx kullanıcılarıyla uçtan uca şifreli birebir mesaj alışverişi yapmak için kullanılan, sürümlü bir HTTPS API'sidir. Dört uç noktası vardır: capabilities, gönderme, gelen kutusu ve onaylama. Yalnızca mühürlü zarfları taşır; içerik sizin tarafınızda şifrelenir.

API'yi kullanmak için AeroNyx hesabına ihtiyacım var mı?

Hayır. Kimlik, bir Ed25519 anahtar çiftidir. Onu yerel olarak oluşturun ve her isteği onunla imzalayın; genel anahtar sizin sohbet adresinizdir.

/messages/ yolunu mu yoksa tam yolu mu imzalarım?

Gönderildiği haliyle tam yolu; /api/relay/developer/v1 ile başlar ve varsa sorgu dizesini de içerir. Yalnızca /messages/ imzalamak 401 üretir. Bu sayfadaki kimlik doğrulama test vektörü tam girdileri gösterir.

Neden 401 alıyorum?

Her kimlik doğrulama hatası aynı 401 yanıtını döndürür. Olağan nedenler; 60 saniyeden fazla sapan bir saat, yeniden kullanılmış bir nonce, gönderilenden farklı bir imzalanan yol veya gövde ya da imzalanan veride küçük harfli bir HTTP yöntemidir. 401 sorun giderme bölümündeki kontrol listesini uygulayın ve özetinizi test vektörüyle karşılaştırın.

API neden 202 döndürüyor ama mesaj AeroNyx App'te hiç görünmüyor?

Aktarıcı geçerli her Base64 yükünü kabul eder, ancak App yalnızca doğrulayıp şifresini çözebildiği mühürlü bir zarfı gösterir. payload_b64 değerinizi bu sayfadaki altın vektöre karşı denetleyin, zarfın receiver_pubkey değerinin alıcı olduğundan emin olun ve zarf zaman damgasını geçerli zamanın 300 saniyesi içinde tutun. Base64 olarak kodlanmış düz metin göndermek kabul edilir ama asla görüntülenmez.

API neden invalid_request döndürüyor?

En yaygın nedenler; kendi genel anahtarınıza göndermek, belgelenen beş alanı tam olarak içermeyen bir gövde, UUID olmayan bir message_id veya 1-255 aralığının dışında bir discriminant değeridir. Sınırlar ve hatalar bölümündeki hata tablosuna bakın.

AeroNyx bu API üzerinden gönderilen mesajları okuyabilir mi?

Hayır. Zarflar, AeroNyx'in hiçbir zaman elinde bulundurmadığı, göndericinin ve alıcının kimliklerinden türetilen bir anahtarla şifrelenir. Hizmet; genel anahtarlar, zamanlar, boyutlar ve IP adresleri gibi teslimat meta verilerini görebilir.

Teslim edilmemiş mesajlar ne kadar süre saklanır?

En fazla 72 saat. Her mesajı yalnızca şifresini çözüp sakladıktan sonra onaylayın; onaylanmamış mesajlar yeniden teslim edilebilir.

Hangi programlama dillerini kullanabilirim?

Ed25519, X25519, HKDF-SHA256 ve XChaCha20-Poly1305 destekleyen her dili; bu, çoğu dil için libsodium bağlayıcılarını da kapsar. Bu sayfa eksiksiz bir Python referans uygulaması ve çalıştırılabilir bir hızlı başlangıç içerir.

AeroNyx Chat API gerçek zamanlı mı?

Gönderim öyle: App'i bağlı olan alıcı mesajınızı hemen alır. HTTPS üzerinden alım yoklama ile çalışır; yeni mesajları bir sonraki GET /inbox/ çağrınızda görürsünüz. Gerçek zamanlı almak için aynı kimliği wss://api.aeronyx.network/ws/relay/ adresindeki Chat Relay WebSocket'ine bağlayın; aynı kuyruğu okur.

Gönderdiğim mesaj için AeroNyx kullanıcısına bildirim gider mi?

Şu anda gitmez. Bu API ile gönderilen mesajlar push bildirimi tetiklemez; App'i kapalı olan kullanıcı mesajınızı App'i bir sonraki açışında görür. App'i bağlı olan kullanıcı ise hemen görür.

<!-- faq:end -->