AeroNyx Chat API Entegrasyon Kılavuzu
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
- 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.
- Her isteği imzalayın. Her çağrı, anahtarınızla imzalanmış bir
Authorizationüstbilgisi taşır (Kimlik doğrulama). - Her mesajı mühürleyin. Mesajı kendi tarafınızda şifreleyip imzalayın ve yalnızca ortaya çıkan zarfı gönderin (Mesaj zarfı).
POST /messages/ile gönderin.GET /inbox/ile alın, her mesajın şifresini çözüp saklayın, ardındanPOST /acks/ile onaylayın.
| Uç nokta | Kimlik doğrulama | Amaç |
|---|---|---|
GET /capabilities/ | yok | Hizmet sınırları ve desteklenen özellikler |
POST /messages/ | gerekli | Mühürlü bir mesaj gönderir |
GET /inbox/ | gerekli | Onaylanmamış mesajları getirir |
POST /acks/ | gerekli | Sakladığı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.
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:
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:
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.
{
"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:
- Saat:
unix-secondsgerçek zamanın 60 saniye içindedir. - Nonce her istek için yenidir, 16 bayttır ve 32 küçük harfli onaltılık karakter olarak yazılmıştır.
- İmzalanan yol
/api/relay/developer/v1önekini ve sorgu dizesini içerir. - İmzalanan yöntem büyük harflidir ve istekle eşleşir.
- İmzalanan gövde baytları, gönderdiğiniz baytlardır. İmzadan sonra JSON'u yeniden serileştirmeyin.
- Ö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/
{
"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_idbir UUID'dir. Her mesaj için yeni bir rastgele UUID kullanın ve yeniden denemeler için gövdeyi aynen saklayın. Aynımessage_idile farklı alanlar göndermek409 idempotency_conflictdöndürür.discriminant, bir AeroNyx App kullanıcısının görmesi gereken birebir mesaj için11değerindedir.sent_atUnix 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:
{
"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.
{
"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_type | Anlamı | msg_id |
|---|---|---|
message_receipt | Alıcının App'i gönderdiğiniz bir mesajı çözdü ve sakladı. | Mesajınızın message_id değeri |
message_read | Alıcı mesajınızı okudu. Yalnızca okundu bilgisi açıksa gönderilir. | Mesajınızın message_id değeri |
message_edit | Gönderen bir mesajı düzenledi (target_msg_id, aynı ID). | Düzenlenen mesajın ID'si |
message_revoke | Gönderen bir mesajı geri çekti. | Geri çekilen mesajın ID'si |
message_reaction | target_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/
{
"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_typeilemsg_idbirlikte 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.
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.- Oturum açtıktan sonra aktarıcı önce sizin için kuyrukta kalan her şeyi yeniden gönderir (
from_offlinedeğeritrue), ardından her yeni öğeyi geldiği anda gönderir. Mesajlar, gelen kutusundaki bir mesajla aynı alanlara sahiprelay_envelopeçerçeveleri olarak gelir; alındılar, düzenlemeler, geri çekmeler ve tepkiler isetypedeğeri yukarıdaki tablodakicontrol_typeolan çerçeveler olarak gelir. - Bir öğeyi sakladıktan sonra onaylayın:
{"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ır | Değer |
|---|---|
| Mühürlü yük | 191 KiB (Base64 JSON isteği istek sınırının altında kalır) |
| İstek gövdesi | 256 KiB |
| Gelen kutusu sayfası ve onay toplu işlemi | 100 öğe |
| Çevrimdışı saklama | 72 saat |
| Durum | Anlamı |
|---|---|
400 invalid_json | Gövde bir JSON nesnesi değildir. |
400 invalid_request | Gö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_expired | sent_at, sunucu saatinden 300 saniyeden fazla farklıdır. |
401 | Kimlik doğrulama eksik, süresi dolmuş, geçersiz veya yeniden oynatılmış. Bkz. 401 hatası sorun giderme. |
403 | Alıcının ilkesi bu göndericiyi reddetti. |
409 | Idempotency çakışması veya aynı istek hâlâ işlenmekte. |
429 | Gö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. |
503 | Kalı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:
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:
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
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)
"""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.
{
"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.
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 -->