Keys & Encryption Model

Everything inside Nyxen is built around one uncompromising design: keys never leave the user’s device. This page explains how Nyxen handles encryption, key derivation, and relay logic—without requiring you to be a cryptographer to understand it.


Core Principles

Principle
Description

Client-side encryption

All encryption and decryption happens locally in the browser or client app.

No plaintext transmission

Nyxen’s servers see only ciphertext, metadata for routing, and TTLs.

Key derivation, not sharing

Each primitive (Dead Drop, Board, Capsule, etc.) derives unique subkeys from a root key using HKDF.

Zero knowledge

Nyxen cannot decrypt, inspect, or index your data.

Ephemeral keys

Keys are short-lived and discarded on expiry or burn.

[!NOTE] This model protects against data compromise at rest on Nyxen’s infrastructure. It does not protect against endpoint compromise—users must keep their devices secure.


The Encryption Stack

Algorithms

Layer
Algorithm
Purpose

Symmetric encryption

AES-GCM-256

Encrypts message, file, or content block. Provides confidentiality + integrity.

Key derivation

HKDF (SHA-256)

Derives unique subkeys for different components.

Key generation

Crypto.getRandomValues()

Produces random 256-bit keys in-browser.

Transport security

TLS 1.3 / DTLS-SRTP

Protects transport between client and Nyxen relays.

All cryptographic operations use Web Crypto API in the web client or libsodium equivalents in native builds.


Key Hierarchy

Nyxen uses a tree-like key structure.

Capsule Key (root)
 ├── Dead Drop Key
 ├── Board Key
 ├── File Drop Key
 ├── Signals Key
 ├── Ghost Code Key
 └── Spectre Voice Key

Each derived key is unique to its purpose.

Example (pseudo-code)

const capsuleKey = generateKey(256);

function deriveKey(rootKey, label) {
  return hkdf(rootKey, "nyxen-derivation", label);
}

// For each primitive:
const deadDropKey = deriveKey(capsuleKey, "dead-drop");
const boardKey    = deriveKey(capsuleKey, "ephemeral-board");
const fileKey     = deriveKey(capsuleKey, "file-drop");

[!TIP] Each component key is independent. Compromise of one does not reveal others.


Encryption Flow (Simplified)

1. Generate Key

Client generates a new AES-GCM key using browser cryptography.

const key = crypto.getRandomValues(new Uint8Array(32));

2. Encrypt Payload

Data is encrypted before leaving the client.

const { ciphertext, nonce, authTag } = encryptAESGCM(key, JSON.stringify(message));

3. Transmit Ciphertext

Relay receives only encrypted data + TTL.

{
  "capsule_id": "C-IR492",
  "ciphertext": "base64...",
  "nonce": "base64...",
  "ttl_seconds": 1800
}

4. Expire / Burn

On TTL or burn event:

  • relay deletes ciphertext;

  • clients wipe their keys and caches.

onExpire() {
  delete(ciphertext);
  clearKey();
}

5. Decrypt (Client-side)

Only peers with the same key can decrypt.

const message = decryptAESGCM(key, ciphertext, nonce);

Key Handling Rules

Rule
Behavior

Keys are generated per context

No global “account key.” Every Dead Drop, Capsule, etc. has its own.

Keys are never stored server-side

Relays only store encrypted payloads.

Keys can be shared manually

Users exchange keys out-of-band (e.g., another Nyxen channel, QR, etc.).

Keys are cleared on expiry

Client deletes keys when TTL ends or user burns manually.

No password recovery

Lose your key, lose access — by design.

[!WARNING] There is no “forgot my password” function in Nyxen. Losing the key means permanent data loss.


Relay Interaction

Nyxen relays are dumb couriers:

  • They route encrypted data between peers.

  • They enforce TTLs and purge expired content.

  • They never decrypt, inspect, or store long-term logs.

Relay Log Example (minimal):

{
  "capsule_id": "C-IR492",
  "message_id": "m13",
  "ttl_seconds": 600,
  "expiry": "2025-11-11T22:15:00Z"
}

After expiry, this record is deleted automatically.


Encryption and TTL Binding

Every encrypted payload carries TTL metadata:

Field
Description

ttl_seconds

How long ciphertext may exist

expires_at

Exact UTC expiration timestamp

burn

Boolean flag for manual destruction

context_id

Capsule or object linkage

When TTL or burn triggers:

  1. The relay deletes the ciphertext.

  2. Clients purge associated keys.

  3. UI displays “burn notice.”

if (now >= expires_at || burn) {
  clearLocalStorage();
  showNotice("This object has been destroyed.");
}

Example: Encrypted Message Structure

{
  "version": "1.0",
  "context": "dead-drop",
  "nonce": "A1B2C3...",
  "ciphertext": "V0dBQm9keS9lbnRyeQ==",
  "ttl_seconds": 1800,
  "created_at": "2025-11-11T21:45:00Z"
}

Architectural Diagram

[ User A ] --encrypt--> [ Relay (ciphertext only) ] --decrypt--> [ User B ]
         |                                                |
      TTL ends → client burn + relay purge → total deletion

Security Posture

Threat
Mitigated by

Server compromise

No plaintext on server

Relay metadata leak

Minimal routing data, no content

Key reuse

Per-context key derivation

Endpoint theft

Out of scope (user responsibility)

Data retention

TTL enforced + burn capability

Replay

Nonce + authTag validation per message

[!NOTE] Nyxen is resilient to data compromise on its infrastructure. It is not resistant to an infected client or user voluntarily leaking keys.


Future Considerations

Planned research and upgrades:

  • Post-quantum key exchange support (X25519 → Kyber hybrid)

  • Multi-device sync with client-side key escrow (optional, local-only)

  • Visual key-pair indicators for context awareness

  • Hardware-backed storage (WebAuthn integration)


Nyxen’s encryption model is not built for marketing language; it’s built for certainty. If you hold the key, you can read the data. If you don’t, you can’t. Even Nyxen can’t.

Last updated