All notable changes to L-THREAD / LTP will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Added canonical orientation explanation and onboarding materials (media assets referenced externally; binaries not stored in repo).
- No protocol or contract changes.
- Published
@ltp/inspect@0.1.1CLI shim forltp inspectentrypoint with deterministic inspector outputs (stable field ordering, gated timestamps, and golden trace quickstart).
- Complete v0.6.0 production security (see roadmap below)
- LRI integration examples
- Cross-language SDK comparison benchmarks
- Production-ready TOON codec implementations
0.2.0 - 2025-12-26
- LTP Inspector CLI Refactor: The
ltp inspectcommand now strictly requires subcommands (trace,replay,explain). Implicit default behavior is removed.- Old:
ltp inspect file.trace.json - New:
ltp inspect trace --input file.trace.jsonl
- Old:
- Format: Input traces MUST be strictly newline-delimited JSON (JSONL). Legacy JSON arrays
[...]are rejected with exit code 2. - Artifacts: Canonical traces renamed to
*.trace.jsonlto reflect their format.canonical-clean.jsonis nowcanonical-linear.jsonl. - Tooling: Deprecated
ltp-inspectbinary references should be replaced withltp inspect.
0.1.0 - 2025-02-20
- Frames (frozen v0.1 surface):
hello,heartbeat,orientation,route_request,route_response,focus_snapshotdocumented as stable for partners. - Canonical flow: Locked reference path
hello → heartbeat → orientation → route_request → route_responsewith deterministic IDs and timestamps for demo parity. - Conformance kit artifacts: Deterministic report (
reports/ci-report.json) and badge payload (reports/badge.json) emitted bypnpm -w ltp:conformance verify:dir fixtures/conformance/v0.1 --out .... - Demo servers & quickstart: One-command
make demo/pnpm demo:allto print the canonical flow; README quickstart updated with install + demo instructions.
Addressing HIGH priority issue from Snowden/Assange security audit
- Metadata Tracking Vulnerability (HIGH PRIORITY FIX ❌→✅)
thread_id,session_id, andtimestampnow encrypted using AES-256-GCM- Prevents adversaries from tracking users across sessions
- Server uses
routing_tag(HMAC-based) for message routing without seeing plaintext metadata - Location:
sdk/js/src/client.ts:1011-1051, 557-577
-
Metadata Encryption Functions - Encrypt sensitive metadata fields
encryptMetadata(metadata, encryptionKey)- Encrypts thread_id, session_id, timestampdecryptMetadata(encryptedMetadata, encryptionKey)- Decrypts metadata blobgenerateRoutingTag(threadId, sessionId, macKey)- Creates HMAC-based routing tag- Format:
ciphertext:iv:tag(colon-separated for easy parsing) - Location:
sdk/js/src/crypto.ts:756-838
-
New Envelope Fields - Support encrypted metadata
LtpEnvelope.encrypted_metadata- Encrypted metadata blob (AES-256-GCM)LtpEnvelope.routing_tag- HMAC-based tag for server routing- Location:
sdk/js/src/types.ts:245-260
-
Client Options - Enable metadata encryption
enableMetadataEncryption- Opt-in flag for metadata encryption (default: false)sessionEncryptionKey- Encryption key from ECDH key derivation- Automatically set when
enableEcdhKeyExchangeis true - Location:
sdk/js/src/types.ts:295-310
-
Message Sending - Encrypts metadata when enabled
send()method now encrypts metadata ifenableMetadataEncryptionis true- Plaintext metadata fields cleared (set to empty/zero) when encrypted
- Server uses
routing_tagfor message routing - Location:
sdk/js/src/client.ts:1011-1051
-
Message Receiving - Decrypts metadata automatically
handleMessageAsync()decryptsencrypted_metadataif present- Restores plaintext metadata to envelope for backward compatibility
- Falls back to plaintext if decryption fails
- Location:
sdk/js/src/client.ts:557-577
Before v0.6.0-alpha.3: ❌ Metadata EXPOSED
// All metadata in cleartext:
{
thread_id: "thread-abc123", // ← Tracking identifier
session_id: "session-xyz789", // ← Session identifier
timestamp: 1705598400000, // ← Behavioral profiling
// Adversary can:
// - Track user across sessions (thread_id)
// - Correlate messages (session_id)
// - Profile activity patterns (timestamp)
}After v0.6.0-alpha.3: ✅ Metadata PROTECTED
// Metadata encrypted:
{
encrypted_metadata: "ciphertext:iv:tag", // ← Encrypted blob
routing_tag: "a1b2c3d4e5f6...", // ← HMAC tag (no plaintext)
thread_id: "", // ← Cleared (server uses routing_tag)
session_id: "", // ← Cleared
timestamp: 0, // ← Zeroed
// Adversary cannot:
// - See thread_id or session_id
// - Track user across sessions
// - Profile activity patterns
// Server uses routing_tag for routing (HMAC-based, no plaintext)
}Protection Mechanism:
// Encryption process:
const metadata = { thread_id, session_id, timestamp };
const encrypted = await encryptMetadata(metadata, encryptionKey);
// Format: ciphertext:iv:tag
// Routing tag generation:
const routingTag = await generateRoutingTag(threadId, sessionId, macKey);
// Server uses routing_tag for routing without decrypting
// Decryption (client-side only):
const decrypted = await decryptMetadata(encrypted, encryptionKey);
// Restores plaintext metadata for application useEnable metadata encryption:
const client = new LtpClient('wss://api.example.com', {
enableEcdhKeyExchange: true, // Required for encryption key
enableMetadataEncryption: true, // Enable metadata encryption
// sessionEncryptionKey set automatically from ECDH
});Backward Compatibility:
- Metadata encryption is opt-in (default: false)
- Servers can handle both encrypted and plaintext metadata
- Clients decrypt automatically if
encrypted_metadatapresent - Falls back to plaintext if decryption fails
- Part of v0.6.0 production security roadmap
- Addresses HIGH priority from Snowden/Assange audit
- Complements HMAC-based nonces (v0.6.0-alpha.1)
- Complements authenticated ECDH (v0.6.0-alpha.2)
Addressing CRITICAL priority issue from Snowden/Assange security audit
- Man-in-the-Middle Vulnerability in ECDH Key Exchange (CRITICAL FIX ❌→✅)
- Ephemeral ECDH public keys now signed with long-term secret key
- Client signs
client_ecdh_public_keybefore sending in handshake - Client verifies
server_ecdh_public_keysignature before deriving keys - MitM attackers can no longer substitute ECDH keys undetected
- Handshake is rejected if server ECDH signature verification fails
- Location:
sdk/js/src/client.ts:471-489, 516-534, 775-800
-
ECDH Key Signing Functions - Authenticate ephemeral keys
signEcdhPublicKey(publicKey, entityId, timestamp, secretKey)- Creates HMAC-SHA256 signature over
publicKey:entityId:timestamp - Used by client to sign ephemeral keys during handshake
- Location:
sdk/js/src/crypto.ts:75-83
-
ECDH Key Verification Functions - Validate signatures
verifyEcdhPublicKey(publicKey, entityId, timestamp, signature, secretKey, maxAge)- Validates signature and checks timestamp freshness (default: 5 minutes)
- Prevents replay attacks on key exchange
- Returns
{ valid: boolean; error?: string } - Location:
sdk/js/src/crypto.ts:99-129
-
New Type Fields - Support authenticated ECDH
HandshakeInitMessage.client_ecdh_signature- Client key signatureHandshakeInitMessage.client_ecdh_timestamp- Signature timestampHandshakeAckMessage.server_ecdh_signature- Server key signatureHandshakeAckMessage.server_ecdh_timestamp- Signature timestampHandshakeResumeMessage.client_ecdh_signature- Resume key signatureHandshakeResumeMessage.client_ecdh_timestamp- Resume timestamp- Location:
sdk/js/src/types.ts:109-114, 154-159, 177-181
- Handshake Protocol - Signs and verifies ECDH keys
sendHandshakeInit()now signs client ECDH key ifsecretKeyprovidedsendHandshakeResume()signs client key for resumed sessionshandleHandshakeAck()verifies server ECDH key signature- Connection terminated if server signature verification fails
- Warnings logged if ECDH enabled without
secretKey(MitM risk)
Before v0.6.0-alpha.2: ❌ MitM VULNERABLE
// Attacker intercepts handshake:
Client → [Attacker] → Server
client_ecdh_public_key: "CLIENT_KEY"
// Attacker replaces with own key:
client_ecdh_public_key: "ATTACKER_KEY"
Server → [Attacker] → Client
server_ecdh_public_key: "SERVER_KEY"
// Attacker replaces with own key:
server_ecdh_public_key: "ATTACKER_KEY"
// Result: Attacker can decrypt all messages
// Client ←[decrypt]→ Attacker ←[decrypt]→ ServerAfter v0.6.0-alpha.2: ✅ MitM PROTECTED
// Client signs its ECDH key:
client_ecdh_public_key: "CLIENT_KEY"
client_ecdh_signature: HMAC(secretKey, "CLIENT_KEY:clientId:timestamp")
client_ecdh_timestamp: 1705598400000
// Server verifies signature before using key
// Attacker cannot forge signature without secretKey
// Server signs its ECDH key:
server_ecdh_public_key: "SERVER_KEY"
server_ecdh_signature: HMAC(serverSecretKey, "SERVER_KEY:sessionId:timestamp")
server_ecdh_timestamp: 1705598401000
// Client verifies signature - REJECTS if invalid:
if (!verifyEcdhPublicKey(...)) {
disconnect(); // ← Connection terminated
return;
}
// Result: MitM attack PREVENTEDProtection Mechanism:
// Signature format prevents key substitution:
const input = `${publicKey}:${entityId}:${timestamp}`;
const signature = HMAC-SHA256(secretKey, input);
// Without secretKey, attacker cannot:
// 1. Generate valid signature for their own key
// 2. Modify the public key without breaking signature
// 3. Replay old signatures (timestamp validation)
// Timestamp validation:
if (age > 5 minutes) reject; // Prevents replay attacks
if (skew > 5 seconds) reject; // Prevents future timestamps- ✅ Backward compatible - Signatures are optional (but recommended)
⚠️ Warning logged - If ECDH enabled withoutsecretKey(insecure)- 🔐 Production ready - When
secretKeyprovided, MitM protection active - 🔄 Server support needed - Server must sign its ECDH key for full protection
npm test # All tests passing ✅Recommended (Authenticated ECDH):
import { LtpClient } from '@liminal/ltp-client';
// Provide secretKey to enable authenticated ECDH
const client = new LtpClient('wss://api.example.com', {
clientId: 'user-device-123',
secretKey: 'shared-secret', // ← Enables key authentication
enableEcdhKeyExchange: true, // ← Automatic key exchange
});
// Handshake now includes signed ECDH keys:
// - client_ecdh_public_key (ephemeral)
// - client_ecdh_signature (authenticated)
// - client_ecdh_timestamp (freshness)
// If server signature invalid → connection rejectedInsecure (No authentication - logs warning):
// Without secretKey, ECDH keys are NOT authenticated
const client = new LtpClient('wss://api.example.com', {
clientId: 'user-device-123',
enableEcdhKeyExchange: true,
// No secretKey provided ← MitM risk!
});
// WARNING logged: "ECDH key exchange enabled but no secretKey
// provided - key not authenticated (MitM risk!)"- HIGH: Remove client ID from nonce (HMAC-based nonce) ✅
- CRITICAL: Authenticate ECDH public keys (sign with long-term keys) ✅
- HIGH: Encrypt metadata fields (thread_id, timestamps)
- MEDIUM: Migrate to X25519 curve
- MEDIUM: Implement message padding
- Remove v0.3 placeholder signatures entirely
- Add key rotation mechanism
- Addresses CRITICAL priority from security audit (PR #13)
- Prevents Man-in-the-Middle attacks on key exchange
- Complements HMAC-based nonces (v0.6.0-alpha.1)
- Requires shared
secretKeyfor authentication
Addressing HIGH priority issue from Snowden/Assange security audit
-
Client Identity Leak in Nonces (HIGH PRIORITY FIX ❌→✅)
- Nonces now use HMAC-based format:
hmac-{32hex}-{timestamp} - NO client_id exposed - prevents user tracking across sessions
- Uses session MAC key to generate unpredictable nonce values
- Attackers can no longer correlate messages to specific users
- Location:
sdk/js/src/client.ts:1160-1187
- Nonces now use HMAC-based format:
-
Dual-Format Nonce Validation (Backward Compatibility)
- Validates both HMAC-based nonces (v0.6+) and legacy format (v0.5)
- Legacy format:
{clientId}-{timestamp}-{random}still supported - Warnings logged when using legacy format
- Location:
sdk/js/src/client.ts:1193-1274
- HMAC-SHA256 Utility Function - Cross-platform HMAC computation
hmacSha256(input, key)- Works in browser (Web Crypto API) and Node.js- Used for secure nonce generation and other HMAC operations
- Location:
sdk/js/src/crypto.ts:18-61
-
generateNonce()- Now Async- Changed from synchronous to async function
- Computes HMAC over
{timestamp}-{random}using session MAC key - Falls back to legacy format if MAC key not available (logs warning)
- Location:
sdk/js/src/client.ts:1160-1187
-
send()Method - Awaits nonce generation- Updated to
await this.generateNonce() - Location:
sdk/js/src/client.ts:932
- Updated to
Before v0.6.0-alpha.1: ❌ IDENTITY LEAK
// Nonce format exposed client_id:
{
"nonce": "user-device-123-1705598400000-a1b2c3d4",
// ^^^^^^^^^^^^^^^^ - Client identity leaked!
// Attacker can:
// - Track user across sessions
// - Correlate messages to specific users
// - Build user activity profiles
}After v0.6.0-alpha.1: ✅ PRIVACY PROTECTED
// Nonce format is HMAC-based (no client_id):
{
"nonce": "hmac-f3a8b9c2d1e4567890abcdef12345678-1705598400000",
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Unpredictable HMAC
// Attacker cannot:
// - Identify which user sent the message
// - Correlate messages across sessions
// - Build user profiles from nonces
}Privacy Protection Mechanism:
// HMAC computation prevents identity inference:
const input = `${timestamp}-${randomHex}`;
const hmac = await hmacSha256(input, sessionMacKey);
const nonce = `hmac-${hmac.substring(0, 32)}-${timestamp}`;
// Even with identical timestamp, different random value = different HMAC
// Without MAC key, attacker cannot predict or verify nonce values- ✅ Backward compatible - Legacy nonce format still validated
- ✅ Graceful migration - Clients can mix HMAC and legacy nonces
⚠️ Warning logged - When using legacy format (prompts upgrade)- 🔄 Transition period - Both formats supported until v0.7.0
npm test # All tests passing ✅Recommended (HMAC-based nonces):
import { LtpClient } from '@liminal/ltp-client';
// Provide sessionMacKey to enable HMAC-based nonces
const client = new LtpClient('wss://api.example.com', {
clientId: 'user-device-123',
sessionMacKey: macKey, // ← Enables HMAC nonces (no ID leak)
enableEcdhKeyExchange: true,
});
// Nonces now: hmac-{32hex}-{timestamp}
await client.send('state_update', { data: 'example' });Legacy (still works, but logs warning):
// Without sessionMacKey, falls back to legacy format
const client = new LtpClient('wss://api.example.com', {
clientId: 'user-device-123',
// No sessionMacKey provided
});
// WARNING: Nonces use legacy format with client ID
// Format: user-device-123-{timestamp}-{random}- HIGH: Remove client ID from nonce (HMAC-based nonce) ✅
- CRITICAL: Authenticate ECDH public keys (sign with long-term keys)
- HIGH: Encrypt metadata fields (thread_id, timestamps)
- MEDIUM: Migrate to X25519 curve
- MEDIUM: Implement message padding
- Remove v0.3 placeholder signatures entirely
- Add key rotation mechanism
- Addresses HIGH priority issue from security audit (PR #13)
- Part of v0.6.0 production security roadmap
- Complements metadata signing (v0.5.0-beta.4)
- Requires session MAC key (derived via ECDH + HKDF)
Ported from PR #11 - Closes metadata tampering vulnerability
- Metadata Tampering Vulnerability (CRITICAL FIX ❌→✅)
- HMAC signatures now include
metafield - HMAC signatures now include
content_encodingfield - Attackers can no longer modify metadata without breaking signature
- Prevents client_id spoofing, platform manipulation, etc.
- Location:
sdk/js/src/crypto.ts
- HMAC signatures now include
-
signMessage()signature - Now acceptsmetaandcontent_encodingawait signMessage({ // ... existing fields meta: message.meta, // ✅ NEW - now signed content_encoding: message.content_encoding // ✅ NEW - now signed }, secretKey);
-
verifySignature()signature - Now verifiesmetaandcontent_encoding- Validation includes all envelope metadata
- Any modification to metadata breaks signature
-
Canonical serialization - Updated to include new fields
metafield included in canonical representationcontent_encodingfield included in canonical representation- Sorted-key JSON serialization maintained
Before v0.5.0-beta.4: ❌ VULNERABLE
// Attacker could modify metadata without detection:
{
"type": "state_update",
"payload": {...},
"signature": "valid_hmac", // ✅ Still valid!
"meta": {
"client_id": "attacker" // ❌ Modified but signature valid
}
}After v0.5.0-beta.4: ✅ PROTECTED
// Any metadata modification breaks signature:
{
"type": "state_update",
"payload": {...},
"signature": "invalid_hmac", // ❌ Signature now invalid
"meta": {
"client_id": "attacker" // Modification detected!
}
}- ✅ Backward compatible -
metaandcontent_encodingare optional - ✅ Graceful degradation - Missing fields treated as empty objects/strings
⚠️ Signature mismatch - Old signatures won't verify if metadata present
npm test # All tests passing ✅No code changes required for existing clients. Signatures will automatically include metadata when present:
// Before (still works):
const signature = await signMessage({
type, thread_id, session_id, timestamp, nonce, payload
}, key);
// After (recommended):
const signature = await signMessage({
type, thread_id, session_id, timestamp, nonce, payload,
meta: { client_id, platform }, // ✅ Now protected
content_encoding: 'json' // ✅ Now protected
}, key);- Ported from Python SDK (PR #11)
- Complements hash chaining (already in main)
- Part of v0.5.0 security hardening roadmap
Completing cryptographic security enhancements - Phase 3
-
Automatic ECDH Key Exchange During Handshake (MAJOR FEATURE)
- Client generates ephemeral ECDH P-256 key pair automatically
- Public key sent in
handshake_initmessage - Server's public key received in
handshake_ack - Session keys derived automatically via HKDF
- Perfect Forward Secrecy (PFS) - ephemeral keys cleared after derivation
- Opt-in via
enableEcdhKeyExchange: trueoption - Location:
sdk/js/src/client.ts:445-458, 655-688
-
Enhanced Type Definitions
HandshakeInitMessage.client_ecdh_public_keyfieldHandshakeAckMessage.server_ecdh_public_keyfieldLtpClientOptions.enableEcdhKeyExchangeflag- Location:
sdk/js/src/types.ts
-
Timing Leak in timingSafeEqual() (SECURITY FIX)
- Early return on length mismatch leaked timing information
- Now compares max(a.length, b.length) characters always
- Includes length difference in result for constant-time behavior
- Added dummy comparison for Node.js crypto.timingSafeEqual fallback
- Location:
sdk/js/src/crypto.ts:128-160
-
Placeholder Signatures (SECURITY IMPROVEMENT)
- Added deprecation warnings when using v0-placeholder signatures
- Warns: "Using insecure placeholder signature - v0.3 compatibility mode"
- Documented removal in v0.6.0
- No fallback to placeholder when signatures required
- Location:
sdk/js/src/client.ts:1082-1089
Independent security assessment conducted
- Perfect Forward Secrecy implementation ✅
- Proper HKDF key derivation (RFC 5869) ✅
- Constant-time comparison (after fix) ✅
- Mandatory signature verification ✅
- Comprehensive replay protection ✅
- CRITICAL: ECDH public keys not authenticated (MitM vulnerability)
- HIGH: Client ID leaked in nonce format
- HIGH: Metadata transmitted in cleartext (tracking risk)
- MEDIUM: P-256 curve (NIST-standardized, X25519 preferred)
- MEDIUM: No traffic padding (vulnerable to traffic analysis)
- LOW: No key rotation mechanism
import { LtpClient } from '@liminal/ltp-client';
// Automatic ECDH key exchange + signature verification
const client = new LtpClient('wss://api.example.com', {
clientId: 'user-device-123',
enableEcdhKeyExchange: true, // ← Automatic key exchange
}, {
onConnected: (threadId, sessionId) => {
console.log('🔒 Secure session established with PFS');
}
});
await client.connect();None - Fully backward compatible with v0.5.0-beta.2
- CRITICAL: Authenticate ECDH public keys (sign with long-term keys)
- HIGH: Remove client ID from nonce (use HMAC-based nonce)
- HIGH: Encrypt metadata fields (thread_id, timestamps)
- MEDIUM: Migrate to X25519 curve
- MEDIUM: Implement message padding
- Remove v0.3 placeholder signatures entirely
- Add key rotation mechanism
Implementing recommendations from cryptographic security audit - Phase 2
-
Mandatory Signature Verification (CRITICAL FIX)
- Signature verification now REQUIRED by default when MAC key is present
- Invalid signatures are REJECTED (not just logged)
- Messages without signatures are REJECTED when verification is required
- Configuration error thrown if verification required but no key provided
- Location:
sdk/js/src/client.ts:446-578
-
Nonce Validation for Replay Protection (CRITICAL FIX)
- Comprehensive nonce validation with uniqueness check
- Nonce format validation:
clientId-timestamp-randomHex - Client ID verification in nonce
- Timestamp drift detection (rejects old and future nonces)
- Entropy validation (minimum 8 hex characters)
- Nonce cache with automatic cleanup (TTL-based)
- Location:
sdk/js/src/client.ts:957-1046
-
Timestamp Validation - Replay protection
- Rejects messages older than
maxMessageAge(default: 60s) - Rejects messages from future (clock skew tolerance: 5s)
- Configurable via
maxMessageAgeoption
- Rejects messages older than
-
Signature Generation - Proper key handling
- Uses
sessionMacKeyif present, falls back tosecretKey - No placeholder when verification is required
- Throws error if key missing but verification required
- Location:
sdk/js/src/client.ts:1048-1076
- Uses
-
Message Handling - Now fully async
handleMessage()→handleMessageAsync()- Signature verification is blocking (security critical)
- Nonce validation integrated into message flow
- Failed verification = message rejected
-
Client Lifecycle - Nonce cache management
- Starts nonce cleanup on connect
- Stops cleanup on disconnect
- Clears cache on manual disconnect
- Periodic cleanup every 60 seconds
✅ Fixed: Optional signature verification (now MANDATORY) ✅ Fixed: No replay protection (now COMPREHENSIVE) ✅ Fixed: Timestamp validation (age + drift detection) ✅ Fixed: Nonce validation (uniqueness + format + entropy) 🚧 In Progress: ECDH handshake protocol 🚧 In Progress: Remove all placeholder signatures
None yet - v0.4 compatibility maintained
However, when a sessionMacKey or secretKey is provided:
- Signature verification is NOW REQUIRED by default
- Messages without valid signatures are REJECTED
- This is a security improvement, not a breaking change
// v0.5.0-beta.2 - Automatic security when key is present
const client = new LtpClient('wss://...', {
sessionMacKey: macKey,
// requireSignatureVerification defaults to TRUE (automatic)
// maxMessageAge defaults to 60000ms (60 seconds)
});
// To disable verification (NOT RECOMMENDED):
const insecureClient = new LtpClient('wss://...', {
sessionMacKey: macKey,
requireSignatureVerification: false, // Explicitly disable
});- Implement mandatory signature verification in client ✅
- Integrate nonce validation (replay protection) ✅
- Add ECDH handshake protocol
- Remove placeholder signatures when verification enabled
- Update all examples with new key exchange pattern
- Security audit documentation
- Performance benchmarks
Implementing recommendations from cryptographic security audit
-
ECDH Key Derivation for Browser (CRITICAL FIX)
- Implemented ECDH
deriveSharedSecret()for Web Crypto API - No longer Node.js-only limitation
- Full browser support for key exchange
- Location:
sdk/js/src/crypto.ts:215-270
- Implemented ECDH
-
HKDF (Key Derivation Function) - RFC 5869 compliant
- Proper key derivation from ECDH shared secret
- Works in both browser (Web Crypto HKDF) and Node.js
- Implements Extract-then-Expand pattern
- Location:
sdk/js/src/crypto.ts:276-346
-
Session Key Derivation - Proper key separation
deriveSessionKeys()derives separate keys:encryptionKey- for AES-GCM encryptionmacKey- for HMAC signaturesivKey- for IV generation
- No more shared secret reuse
- Location:
sdk/js/src/crypto.ts:352-369
- Client Options - Move toward proper key management
- Added
sessionMacKey(replacessecretKey) - Added
requireSignatureVerification(defaults to TRUE when key present) - Added
maxMessageAgefor replay protection (default 60s) - Deprecated
secretKey(kept for v0.4 compatibility) - Location:
sdk/js/src/types.ts:270-293
- Added
✅ Fixed: ECDH browser limitation ✅ Fixed: No HKDF - now using proper KDF ✅ Fixed: Key reuse - now separate keys for different purposes 🚧 In Progress: Mandatory signature verification 🚧 In Progress: Replay attack protection with nonce validation 🚧 In Progress: Remove placeholder signatures
None yet - v0.4 compatibility maintained
v0.5.0-beta is NOT production ready. Use for testing and feedback only.
// OLD (v0.4): Shared secret
const client = new LtpClient('wss://...', {
secretKey: 'shared-secret', // ❌ Deprecated
});
// NEW (v0.5): ECDH key exchange + HKDF
const { publicKey, privateKey } = await generateKeyPair();
const sharedSecret = await deriveSharedSecret(privateKey, serverPublicKey);
const { macKey } = await deriveSessionKeys(sharedSecret, sessionId);
const client = new LtpClient('wss://...', {
sessionMacKey: macKey, // ✅ Proper key management
requireSignatureVerification: true, // ✅ Mandatory (default)
maxMessageAge: 60000, // ✅ Replay protection
});Major cryptographic enhancements - Message signing and verification
-
Cryptographic Module - New crypto.ts with comprehensive utilities
- HMAC-SHA256 message signing and verification
- ECDH key pair generation and key exchange (P-256)
- AES-256-GCM encryption/decryption
- Timing-safe comparison
- Browser (Web Crypto API) and Node.js support
-
Message Signing - Automatic HMAC-SHA256 signatures
- Optional secretKey in LtpClientOptions
- Backward compatible with v0-placeholder fallback
- Non-blocking signature generation
-
Signature Verification - Validate incoming messages
- Optional enableSignatureVerification flag
- Logs warnings for invalid signatures
- Non-blocking verification
-
Exported Crypto Utilities - Public API for advanced use
- signMessage(), verifySignature()
- generateKeyPair(), deriveSharedSecret()
- encryptPayload(), decryptPayload()
- JavaScript SDK version → 0.4.0
- Enhanced LtpClientOptions with secretKey and enableSignatureVerification
- HMAC-SHA256 message authentication
- Timing-safe signature comparison
- Foundation for E2E encryption
100% backward compatible with v0.3.x. Crypto features are opt-in.
Critical Security Enhancements - Production Ready
-
[CRITICAL] Fixed non-cryptographic random in JavaScript SDK
- Replaced
Math.random()with Web Crypto API (crypto.getRandomValues()) for nonce generation - Replaced
Math.random()with Node.jscrypto.randomBytes()for server environments - Added fallback to UUID v4 for environments without crypto support
- Locations:
sdk/js/src/client.ts:735-817
- Replaced
-
[CRITICAL] Fixed non-cryptographic random in server examples
- Replaced
Math.random()withcrypto.randomBytes()inattachSecurity()function - Locations:
examples/js-minimal-server/index.js:58
- Replaced
-
[MEDIUM] Enhanced Python SDK nonce generation
- Replaced
uuid4().hex[:6]withsecrets.token_hex(8)for stronger randomness - Now uses cryptographically secure random throughout
- Locations:
sdk/python/ltp_client/client.py:407-409
- Replaced
-
Rate Limiting - Production-ready rate limiting in server examples
- Per-client message rate limiting (100 messages/minute default)
- Automatic cleanup of expired rate limit entries
RATE_LIMIT_EXCEEDEDerror response- Locations:
examples/js-minimal-server/index.js:19-51, 192-206
-
Replay Attack Protection - Comprehensive nonce validation example
- Nonce uniqueness validation with TTL cache
- Timestamp drift detection (max 1 minute)
- Client ID verification in nonce
- Production implementation guide in
DEPLOYMENT.md - Locations:
DEPLOYMENT.md:708-807
-
Security Hardening Guide - Complete production security guide
SECURITY_HARDENING.md- 500+ line comprehensive security guide- Cryptographic best practices for all SDKs
- TLS 1.3+ configuration examples
- Authentication & authorization patterns
- DoS protection strategies
- Session management security
- Monitoring & logging recommendations
- Production deployment checklist
-
Updated all SDKs to use cryptographically secure random number generation
- JavaScript/TypeScript: Web Crypto API + Node.js crypto module
- Python:
secretsmodule (CSPRNG) - Elixir:
:crypto.strong_rand_bytes()(already secure) - Rust:
uuidcrate with crypto RNG (already secure)
-
Enhanced server example security
- Added rate limiting middleware
- Added connection cleanup
- Improved error responses for security violations
- Added
SECURITY_HARDENING.md- Production security guide - Updated
DEPLOYMENT.mdwith replay attack protection examples - Enhanced security sections in all documentation
- Added security checklist for production deployments
From v0.3.0 to v0.3.1:
No breaking changes. All security enhancements are backward compatible.
Recommended actions:
- Update all SDKs to v0.3.1+
- Review
SECURITY_HARDENING.mdfor production deployments - Implement rate limiting in production servers
- Consider adding replay attack protection (see
DEPLOYMENT.md) - Verify TLS 1.3+ is configured
Risk Assessment:
- v0.3.0 and earlier: Not recommended for production (weak random in JS SDK)
- v0.3.1+: Production ready with proper security hardening
0.3.0 - 2025-01-15
- TOON (Token-Oriented Object Notation) support - Optional compact payload encoding for large arrays
content_encodingfield in LTP envelope (v0.3+)LtpCodecinterface for TOON encoding/decodingpreferredEncodingclient option- Structured logging with
LtpLoggerinterface - Advanced examples for all SDKs:
- Production-ready client wrappers
- Event-driven architecture patterns
- Async worker pools
- Supervised clients (Elixir)
- Concurrent operations (Rust)
- Performance benchmarks:
- JSON vs TOON size/performance comparison
- Throughput benchmarks
- Comprehensive documentation:
ARCHITECTURE.md- Ecosystem architecture overviewDEPLOYMENT.md- Production deployment guideAPI.md- Complete API referenceCONTRIBUTING.md- Contribution guidelines
- Updated protocol version to v0.3
- Updated SDK versions to 0.3.0 (JS, Python)
- Enhanced error handling and logging across all SDKs
- Improved test coverage and stability
- Elixir SDK test stability issues
- Dependency resolution for Elixir SDK
- Process isolation in Elixir tests
0.2.0 - 2024-12-XX
- Thread persistence with
handshake_resume - Heartbeat mechanism (ping/pong)
- Automatic reconnection with exponential backoff
- Security skeleton (nonce/signature fields)
- Multi-language SDK support:
- JavaScript/TypeScript SDK
- Python SDK
- Elixir SDK
- Rust SDK
- Updated protocol version to v0.2
- Enhanced handshake protocol with resume capability
0.1.0 - 2024-XX-XX
- Initial protocol specification
- Basic handshake (
handshake_init) - Message envelope format
- Context preservation metadata
- Liminal metadata (affect, context tags)
- JavaScript SDK implementation
- Python SDK implementation