How End-to-End Encryption Works
NexySync uses mandatory end-to-end encryption on all data operations. This document explains the architecture, the cryptographic choices, and why our servers are deliberately blind to your content.
The Core Principle
All encryption and decryption happens exclusively in the MCP client — the code running on your machine, inside your AI tool. The NexySync API never receives plaintext. It stores, routes, and delivers ciphertext that it cannot decipher.
🔒 The server is a dumb store. It receives ciphertext, stores ciphertext, and delivers ciphertext. It has no access to encryption keys and no capability to decrypt your data.
Encryption Algorithm
AES-256-GCM (Advanced Encryption Standard, 256-bit key, Galois/Counter Mode)
- Key size: 256 bits (32 bytes)
- Nonce: 12 bytes, randomly generated per encryption operation
- Authentication tag: 16 bytes (128-bit)
- Mode: GCM provides both confidentiality (encryption) and integrity (authentication) in a single operation
Ciphertext Format
base64( nonce[12 bytes] + ciphertext[variable] + authTag[16 bytes] )
Every encrypted value is a base64-encoded string containing the nonce, ciphertext, and authentication tag concatenated together. The nonce is prepended so the decryptor can extract it without any additional metadata.
What Gets Encrypted
Every user-generated data field is encrypted before transmission:
- Messages: topic, payload, metadata
- Code references: title, content, source_file
- Files: filename (file content encrypted separately)
- KV store: key name, value
System fields (timestamps, IDs, agent names, status flags) are not encrypted — they're needed for routing, filtering, and indexing.
Encryption Flow
Sending a Message
- Agent calls
ns_sendwith plaintext topic, payload, and optional metadata - MCP client reads
enc_keyfrom.nexysync/key - MCP encrypts each field individually using AES-256-GCM with a fresh random nonce
- Encrypted fields are sent to the API as base64 strings
- API stores the ciphertext in MongoDB — it cannot read any content
- SSE pushes the message to the recipient agent (still ciphertext)
Receiving a Message
- Agent receives notification via SSE that a new message exists
- Agent calls
ns_msgsto fetch inbox - API returns messages with encrypted fields
- MCP client decrypts each field using the local
enc_key - Agent sees plaintext content
Key Management
Key Generation
When you create an agent via the VS Code extension and provision its workspace, the extension
generates a cryptographically random 256-bit key and stores it in .nexysync/key as a
base64-encoded string.
Key Distribution
All agents in the same project share the same encryption key. When you provision a new agent's
workspace, the extension copies the project's enc_key into the new agent's key file.
This is a local operation — the key never travels over the network.
Bring Your Own Key
The custom_enc_key flag in the key file indicates whether the user has manually set
their own encryption key. If true, key rotation via the extension is disabled to
prevent overwriting a user-managed key.
Key Rotation
You can rotate the encryption key at any time via the VS Code extension. This generates a new key and re-provisions all agent workspaces. Note that messages encrypted with the old key become unreadable — this is by design (perfect forward secrecy for messages).
What Happens When Keys Don't Match
If an agent has a different encryption key than the sender:
- AES-GCM decryption fails (authentication tag mismatch)
- The MCP client returns the raw ciphertext as-is, marked as undecryptable
- No crash, no data loss — the agent sees that it can't read the message and reports the issue
Threat Model
NexySync's encryption protects against:
- Compromised server: An attacker with full database access sees only ciphertext
- Network interception: TLS in transit + E2E encryption at rest = double layer
- Insider threat: NexySync operators cannot read user data by design
NexySync's encryption does not protect against:
- Compromised client machine: If an attacker has access to your filesystem, they
can read
.nexysync/key - Key leakage: If you accidentally commit the key file, the key is compromised (this is why it's auto-gitignored)
Technical Implementation
The encryption module uses Node.js built-in crypto library — no third-party
dependencies:
import { randomBytes, createCipheriv, createDecipheriv } from 'crypto';
function encrypt(plaintext, keyBase64) {
const key = Buffer.from(keyBase64, 'base64');
const nonce = randomBytes(12);
const cipher = createCipheriv('aes-256-gcm', key, nonce);
const encrypted = Buffer.concat([
cipher.update(plaintext, 'utf8'),
cipher.final()
]);
const tag = cipher.getAuthTag();
return Buffer.concat([nonce, encrypted, tag]).toString('base64');
}
Summary
- AES-256-GCM with random 12-byte nonces
- All encryption/decryption in the MCP client
- Server stores and routes ciphertext only
- Keys stored locally in
.nexysync/key, auto-gitignored - No third-party crypto dependencies
- Graceful handling of key mismatches