npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@telestack/telestacksec

v0.1.0

Published

TelestackSEC end-to-end encryption SDK for secure messaging

Readme

TelestackSEC

Military-grade end-to-end encrypted messaging for any application. Just plug in your database URL and you're done.

Features

Dead Simple API - 3 lines to encrypt, 3 lines to decrypt
Production Ready - Uses official libsignal library
Secure by Default - All keys encrypted at rest
Automatic Key Management - Generates, rotates, stores securely
Session Management - Auto-established and maintained
Forward Secrecy - Ratcheting keys for all communications
Group Messaging - Highly efficient Sender Keys protocol ✅ Encrypted Calls - Audio/Video signaling and stream encryption ✅ Real-Time Delivery - Sub-millisecond WebSocket push notifications ✅ Zero Configuration - Just provide database URL and master key


Quick Start

1. Install

npm install @telestack/telestacksec

2. Set Up Environment

Create a .env file:

DATABASE_URL="postgresql://user:password@localhost:5432/signal_db"
MASTER_KEY="your-secure-master-key-min-32-chars"

Important: Load environment variables in your application before initializing TelestackSEC:

import dotenv from 'dotenv';

// Load .env file (this is your responsibility, not handled by TelestackSEC)
dotenv.config();

import { TelestackSEC } from '@telestack/telestacksec';
// ... rest of code

Note: TelestackSEC does not call dotenv.config() internally. This follows library best practices and allows you to manage environment variables according to your application's needs (e.g., via Docker, CI/CD secrets, or environment injection).

3. Initialize

import { TelestackSEC } from '@telestack/telestacksec';

const signal = new TelestackSEC({
  databaseUrl: process.env.DATABASE_URL,
  masterKey: process.env.MASTER_KEY,
});

// Initialize database connection
await signal.initialize();

Important: Create the SDK instance once per application lifecycle and reuse it. TelestackSEC internally manages a single Prisma Client connection to prevent database exhaustion ("too many connections" errors).

Recommended pattern:

// app.ts - Create SDK once at startup
const signal = new TelestackSEC({
  databaseUrl: process.env.DATABASE_URL,
  masterKey: process.env.MASTER_KEY,
});
await signal.initialize();

// Export and reuse across your application
export { signal };

// In other modules:
import { signal } from './app';
const encrypted = await signal.encrypt({ from, to, message });

// On shutdown:
process.on('SIGTERM', async () => {
  await signal.disconnect();
  process.exit(0);
});

Note: Do not create a new TelestackSEC instance per request (e.g., in serverless functions or request handlers). This will exhaust database connections. Instead, use connection pooling at the database level or create the SDK at the application root and share it.

4. Register Users

// User A registers
const alice = await signal.user.register('[email protected]');
console.log('Alice ID:', alice.userId);

// User B registers
const bob = await signal.user.register('[email protected]');
console.log('Bob ID:', bob.userId);

5. Encrypt & Decrypt

// Alice sends encrypted message to Bob
const encrypted = await signal.encrypt({
  from: alice.userId,
  to: bob.userId,
  message: 'Secret message!',
});

console.log('Encrypted:', encrypted.ciphertext);
console.log('Session ID:', encrypted.sessionId);

// Bob receives and decrypts
const decrypted = await signal.decrypt({
  to: bob.userId,
  ciphertext: encrypted.ciphertext,
  sessionId: encrypted.sessionId,
});

console.log('Message:', decrypted.message); // "Secret message!"
console.log('From:', decrypted.from); // alice.userId

6. Real-Time Listening (WebSocket)

Configure relayUrl and relayAuthKey and listen for instant push notifications:

// Bob listens for incoming E2EE messages in real-time
signal.on('message', (msg) => {
    console.log(`Received real-time message from ${msg.from}:`, msg.message);
});

await signal.listen(bob.userId);

API Reference

User Management

// Register a new user
const user = await signal.user.register('[email protected]');
// {
//   userId: 'uuid',
//   email: '[email protected]',
//   publicKey: 'base64...',
//   createdAt: Date
// }

// Delete a user
await signal.user.delete(userId);

// Get user's public key
const publicKey = await signal.user.getPublicKey(userId);

Messaging

// Encrypt message
const encrypted = await signal.encrypt({
  from: senderUserId,
  to: recipientUserId,
  message: 'Your message here',
});

// Decrypt message
const decrypted = await signal.decrypt({
  to: recipientUserId,
  ciphertext: encrypted.ciphertext,
  sessionId: encrypted.sessionId,
});

Group Messaging

// Create a new group
const group = await signal.group.createGroup({
    name: 'Secret Team',
    creatorId: alice.userId,
    memberIds: [bob.userId, charlie.userId]
});

// Send message to group
const msg = await signal.group.sendGroupMessage({
    groupId: group.groupId,
    senderId: alice.userId,
    message: 'Hello team!'
});

// Receive group message
const decryptedContent = await signal.group.receiveGroupMessage({
    groupId: group.groupId,
    senderId: alice.userId,
    recipientId: bob.userId,
    ciphertext: msg.ciphertext
});

Encrypted Calls

// Initiate an encrypted call (sends SDP offer)
const callOffer = await signal.call.initiateCall({
    callerId: alice.userId,
    calleeId: bob.userId,
    callType: 'VIDEO',
    sdpOffer: '...' // From your WebRTC RTCPeerConnection
});

// Answer a call
const answer = await signal.call.answerCall({
    callId: callOffer.callId,
    calleeId: bob.userId,
    sdpAnswer: '...' 
});

// Encrypt media streams using derived keys
const encryptedFrame = await signal.call.encryptFrame(
    callOffer.callId,
    Buffer.from('video-frame-data')
);


### Session Management

