# Shade End-to-end encryption library implementing the Signal Protocol (X3DH + Double Ratchet) for TypeScript/Bun. Drop into any project — frontend, backend, mobile — to get forward secrecy, post-compromise recovery, and self-healing security. ## What you get - **X3DH** initial key agreement (works asynchronously via prekey bundles) - **Double Ratchet** for per-message forward secrecy and post-compromise security - **Self-authenticated prekey server** (Hono, Docker-ready) with rate limiting, metrics, health checks - **Persistent storage backends**: SQLite (zero-config) and PostgreSQL (Drizzle) - **Identity rotation** with grace period for old sessions - **Safety numbers** (Signal-style fingerprints) for out-of-band verification - **Constant-time comparisons** and **memory zeroization** for hardened operation - **Binary wire format** that's significantly smaller than JSON - **Crash-safe** — sessions survive container restarts, power outages, SIGKILL ## Quick start ```bash # In your project bun add @shade/core @shade/crypto-web @shade/storage-sqlite ``` ```ts import { ShadeSessionManager } from '@shade/core'; import { SubtleCryptoProvider } from '@shade/crypto-web'; import { SQLiteStorage } from '@shade/storage-sqlite'; const crypto = new SubtleCryptoProvider(); const storage = new SQLiteStorage('/data/shade-client.db'); const manager = new ShadeSessionManager(crypto, storage); await manager.initialize(); // Establish a session with a peer (after fetching their bundle) await manager.initSessionFromBundle('bob', bobBundle); // Encrypt const envelope = await manager.encrypt('bob', 'Hello, encrypted world!'); // Decrypt const plaintext = await manager.decrypt('alice', incomingEnvelope); ``` ## Architecture ``` Shade Prekey Server (Hono) │ POST /v1/keys/register (signed) GET /v1/keys/bundle/:address POST /v1/keys/replenish (signed) DELETE /v1/keys/:address (signed) │ ┌─────────────────────┴─────────────────────┐ │ │ [Client A] [Client B] ShadeSessionManager ShadeSessionManager │ │ ├──── X3DH ────────────────────────────────►│ │ │ │◄──── Double Ratchet messages ────────────►│ │ │ SQLiteStorage / PostgresStorage SQLiteStorage / PostgresStorage ``` ## Packages | Package | Purpose | |---------|---------| | `@shade/core` | Protocol logic (X3DH, Double Ratchet, session manager, errors) | | `@shade/crypto-web` | SubtleCrypto + @noble/curves provider, in-memory storage | | `@shade/storage-sqlite` | Persistent SQLite storage (zero-config, bun:sqlite) | | `@shade/storage-postgres` | PostgreSQL storage with Drizzle for shared databases | | `@shade/server` | Prekey server (Hono routes, auth, rate limit, health, metrics) | | `@shade/transport` | HTTP + WebSocket transport wrappers with auto-encryption | | `@shade/proto` | Compact binary wire format (smaller than JSON) | ## Security properties | Property | Description | |----------|-------------| | **Forward secrecy** | Compromising a key cannot decrypt past messages | | **Post-compromise security** | Self-heals after key compromise on next DH ratchet | | **Authentication** | Ed25519 identity signatures on prekey server writes | | **Replay protection** | ±5 minute timestamp window on signed requests | | **Constant-time comparisons** | Timing attacks on identity keys are blocked | | **Memory zeroization** | Key material is zeroed after use (best-effort in JS) | | **Identity verification** | Safety numbers (60 digits) for out-of-band comparison | | **Identity rotation** | 7-day grace period for old sessions during rotation | ## Documentation - [SECURITY.md](./SECURITY.md) — Reporting vulnerabilities, security policy - [THREAT-MODEL.md](./THREAT-MODEL.md) — Honest threat model and assumptions - [examples/](./examples/) — Runnable example applications - [MIGRATION.md](./MIGRATION.md) — How to replace existing crypto with Shade ## Deployment For containerized deployment (Docker/Dokploy): ```yaml services: shade-prekey: image: shade-prekey-server:latest ports: - "3900:3900" volumes: - shade-data:/data environment: - SHADE_PREKEY_DB_PATH=/data/shade-prekeys.db volumes: shade-data: ``` The SQLite database persists to a Docker volume so all keys and prekey bundles survive restarts. ## License MIT