Guia de integração da API de chat da AeroNyx
Envie e receba mensagens da AeroNyx com criptografia de ponta a ponta por HTTPS: assinatura de requisições, um início rápido executável, o formato do envelope selado, códigos de erro e vetores de teste.
A API de chat da AeroNyx permite que um servidor, um bot ou um aplicativo envie e receba mensagens individuais com criptografia de ponta a ponta de usuários da AeroNyx por meio de HTTPS comum. Ela não usa a AeroNyx Privacy Network, a VPN, o roteamento onion nem a caixa de correio anônima.
O envio é imediato: se o App do destinatário estiver conectado, a mensagem chega na hora. O recebimento por esta API funciona por polling: GET /inbox/ retorna o que estiver aguardando e nunca mantém a requisição aberta. Para receber em tempo real, conecte a mesma identidade ao WebSocket do Chat Relay (wss://api.aeronyx.network/ws/relay/). Veja a seção "Entrega em tempo real" abaixo.
URL base: https://api.aeronyx.network/api/relay/developer/v1
Como funciona
- Crie uma identidade. Um par de chaves Ed25519 é a sua identidade de chat. A chave pública de 32 bytes, escrita como 64 caracteres hexadecimais em minúsculas, é o seu endereço de chat. Não é necessária nenhuma conta da AeroNyx.
- Assine cada requisição. Cada chamada leva um cabeçalho
Authorizationassinado com a sua chave (Autenticação). - Sele cada mensagem. Criptografe e assine a mensagem do seu lado e envie apenas o envelope resultante (Envelope da mensagem).
- Envie com
POST /messages/. - Receba com
GET /inbox/, descriptografe e armazene cada mensagem e, em seguida, confirme-a comPOST /acks/.
| Endpoint | Autenticação | Finalidade |
|---|---|---|
GET /capabilities/ | nenhuma | Limites do serviço e recursos compatíveis |
POST /messages/ | obrigatória | Enviar uma mensagem selada |
GET /inbox/ | obrigatória | Buscar mensagens não confirmadas |
POST /acks/ | obrigatória | Confirmar as mensagens que você armazenou |
Início rápido
Este script envia uma mensagem de uma identidade nova para outra, lê a mensagem
de volta, a descriptografa e a confirma. Salve a
implementação de referência abaixo como
aeronyx_chat.py, instale as dependências com pip install pynacl requests
e execute o script.
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())
Autenticação
Todos os endpoints, exceto GET /capabilities/, exigem este cabeçalho:
Authorization: AeroNyx-Chat-V1 <public-key-hex>:<unix-seconds>:<nonce-hex>:<signature-hex>
public-key-hexé o seu endereço de chat de 64 caracteres.unix-secondsdeve estar a até 60 segundos do relógio do servidor.nonce-hexsão 16 bytes aleatórios escritos como 32 caracteres hexadecimais em minúsculas. Cada nonce só pode ser usado uma vez.signature-hexé uma assinatura Ed25519, escrita como 128 caracteres hexadecimais, sobre este digest de 32 bytes:
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 é o caminho completo da requisição como enviado, incluindo o
prefixo /api/relay/developer/v1 e qualquer query string. Por exemplo:
| Requisição | PATH_WITH_QUERY a assinar |
|---|---|
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 são os bytes do corpo da requisição exatamente como
enviados. Por isso, os espaços em branco e a ordem das chaves do JSON importam:
serialize uma única vez, assine esses bytes e envie esses mesmos bytes. Uma
requisição sem corpo assina o SHA-256 da string vazia.
Vetor de teste de autenticação
Use-o para verificar o seu código de assinatura antes de chamar a API. A chave serve apenas para testes; nunca a use em mensagens reais.
{
"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"
}
As assinaturas Ed25519 são determinísticas, portanto as mesmas entradas devem
gerar exatamente este signature_hex. Se o seu body_sha256_hex for diferente,
os bytes do seu corpo são diferentes.
Solução de problemas do 401
A API responde a toda falha de autenticação com o mesmo 401, portanto verifique
os itens abaixo na ordem:
- O relógio:
unix-secondsestá a até 60 segundos do horário real. - O nonce é novo a cada requisição, tem 16 bytes e está escrito como 32 caracteres hexadecimais em minúsculas.
- O caminho assinado inclui
/api/relay/developer/v1e a query string. - O método assinado está em maiúsculas e corresponde à requisição.
- Os bytes do corpo assinado são os bytes que você envia. Não serialize o JSON novamente depois de assinar.
- O seu digest e a sua assinatura coincidem com o vetor de teste acima.
Endpoints
Informações do serviço
GET /capabilities/ não precisa de autenticação e retorna os limites atuais e os
recursos compatíveis.
Enviar uma mensagem
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>"
}
Todos os cinco campos são obrigatórios e nenhum outro é permitido.
receiver_pubkeyé o endereço de chat do destinatário. Ele deve ser diferente do seu próprio endereço.message_idé um UUID. Use um novo UUID aleatório para cada mensagem e guarde o corpo exato para novas tentativas. Enviar o mesmomessage_idcom campos diferentes retorna409 idempotency_conflict.discriminanté11para uma mensagem individual que um usuário do AeroNyx App deve ver.sent_até em segundos Unix e deve estar a até 300 segundos do horário do servidor.payload_b64é o envelope selado descrito em Envelope da mensagem.
O relay verifica apenas se payload_b64 é um Base64 válido de 1 byte a 191 KiB.
Ele não consegue distinguir um envelope de outros dados, portanto um payload que
não seja um envelope válido é aceito aqui e descartado silenciosamente pelo App
do destinatário. Teste o seu envelope com o vetor de referência.
Uma requisição bem-sucedida retorna HTTP 202:
{
"message_id": "018f5164-c1b2-7e2b-9d8a-2c60597f4c2f",
"accepted": true,
"delivered_live": false
}
accepted significa que o relay armazenou o texto cifrado de forma durável. delivered_live é true quando o relay passou a mensagem para a entrega ao vivo sem erro. Ele é true mesmo quando o destinatário não tem nenhuma conexão aberta, então não indica se alguém está online. Nenhum dos dois campos significa que o destinatário decifrou ou exibiu a mensagem; quem indica isso é um recibo de entrega na sua caixa de entrada (veja "Eventos na caixa de entrada").
Buscar a caixa de entrada
GET /inbox/?limit=50
Retorna até 100 eventos não confirmados. O mesmo evento pode aparecer novamente após um timeout ou uma reinicialização.
{
"messages": [{
"sender_pubkey": "<hex>",
"discriminant": 11,
"payload_b64": "<ciphertext>",
"timestamp": 1780000000,
"msg_id": "018f5164-c1b2-7e2b-9d8a-2c60597f4c2f"
}],
"has_more": false
}
Se has_more for true, confirme o que você armazenou e busque novamente.
Eventos na caixa de entrada
A caixa de entrada guarda tudo o que o relay enfileirou para a sua identidade, não só mensagens novas. Um item sem control_type é uma mensagem: abra payload_b64 como descrito em "Envelope da mensagem". Um item com control_type é um evento sobre uma mensagem. Verifique control_type antes de decifrar qualquer coisa: recibos não têm payload_b64.
control_type | Significado | msg_id |
|---|---|---|
message_receipt | O App do destinatário decifrou e armazenou uma mensagem que você enviou. | O message_id da sua mensagem |
message_read | O destinatário leu a sua mensagem. Enviado só quando ele tem recibos de leitura ativados. | O message_id da sua mensagem |
message_edit | O remetente editou uma mensagem (target_msg_id, o mesmo ID). | O ID da mensagem editada |
message_revoke | O remetente retirou uma mensagem. | O ID da mensagem retirada |
message_reaction | Uma reação a target_msg_id foi adicionada ou removida. | O ID da reação |
Edições, retiradas e reações são assinadas (payload_sig); os formatos estão descritos em Integração de clientes com o AeroNyx Chat Relay. Confirmar um ID remove todos os itens na fila que o carregam, então processe a página inteira antes de confirmar. Confirme também os tipos de evento que você não trata; caso contrário, eles voltam.
Confirmar mensagens
POST /acks/
{
"message_ids": ["018f5164-c1b2-7e2b-9d8a-2c60597f4c2f"]
}
Confirme uma mensagem somente depois de validá-la, descriptografá-la e gravá-la
em armazenamento durável. IDs desconhecidos ficam de fora da lista acknowledged
na resposta.
Modelo de entrega
- A entrega é pelo menos uma vez. Elimine duplicatas por
control_typejunto commsg_id: um recibo carrega o ID da mensagem que ele confirma. - O texto cifrado não entregue é mantido por 72 horas. Não trate o serviço como um histórico permanente.
- Repita um envio que falhou com exatamente o mesmo corpo e um nonce de autenticação novo.
Entrega em tempo real
Envio
POST /messages/ entrega a sua mensagem a todas as conexões que o destinatário tem abertas e, ao mesmo tempo, a mantém na fila até que o App do destinatário a confirme. Um destinatário com o App conectado recebe na hora; um que está offline recebe na próxima vez que o App se conectar, dentro de 72 horas.
Mensagens enviadas por esta API não disparam notificação push. Um destinatário com o App fechado não é avisado e verá a mensagem na próxima vez que abrir o App.
Recebimento por polling
GET /inbox/ retorna na hora, haja ou não algo aguardando. Uma mensagem só chega a você na próxima chamada, então o atraso que você vê é o seu intervalo de polling. Para conversas, receba pelo WebSocket.
Recebimento em tempo real
Conecte a mesma identidade ao WebSocket do Chat Relay. Não é preciso conta nem cadastro.
- Abra
wss://api.aeronyx.network/ws/relay/e faça login com a mesma chave Ed25519, como descrito na parte de login de Integração de clientes com o AeroNyx Chat Relay. - Após o login, o relay primeiro reenvia tudo o que ainda está na fila para você (
from_offlineétrue) e depois envia cada item novo assim que ele chega. Mensagens chegam como framesrelay_envelopecom os mesmos campos de uma mensagem da caixa de entrada; recibos, edições, retiradas e reações chegam como frames cujotypeé ocontrol_typeda tabela acima. - Depois de armazenar um item, confirme-o:
{"type": "relay_offline_ack", "msg_id": "018f5164-c1b2-7e2b-9d8a-2c60597f4c2f"}
O WebSocket e GET /inbox/ leem a mesma fila: um item confirmado em um some do outro, e POST /acks/ vale para os dois. Você pode continuar enviando por HTTPS enquanto recebe pelo WebSocket.
Limites e erros
| Limite | Valor |
|---|---|
| Payload selado | 191 KiB (o JSON da requisição em Base64 fica abaixo do limite da requisição) |
| Corpo da requisição | 256 KiB |
| Página da caixa de entrada e lote de confirmações | 100 itens |
| Retenção offline | 72 horas |
| Status | Significado |
|---|---|
400 invalid_json | O corpo não é um objeto JSON. |
400 invalid_request | O corpo não contém exatamente os cinco campos, receiver_pubkey não tem 64 caracteres hexadecimais em minúsculas ou é igual ao seu próprio endereço, discriminant está fora do intervalo de 1 a 255, message_id não é um UUID, ou payload_b64 não é um Base64 válido. |
400 invalid_payload | O payload decodificado está vazio ou é maior que 191 KiB. |
400 message_expired | sent_at difere do horário do servidor em mais de 300 segundos. |
401 | A autenticação está ausente, expirada, inválida ou foi reutilizada (replay). Consulte Solução de problemas do 401. |
403 | A política do destinatário rejeitou este remetente. |
409 | Conflito de idempotência, ou uma requisição idêntica ainda está em andamento. |
429 | O limite de taxa do remetente foi atingido: 50 requisições por segundo e 100.000 por dia UTC para cada identidade. Aguarde o tempo indicado no cabeçalho Retry-After. |
503 | A entrega durável ou a autenticação está temporariamente indisponível. Repita o corpo exato com um nonce novo. |
O que este guia não cobre
Este guia cobre mensagens individuais por HTTPS. Para todo o resto, conecte a mesma identidade ao WebSocket do relay de chat e consulte Integração de clientes com o AeroNyx Chat Relay:
- Mensagens de grupo, presença e indicadores de digitação.
- Anexos criptografados, incluindo mensagens de voz e arquivos de vídeo.
- Chamadas de voz e vídeo. Os frames de sinalização estão listados nesse artigo, mas um guia completo de integração de chamadas (token de mídia, assinatura de convite e criptografia da chamada) ainda não foi publicado.
Privacidade e visibilidade
O conteúdo das mensagens tem criptografia de ponta a ponta: ele é selado do seu lado com uma chave derivada das identidades do remetente e do destinatário, que a AeroNyx nunca possui, e a API armazena e encaminha apenas o texto cifrado. Para entregar as mensagens, o serviço consegue ver as chaves públicas do remetente e do destinatário, o horário da mensagem, o tamanho, o estado de entrega e o endereço IP de cada requisição. O HTTPS não oculta esses metadados do serviço.
Envelope da mensagem
As mensagens que um usuário do AeroNyx App deve ver usam discriminant: 11 e um
envelope selado de ponta a ponta em payload_b64. O relay nunca vê a chave nem o
texto simples. Todos os inteiros são little-endian.
1. Chave compartilhada
Os dois lados derivam a mesma chave de 32 bytes a partir das suas identidades de chat Ed25519:
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)
Com a libsodium, isso corresponde a crypto_sign_ed25519_sk_to_curve25519,
crypto_sign_ed25519_pk_to_curve25519 e crypto_scalarmult, seguidos de
HKDF. Os remetentes sempre usam a chave HKDF. Versões muito antigas do App
enviavam com raw diretamente, portanto um destinatário pode tentar raw se a
chave HKDF falhar ao descriptografar.
2. Texto simples
Uma mensagem de texto é o seu próprio texto em UTF-8. As mensagens do App que
contêm anexos, respostas ou encaminhamentos são um objeto JSON com
"type": "aeronyx_message" e um campo text; exiba text se você não der
suporte ao restante.
3. Criptografia
XChaCha20-Poly1305 com um nonce aleatório novo de 24 bytes e sem dados
associados. ciphertext abaixo é a saída da cifra, incluindo a sua tag de 16
bytes.
4. Assinatura
O remetente assina estes 121 bytes com 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. Envelope
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 é o Base64 padrão desses bytes. Observe que content_type
tem 1 byte nos dados assinados, mas 4 bytes no envelope.
Recebimento
Analise o envelope, verifique se receiver_pubkey é a sua identidade, verifique a
assinatura com o sender_pubkey do envelope e, em seguida, descriptografe com a
chave derivada desse remetente. Descarte a mensagem se qualquer etapa falhar. O
App descarta mensagens ao vivo cujo timestamp esteja a mais de 300 segundos do
seu relógio.
Implementação de referência (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"),
}
Vetor de referência
Chaves, nonce, ID e horário fixos, apenas para testes. Nunca use estas chaves.
{
"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))) deve produzir exatamente este payload_b64, e
open_(bob, payload_b64) deve retornar o texto simples. Este vetor foi
verificado com o acordo de chaves, o assinador e a cifra do próprio AeroNyx App e
com o decodificador estrito de envelopes do nó.
Perguntas frequentes
O que é a API de chat da AeroNyx?
É uma API HTTPS versionada para trocar mensagens individuais com criptografia de ponta a ponta com usuários da AeroNyx a partir de um servidor, bot ou aplicativo. Ela tem quatro endpoints: capabilities, envio, caixa de entrada e confirmação. Ela transporta apenas envelopes selados; o conteúdo é criptografado do seu lado.
Preciso de uma conta da AeroNyx para usar a API?
Não. Um par de chaves Ed25519 é a identidade. Gere-o localmente e assine cada requisição com ele; a chave pública é o seu endereço de chat.
Eu assino /messages/ ou o caminho completo?
O caminho completo como enviado, começando com /api/relay/developer/v1, mais a query string, se houver. Assinar apenas /messages/ resulta em um 401. O vetor de teste de autenticação desta página mostra as entradas exatas.
Por que recebo 401?
Toda falha de autenticação retorna o mesmo 401. As causas habituais são um relógio com mais de 60 segundos de diferença, um nonce reutilizado, um caminho ou corpo assinado diferente do que foi enviado, ou um método HTTP em minúsculas nos dados assinados. Percorra a lista de verificação em Solução de problemas do 401 e compare o seu digest com o vetor de teste.
Por que a API retorna 202, mas a mensagem nunca aparece no AeroNyx App?
O relay aceita qualquer payload em Base64 válido, mas o App só exibe um envelope selado que consiga verificar e descriptografar. Confira o seu payload_b64 com o vetor de referência desta página, certifique-se de que o receiver_pubkey do envelope seja o destinatário e mantenha o timestamp do envelope a até 300 segundos do horário atual. Enviar texto simples codificado em Base64 é aceito, mas nunca é exibido.
Por que a API retorna invalid_request?
As causas mais comuns são enviar para a sua própria chave pública, um corpo que não contém exatamente os cinco campos documentados, um message_id que não é um UUID, ou um discriminant fora do intervalo de 1 a 255. Consulte a tabela de erros em Limites e erros.
A AeroNyx consegue ler as mensagens enviadas por esta API?
Não. Os envelopes são criptografados com uma chave derivada das identidades do remetente e do destinatário, que a AeroNyx nunca possui. O serviço consegue ver metadados de entrega, como chaves públicas, horários, tamanhos e endereços IP.
Por quanto tempo as mensagens não entregues são mantidas?
Por até 72 horas. Confirme cada mensagem somente depois de descriptografá-la e armazená-la; as mensagens não confirmadas podem ser entregues novamente.
Quais linguagens de programação posso usar?
Qualquer linguagem com Ed25519, X25519, HKDF-SHA256 e XChaCha20-Poly1305, o que inclui bindings da libsodium para a maioria das linguagens. Esta página inclui uma implementação de referência completa em Python e um início rápido executável.
A API de chat da AeroNyx é em tempo real?
O envio é: um destinatário com o App conectado recebe a sua mensagem na hora. O recebimento por HTTPS é por polling, então você vê mensagens novas na próxima chamada a GET /inbox/. Para receber em tempo real, conecte a mesma identidade ao WebSocket do Chat Relay em wss://api.aeronyx.network/ws/relay/; ele lê a mesma fila.
Um usuário da AeroNyx recebe notificação por uma mensagem que eu envio?
No momento, não. Mensagens enviadas por esta API não disparam notificação push, então um usuário com o App fechado verá a sua mensagem na próxima vez que abri-lo. Um usuário com o App conectado vê na hora.
<!-- faq:end -->