Payload Encryptor - Secure Hybrid Encryption for Client-Server Communication

Payload Encryptor - Secure Hybrid Encryption for Client-Server Communication

January 13, 2026

Learn how to implement end-to-end encryption between your client and server using the ECIES pattern.

What is Payload Encryptor?

Payload Encryptor is a secure hybrid encryption library that implements the ECIES (Elliptic Curve Integrated Encryption Scheme) pattern for client-server communication. It provides military-grade encryption with zero configuration.

Whether you're building a banking app, a healthcare platform, or any application that handles sensitive data, Payload Encryptor ensures your data is protected in transit.

Key Features

  • 🔐 Two-Way Encryption - Encrypts both requests (client to server) and responses (server to client)
  • 🌍 Universal Support - Works in Browsers, Node.js, Bun, Deno, and Cloudflare Workers
  • 🔄 Forward Secrecy - Uses new ephemeral keys for every message
  • Zero Configuration - No complex bundler setup or secrets transmitted over the wire

Security Specifications

Payload Encryptor uses industry-standard cryptographic algorithms:

  • Key Exchange: X25519 (Curve25519)
  • Encryption: ChaCha20-Poly1305 AEAD
  • Key Derivation: HKDF-SHA256
  • Authentication: Poly1305 MAC

Only public keys are transmitted - private keys and session keys NEVER leave their respective environments.

Installation

Get started by installing the package:

npm i payload-encryptor

Or with other package managers:

bun add payload-encryptor
yarn add payload-encryptor

Server-Side Setup

First, let's set up the server. The PayloadServer class handles session creation, decryption of incoming requests, and encryption of responses.

PayloadServer API

  • static create(): Promise<PayloadServer> - Initialize a server instance
  • createSession(): SessionInit - Generate session data (public key) to send to client
  • decrypt<T>(payload: EncryptedPayload): T - Decrypt incoming client request
  • encryptResponse(clientEphemeralPublic: string, data: any): EncryptedResponse - Encrypt response back to client

Server Implementation

import { PayloadServer } from 'payload-encryptor';

// Create the server instance
const server = await PayloadServer.create();

// Endpoint to provide session/public key to client
app.get('/session', () => {
  return server.createSession();
});

// Endpoint to receive encrypted data
app.post('/data', async (req) => {
  // Get the encrypted payload from request
  const encrypted = await req.json();
  
  // Decrypt the client's request
  const decrypted = server.decrypt(encrypted);
  console.log('Received:', decrypted);
  
  // Process the data and encrypt the response
  const response = server.encryptResponse(
    encrypted.clientEphemeralPublic,
    { result: 'success', data: 'Your secret response' }
  );
  
  return response;
});

Client-Side Setup

The PayloadClient class handles session management, encryption of outgoing requests, and decryption of server responses.

PayloadClient API

  • static create(): Promise<PayloadClient> - Initialize a client instance
  • setSession(session: SessionInit): void - Set the server's session/public key
  • getSession(): SessionInit | null - Retrieve current session data
  • hasSession(): boolean - Check if a session is established
  • encrypt(payload: string | object): EncryptedPayload - Encrypt a request payload
  • decryptResponse<T>(response: EncryptedResponse): T - Decrypt the server's response

Client Implementation

import { PayloadClient } from 'payload-encryptor';

// Create the client instance
const client = await PayloadClient.create();

// Fetch the session from the server
const session = await fetch('/session').then(r => r.json());

// Set the session (server's public key)
client.setSession(session);

// Encrypt your sensitive data
const encrypted = client.encrypt({
  data: 'This is my secret message',
  userId: 12345
});

// Send the encrypted payload
const encryptedResponse = await fetch('/data', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(encrypted)
}).then(r => r.json());

// Decrypt the server's response
const response = client.decryptResponse(encryptedResponse);
console.log('Server response:', response);

Integration with HTTP Libraries

Payload Encryptor works seamlessly with popular HTTP libraries:

With Axios

import axios from 'axios';
import { PayloadClient } from 'payload-encryptor';

const client = await PayloadClient.create();
const session = await axios.get('/session').then(r => r.data);
client.setSession(session);

// Send encrypted request
const response = await axios.post('/data', client.encrypt({
  secret: 'my-secret-data'
}));

// Decrypt response
const decrypted = client.decryptResponse(response.data);

With Got

import got from 'got';
import { PayloadClient } from 'payload-encryptor';

const client = await PayloadClient.create();
const session = await got('/session').json();
client.setSession(session);

// Send encrypted request
const response = await got.post('/data', {
  json: client.encrypt({ secret: 'data' })
}).json();

// Decrypt response
const decrypted = client.decryptResponse(response);

With WebSockets

import { PayloadClient } from 'payload-encryptor';

const client = await PayloadClient.create();
const ws = new WebSocket('wss://example.com/socket');

// After receiving session from server
ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  
  if (data.type === 'session') {
    client.setSession(data.session);
  } else if (data.type === 'encrypted') {
    const decrypted = client.decryptResponse(data.payload);
    console.log('Received:', decrypted);
  }
};

// Send encrypted message
function sendSecure(data: any) {
  const encrypted = client.encrypt(data);
  ws.send(JSON.stringify({ type: 'encrypted', payload: encrypted }));
}

Complete Example: Secure API

Here's a complete example showing both server and client working together:

Server (Express.js)

import express from 'express';
import { PayloadServer } from 'payload-encryptor';

const app = express();
app.use(express.json());

const server = await PayloadServer.create();

app.get('/api/session', (req, res) => {
  res.json(server.createSession());
});

app.post('/api/login', (req, res) => {
  const { username, password } = server.decrypt(req.body);
  
  // Validate credentials (example)
  if (username === 'admin' && password === 'secret') {
    const response = server.encryptResponse(
      req.body.clientEphemeralPublic,
      { success: true, token: 'jwt-token-here' }
    );
    res.json(response);
  } else {
    const response = server.encryptResponse(
      req.body.clientEphemeralPublic,
      { success: false, error: 'Invalid credentials' }
    );
    res.status(401).json(response);
  }
});

app.listen(3000);

Client (Browser)

import { PayloadClient } from 'payload-encryptor';

async function secureLogin(username: string, password: string) {
  const client = await PayloadClient.create();
  
  // Get session
  const session = await fetch('/api/session').then(r => r.json());
  client.setSession(session);
  
  // Encrypt credentials
  const encrypted = client.encrypt({ username, password });
  
  // Send encrypted login request
  const response = await fetch('/api/login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(encrypted)
  }).then(r => r.json());
  
  // Decrypt response
  const result = client.decryptResponse(response);
  
  if (result.success) {
    console.log('Login successful! Token:', result.token);
  } else {
    console.log('Login failed:', result.error);
  }
}

secureLogin('admin', 'secret');

Why Forward Secrecy Matters

Payload Encryptor generates new ephemeral keys for every message. This means:

  • Even if a key is compromised, only that single message is at risk
  • Past communications remain secure
  • Each request/response pair has unique encryption keys

This is the same level of security used by Signal and WhatsApp.

Conclusion

Payload Encryptor makes it easy to add end-to-end encryption to any application. With support for all major runtimes and zero configuration required, you can secure your client-server communication in minutes.

Stop transmitting sensitive data in plain text. Start encrypting today!

npm i payload-encryptor

Check out the full documentation on npm!