STVOR SDK
Signal-style E2EE for your app — minimal API
STVOR provides end-to-end encryption using the Signal Protocol (X3DH + Double Ratchet). Messages are encrypted on the client — the server never sees plaintext.
- Startups & MVPs
- Internal tools
- Pilot projects
- Healthcare/Finance
- Mobile apps
- Multi-instance prod
Installation
Install the SDK
npm install @stvor/sdk
This version requires
ws for WebSocket transport. Browser support coming in v2.3.
npm install ws
Quick Start (v2.3.1)
The simplest example — connect two users and send an encrypted message:
✨ New in v2.3.1
The SDK now auto-waits for recipients. No more setTimeout hacks!
import { createApp } from '@stvor/sdk';
// Initialize
const app = await createApp({
appToken: 'stvor_demo_token',
relayUrl: 'ws://localhost:8080' // Your relay server
});
// Connect users
const alice = await app.connect('alice@example.com');
const bob = await app.connect('bob@example.com');
// Setup message handler
bob.onMessage((from, msg) => {
console.log(`📩 ${from}: ${msg}`);
});
// send() now auto-waits for recipient (up to 10s timeout)
await alice.send('bob@example.com', 'Hello Bob! 🔐');
console.log('✅ Message sent!');
Explicit Readiness Control
For more control, use waitForUser() and onUserAvailable():
// Option 1: Explicit wait before sending
await alice.waitForUser('bob@example.com');
await alice.send('bob@example.com', 'Hello!');
// Option 2: React to user availability
alice.onUserAvailable((userId) => {
console.log(`${userId} is now online and ready for messages`);
});
// Option 3: Check synchronously
if (alice.isUserAvailable('bob@example.com')) {
await alice.send('bob@example.com', 'Hello!');
}
// Option 4: Skip auto-wait (throws immediately if not available)
await alice.send('bob@example.com', 'Hello!', { waitForRecipient: false });
// Option 5: Custom timeout
await alice.send('bob@example.com', 'Hello!', { timeout: 30000 });
Full Working Example
Copy these files to get E2EE working in 2 minutes:
Step 1: Create relay-server.mjs
This relay server is a minimal example for local development. It is NOT the STVOR API — it's a simple WebSocket forwarder to help you test encryption. For production, you'll need proper auth, persistence, and scaling.
The relay server forwards encrypted messages between clients. It cannot decrypt anything.
// relay-server.mjs
import WebSocket, { WebSocketServer } from 'ws';
const PORT = 8080;
const wss = new WebSocketServer({ port: PORT });
console.log(`🔌 Relay running on ws://localhost:${PORT}`);
const clients = new Map();
const pubkeys = new Map();
wss.on('connection', (ws) => {
// Send all known keys to new client
for (const [user, pub] of pubkeys.entries()) {
ws.send(JSON.stringify({ type: 'announce', user, pub }));
}
ws.on('message', (data) => {
let msg;
try { msg = JSON.parse(data.toString()); } catch { return; }
if (msg.type === 'announce' && msg.user) {
clients.set(msg.user, ws);
pubkeys.set(msg.user, msg.pub);
// Broadcast to all
for (const [_, client] of clients) {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify({
type: 'announce',
user: msg.user,
pub: msg.pub
}));
}
}
}
if (msg.type === 'message' && msg.to) {
const target = clients.get(msg.to);
if (target && target.readyState === WebSocket.OPEN) {
target.send(JSON.stringify(msg));
}
}
});
});
Step 2: Create app.mjs
Your application code with E2EE messaging:
// app.mjs
import { createApp } from '@stvor/sdk';
async function main() {
const app = await createApp({
appToken: 'stvor_demo_token',
relayUrl: 'ws://localhost:8080'
});
// Connect Bob (receiver)
const bob = await app.connect('bob@example.com');
bob.onMessage((from, msg) => {
console.log(`📩 [Bob] from ${from}: ${msg}`);
});
// Connect Alice (sender)
const alice = await app.connect('alice@example.com');
alice.onMessage((from, msg) => {
console.log(`📩 [Alice] from ${from}: ${msg}`);
});
// SDK auto-waits for recipient keys (no setTimeout needed!)
await alice.send('bob@example.com', 'Hello Bob! This is E2EE! 🔐');
console.log('✅ Alice sent message');
// Bob replies
await bob.send('alice@example.com', 'Hi Alice! Got it! 🎉');
console.log('✅ Bob sent message');
// Keep alive to see messages
await new Promise(r => setTimeout(r, 1000));
}
main().catch(console.error);
Step 3: Create package.json
{
"name": "my-e2ee-app",
"type": "module",
"dependencies": {
"@stvor/sdk": "^2.3.1",
"ws": "^8.13.0"
}
}
Step 4: Run it!
# Terminal 1: Start relay
node relay-server.mjs
# Terminal 2: Run app
npm install
node app.mjs
✅ Expected Output:
✅ Alice sent message
✅ Bob sent message
📩 [Bob] from alice@example.com: Hello Bob! This is E2EE! 🔐
📩 [Alice] from bob@example.com: Hi Alice! Got it! 🎉
Security Model
✅ What STVOR Guarantees
- End-to-end encryption — plaintext never leaves the device
- Forward Secrecy — compromise of current keys doesn't expose past messages
- Post-Compromise Security — after key rotation, attacker loses access
- Zero-knowledge relay — server cannot decrypt messages
Limitations
Known Limitations (v2.2.2)
- In-memory keys: Lost on app restart
- Single-instance only: No distributed state
- No mobile support: Node.js only
- Demo-level replay protection: In-memory nonce cache
- No multi-device: Each device = separate identity
Honest Positioning
STVOR is a practical reference implementation. Production-grade crypto (Signal Protocol), but demo-level infrastructure. Perfect for learning, prototypes, and low-risk applications.