Core Concepts

Encryption & Security

Noxy uses Kyber-based post-quantum encryption to ensure encrypted decision requests remain secure even against future quantum computing threats. The relay routes ciphertext and never sees the plaintext you send.

The quantum threat

Current encryption methods (RSA, ECC) that secure most of the internet today will be vulnerable to quantum computers. While large-scale quantum computers don't exist yet, the threat is real and imminent.

Organizations are already collecting encrypted data today with the intent to decrypt it once quantum computers become available — a strategy known as "harvest now, decrypt later."

How Kyber encryption works

  1. Key generation. Each user's device generates a Kyber public/private key pair resistant to quantum attacks.
  2. Encapsulation. The sender uses the recipient's public key to create an encrypted shared secret and ciphertext.
  3. Decapsulation. Only the recipient can use their private key to extract the shared secret and decrypt the message.

The trust boundary

Noxy is designed around a clear trust boundary. Two endpoints — your agent and the user's device — share a per-decision symmetric key derived through a post-quantum key encapsulation. Everything in between, including the relay, only ever sees ciphertext, the per-device envelope metadata, and lifecycle bookkeeping.

                      ciphertext only              
┌────────────┐   TLS   ┌────────────┐   TLS   ┌────────────┐
│ Agent SDK  │ ──────► │ Noxy relay │ ──────► │ Client SDK │
│  encrypt   │         │  (routes)  │         │  decrypt   │
└────────────┘         └────────────┘         └────────────┘

Cryptographic construction

The Agent SDK performs the following per recipient device, on your machine, before sending anything to the relay:

  1. Post-quantum KEM. Kyber768 (ML-KEM 768) is used to encapsulate a 32-byte shared secret to the device's pq_public_key. Output: a Kyber ciphertext and the shared secret.
  2. HKDF-SHA256. The shared secret is fed into HKDF to derive a 256-bit AES key.
  3. AES-256-GCM. The decision JSON is encrypted with the derived key and a fresh 12-byte random nonce. Authentication tag is included.
  4. Envelope. Only the Kyber ciphertext, the nonce, and the AES-GCM ciphertext leave the agent. None of the plaintext, none of the key material, and none of the random secret cross the wire.

On the device, the Client SDK reverses the process using the device's Kyber private key, which never leaves secure storage.

Post-quantum, today

The key-encapsulation step uses Kyber768 (ML-KEM 768), one of the NIST-selected post-quantum primitives. This means the per-decision symmetric key is protected against future quantum adversaries — even if an attacker stores ciphertext today and decrypts it years later, the Kyber capsule still resists them. The classical layer (AES-256-GCM) is added on top so the construction degrades gracefully if any single layer is weakened.

Security properties

  • Messages encrypted on the sender's device, decrypted only on the recipient's device
  • Network nodes cannot read message contents
  • Perfect forward secrecy for every message

NIST standardized

  • Kyber selected by NIST as the primary post-quantum standard
  • Extensively peer-reviewed by cryptographers
  • Battle-tested in real-world implementations

What the relay can see

  • Yes: your APP_ID, the identity (email, wallet, phone, or user_id) the decision is routed to, the device id and public keys, the ciphertext blob, and lifecycle metadata (timestamps, statuses, request and decision ids).
  • No: the plaintext of your payload, the user's free-text response, your APP_TOKEN, or any private key material.

App secrets and what they protect

SecretLives onUsed forIf leaked
APP_TOKENYour backend / agent processAuthorizing every Agent-API callAn attacker could route decisions and burn quota — rotate immediately.
APP_SIGNING_SECRETBundled with the client appHMAC for one-time device registrationAn attacker could register additional devices for known identities; payloads still cannot be decrypted without the device's private key.
APP_IDClient appIdentifying your application to the relayNot sensitive; visible by design.

Rotate when in doubt. APP_TOKEN and APP_SIGNING_SECRET can be rotated from the dashboard. Existing devices remain registered; only new registrations use the new secret.

Device keys

  • Each device generates its own classical and post-quantum key pairs at registration time.
  • Private keys never leave the device — Client SDKs store them in platform-native secure storage (Keychain on iOS, EncryptedSharedPreferences on Android, IndexedDB + AES-GCM in the browser).
  • You can call rotateKeys() at any time to generate a fresh pair; the relay re-publishes the new public keys for subsequent decisions.
  • revokeDevice() removes the device from the relay; queued decisions for that device are dropped.

Transport security

Every connection to the relay is TLS-terminated. Agent calls are gRPC over HTTP/2 with the Bearer token in authorization metadata. Client connections are gRPC bi-directional streams (mobile, server) or WebSockets over TLS (browser). Cleartext is only used in local development against a local relay.

Operational guarantees

  • Rate limits apply per app and per connection. Authenticated sessions get higher quotas than unauthenticated traffic.
  • Durable lifecycle. Decisions persist in the relay's durable store while in flight and are reaped after the TTL plus a small grace.
  • Per-request correlation. Every request you send includes a request_id that is returned in responses and surfaced in audit logs, making postmortems easy.

Recommendations

  • Keep APP_TOKEN in a secret manager. Never bundle it in client apps.
  • Treat APP_SIGNING_SECRET like a public-but-rate-limiting credential: bundle it with the client, but rotate when an app version is end-of-life.
  • Use user_id for non-PII identifiers when you don't need the relay to know an email or phone.
  • Pin client-side identity changes (e.g. case-normalize emails) at registration; once devices are registered the binding is immutable.