Skip to main content
This is a security-focused overview of Crosslink’s design. It is not a formal audit report. Crosslink provides transport-layer security primitives; your application must implement application-layer security (input validation, rate limiting, audit logging) independently.

Security principles

Crosslink is built on four principles:
  1. End-to-end encryption — No intermediary can read or modify traffic
  2. Forward secrecy — Compromising a long-term key does not expose past sessions
  3. Trust minimization — Services are untrusted; the client’s browser/OS is the trust boundary
  4. Capability-based authorization — Declarative, host-authored, no ambient authority

Cryptographic primitives

What the relay and signaling servers see

Authentication flow

Threat model

In scope

Out of scope

Pairing security

Single-use codes

Each pairing code can only be used once. The code is:
  • 9 digits (1 billion possible values)
  • Valid for 2 minutes
  • Tied to the host’s fingerprint
  • Bound to specific capabilities

SAS verification

Short Authentication String (SAS) digits are derived from:
  • The host’s Ed25519 identity public key
  • The client’s Ed25519 identity public key
  • The application ID
Both parties must see the same digits. If they don’t, a MITM is detected.

Fingerprint pinning

The QR code includes the first 16 hex characters of the host’s Ed25519 fingerprint. The client verifies this against the actual host public key during pairing.
If the fingerprint in the QR code does not match the host’s actual fingerprint, pairing must be rejected. This is the primary MITM defense during pairing.

Session security

Forward secrecy

Every session uses fresh ephemeral X25519 keys. Compromising the host’s long-term identity key does not expose past session content.

Session binding

Sessions are bound to:
  • The specific host identity
  • The specific client identity
  • The capabilities granted during pairing
  • The session’s ephemeral key material

Frame integrity

Every encrypted frame includes:
  • A 24-byte XChaCha nonce (random, unique per frame)
  • A Poly1305 authentication tag (detects tampering)
  • Sequence numbers (detects replay/reordering)

Secret storage security

Host (Node.js)

The SDK tries backends in order:
  1. keytar — OS keychain (macOS Keychain, Linux libsecret, Windows Credential Vault), if the optional keytar package is installed by the host application. Crosslink does not depend on it or install it for you.
  2. Electron safeStorage — Same vaults via Electron bindings
  3. Encrypted file — AES-256-GCM with scrypt-derived key
  4. Plaintext file — Requires explicit allowPlaintextFallback: true
The plaintext fallback is not selected automatically. A host that silently degrades to plaintext is worse than one that refuses to start.

Client (Browser)

The SDK tries backends in order:
  1. IndexedDB + AES-256-GCM — Non-extractable WebCrypto key
  2. localStorage — Plaintext fallback
  3. Memory — Ephemeral, lost on reload
The non-extractable WebCrypto key is not a vault. A script running on the origin can still use the key to decrypt. It prevents copy-and-leave attacks, not code-execution attacks.

Security checklist for production

  • Use HTTPS for all signaling/relay endpoints
  • Configure pairing limits and relay channel/client/byte/bandwidth quotas
  • Implement application-specific rate limiting in RPC handlers
  • Validate all RPC inputs (schema validation)
  • Log security-relevant events (pairing, capability grants, errors)
  • Set appropriate capability TTLs
  • Review granted capabilities before approving pairing
  • Monitor for anomalous connection patterns
  • Keep the SDK updated to the latest version
  • Use OS keychain for host secret storage (not plaintext)
  • Use CrosslinkClient.create() for browser clients (encrypted storage)
  • Use hybridPq: "required" only after every deployed peer supports it