```typescript
// Get session status
const status = await signal.session.getStatus(userId1, userId2);

// List all sessions for a user
const sessions = await signal.session.list(userId);

// Reset/delete a session (forgets encryption state)
await signal.session.reset(userId1, userId2);

Admin & Maintenance

// Check system health
const health = await signal.admin.health();
// { status, database, timestamp, message }

// Rotate user's prekeys
const rotated = await signal.admin.rotatePrekeys(userId);
// { userId, newPrekeysGenerated, oldPrekeysRemoved, timestamp }

// Get full diagnostics
const diag = await signal.admin.getDiagnostics();

Configuration

const signal = new TelestackSEC({
  // Required
  databaseUrl: 'postgresql://...',

  // Optional (with defaults)
  masterKey: 'your-key',              // Required in env if not provided
  maxPrekeys: 50,                     // Number of prekeys to maintain
  prekeysThreshold: 20,               // Regenerate when below this
  messageHistoryEnabled: true,        // Store encrypted messages in DB
  sessionExpiryDays: 90,              // null = no expiry
  logLevel: 'info',                   // 'debug' | 'info' | 'warn' | 'error'
});

Security Architecture

How It Works

  1. Identity Keys - Unique master secret per user (stored encrypted)
  2. PreKeys - One-time keys for session initiation (auto-rotated)
  3. X3DH - Key agreement protocol (ECDH-based)
  4. Double Ratchet - Message encryption & key derivation per message
  5. Master Key Encryption - All database keys encrypted at rest

What's Secure

  • ✅ Messages encrypted in transit
  • ✅ Keys encrypted at rest (AES-256-GCM)
  • ✅ Forward secrecy (old messages safe even if key stolen)
  • ✅ Break-in recovery (compromised key doesn't leak old messages)
  • ✅ Replay protection (can't replay old messages)
  • ✅ Message authenticity (detects tampering)

Database Setup

The SDK manages the database schema automatically using Prisma.

Supported Databases

  • PostgreSQL (recommended)
  • MySQL (experimental)
  • SQLite (development only)

Manual Migration

# From SDK directory
npx prisma migrate dev --name init

# Generate Prisma client
npx prisma generate

Schema Overview

Users ──→ IdentityKeys (1-to-1)
   ├──→ PreKeys (1-to-many)
   ├──→ Sessions (many-to-many)
   └──→ Messages (many-to-many)

Examples

Express.js Integration

import express from 'express';
import { TelestackSEC } from '@telestack/telestacksec';

const app = express();
const signal = new TelestackSEC({ databaseUrl: process.env.DATABASE_URL });

app.use(express.json());

// Initialize on startup
app.listen(3000, async () => {
  await signal.initialize();
});

// Send encrypted message
app.post('/messages', async (req, res) => {
  const { from, to, message } = req.body;

  try {
    const encrypted = await signal.encrypt({ from, to, message });
    res.json({ success: true, ...encrypted });
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
});

// Receive encrypted message
app.post('/messages/decrypt', async (req, res) => {
  const { to, ciphertext, sessionId } = req.body;

  try {
    const decrypted = await signal.decrypt({ to, ciphertext, sessionId });
    res.json(decrypted);
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
});

WebSocket Chat

import { Server } from 'socket.io';
import { TelestackSEC } from '@telestack/telestacksec';

const io = new Server(server);
const signal = new TelestackSEC({ databaseUrl: process.env.DATABASE_URL });

await signal.initialize();

io.on('connection', (socket) => {
  // User sends encrypted message
  socket.on('message', async (data) => {
    const encrypted = await signal.encrypt(data);
    io.to(data.to).emit('message', encrypted);
  });

  // Receive and decrypt
  socket.on('receive', async (data) => {
    const decrypted = await signal.decrypt(data);
    console.log('Received:', decrypted.message);
  });
});

Error Handling

import { SignalError, SignalErrorCode } from '@telestack/telestacksec';

try {
  const encrypted = await signal.encrypt({ from, to, message });
} catch (error) {
  if (error instanceof SignalError) {
    switch (error.code) {
      case SignalErrorCode.USER_NOT_FOUND:
        console.log('User does not exist');
        break;
      case SignalErrorCode.SESSION_INIT_FAILED:
        console.log('Could not establish secure session');
        break;
      case SignalErrorCode.DECRYPTION_FAILED:
        console.log('Message corrupted or tampered');
        break;
      default:
        console.log('Error:', error.message);
    }
  }
}

Best Practices

  1. Always call initialize() before using the SDK
  2. Store master key securely - Use environment variables or key vaults
  3. Rotate prekeys regularly - SDK auto-rotates, but you can trigger manually
  4. Close connections on shutdown - Call await signal.disconnect()
  5. Don't share userId - Use it only internally; send public keys for auth
  6. Validate senders - Verify user identity at application level
  7. Use HTTPS/WSS - Encrypt transport layer too
  8. Monitor prekey availability - Check health regularly

Troubleshooting

"Master key must be at least 32 characters"

Add a longer master key to .env:

MASTER_KEY="your-very-secure-key-at-least-32-chars"

"Failed to connect to database"

Check your DATABASE_URL:

# Test connection
psql $DATABASE_URL

"No unused prekeys available"

Run prekey rotation:

await signal.admin.rotatePrekeys(userId);

"Session not found"

Session is deleted or expired. Request sender to establish a new session (just encrypt again).


Performance

  • Latency: 10-50ms per encrypt/decrypt (DB + crypto)
  • Throughput: 1000+ messages/second (depends on DB)
  • Storage: ~2KB per session, ~1KB per message

License

MIT


Support & Connect With Us

📧 Email: [email protected]

GitHub LinkedIn X


Ready to add military-grade encryption to your app? 🚀

const signal = new TelestackSEC({ databaseUrl });
await signal.initialize();

That's it!