@hyperfrontend/network-protocol
Production-grade network protocol for secure, real-time cross-window and cross-process communication with built-in encryption, obfuscation, routing, and message queueing.
What is @hyperfrontend/network-protocol?
You already have a transport: a WebSocket, postMessage to another window or a worker, a Node IPC pipe. What you do not have is the envelope to put on it. That is this library. Hand it a function that transmits bytes and a callback for delivered messages, and you get a channel back. Outbound messages are encrypted with a key the two ends exchange, serialized, then obfuscated with a password both sides derive from the current time window. Inbound bytes run the same three steps backwards and arrive as a typed packet with an origin, a target, and a payload that has already been checked for the fields it claims to have.
Every stage is its own FIFO queue that finishes one message before pulling the next, so the async crypto cannot reorder your sends, and either direction can be stopped and resumed. A packet that fails validation or decryption fails inside its stage, gets logged through the logger you passed in, and leaves the rest of the pipeline running.
Most projects should not start here. For typed, contract-checked messages between windows, use @hyperfrontend/nexus, which attaches this protocol through registerProtocol and owns the handshake, the origin policy, and the connection lifecycle. To compose whole applications into a host page, use @hyperfrontend/features, which sits above nexus. Come here directly when you own the transport and want the envelope on your own terms.
At a glance:
import { logger } from '@hyperfrontend/logging'
import { createChannel } from '@hyperfrontend/network-protocol/browser/channel'
import { createData, deserializeData } from '@hyperfrontend/network-protocol/browser/data'
import { createProtocol } from '@hyperfrontend/network-protocol/browser/v2'
const worker = new Worker('./peer.js')
const channel = createChannel(
'worker-bridge',
(bytes) => worker.postMessage(bytes), // your transport, outbound
(packet) => render(packet.origin, packet.data.message), // decrypted, validated, in order
createProtocol(logger, 'pre-shared-secret', 5) // 5 minute obfuscation window
)
worker.addEventListener('message', (event) => channel.receive(event.data)) // your transport, inbound
// pid is a UUID v4 naming the conversation, 1 is the step number within it
const data = deserializeData(await createData(crypto.randomUUID(), 1, { type: 'PING' }))
channel.send('page', 'worker', data)
Key Features
- Multi-layered security protocol - Combines dynamic key encryption, time-based password rotation, and packet obfuscation
- Isomorphic design - Identical APIs for browser (
postMessage) and Node.js (IPC) with platform-specific implementations - Topic-based routing - Pub/sub message distribution with dynamic subscription resolution and WeakMap-based channel tracking
- Staged message queues - Separate queues for each transformation stage (encrypt, serialize, obfuscate, etc.) with independent control
- Protocol versioning - Extensible protocol system with v1 implementation and provider-based configuration
- Structured packet format - Typed packets with origin/target tracking through all transformation stages
- Channel management - Named channels with UUID tracking, lifecycle control (stop/resume), and dedicated inbound/outbound queues
- Schema validation - Each message carries a generated JSON Schema and a hash of it, so the receiver can check the shape it was sent
Architecture Highlights
The protocol implements a functional pipeline architecture where each transformation stage (encryption, serialization, obfuscation) operates independently through dedicated queues. Packets progress through typed transformations: UnencryptedPacket<T> → UnserializedEncryptedPacket → SerializedEncryptedPacket → ObfuscatedPacket (and reverse for inbound). Platform-specific implementations inject dependencies (crypto functions, transport mechanisms) through factory patterns, maintaining pure business logic in the shared lib layer. The v1 protocol uses time-based password generation from @hyperfrontend/cryptography for dynamic encryption keys and obfuscation passwords, refreshing at configurable intervals.
Why Use @hyperfrontend/network-protocol?
You own the transport and do not want to invent the envelope
Raw postMessage and IPC give you bytes and nothing else. Everything above them is yours to build: a key exchange, a serialization format, something that stops two overlapping crypto calls from delivering your messages out of order, and a shape check so a malformed payload never reaches a handler. That layer is small enough to write and easy to get subtly wrong. It is the part this library ships, and it is the only part it ships. Your socket, your window, your pipe stays yours.
Two encryption layers, and you can see both of them
The payload is encrypted with a key the peers exchange in the first packet. The serialized packet is then obfuscated with a password both ends derive from the current time window, so what crosses the wire does not present itself as a recognizable ciphertext envelope. Deobfuscation retries with the previous and next windows, which is what keeps a few seconds of clock drift from dropping traffic. V2 adds a pre-shared key so that even the first packet, the one carrying the exchange key, is encrypted. Whether that trade is worth the key distribution problem depends on your transport, and the V1 versus V2 table below is the short answer.
Ordering and backpressure come from the queues, not from a promise
Each transformation is its own FIFO queue that awaits one message before pulling the next, so encryption timing cannot shuffle your sends. channel.stop() pauses both directions, channel.resume() drains them, and channel.outbound.encryptionQueue.size tells you how far behind you are. Failures are contained: a packet that will not validate or decrypt fails inside its stage, goes to the logger you injected, and the channel keeps running.
The same code in a browser and in Node
/browser/* and /node/* export the same functions with the same signatures. Only the crypto and the transport injection differ, so an Electron main process and its renderer, or a page and a worker, can share channel and routing code and swap one import. Every piece is a factory that takes its dependencies as arguments, so replacing the encryption suite, the obfuscation suite, or the serialization step means passing a different function, not forking the pipeline.
Installation
npm install @hyperfrontend/network-protocol
Requirements
- Node.js: 18.0.0 or higher (19+ recommended for stable Web Crypto API support)
- npm: 8.0.0 or higher
- Browser: Modern browsers with Web Crypto API support
Note: The
/node/*entry points depend on@hyperfrontend/cryptographywhich useswebcrypto.subtle. This API was experimental in Node.js 18.x. For production use with Node.js entry points, Node.js 19+ is recommended.
Quick Start
Browser: cross-window messages
import { logger } from '@hyperfrontend/logging'
import { createChannel } from '@hyperfrontend/network-protocol/browser/channel'
import { createData, deserializeData } from '@hyperfrontend/network-protocol/browser/data'
import { createProtocol } from '@hyperfrontend/network-protocol/browser/v1'
// createProtocol(logger, refreshRate) returns a provider; refreshRate is the obfuscation window in minutes
const protocolProvider = createProtocol(logger, 5)
// the channel owns the pipeline, you own the transport on both sides of it
const channel = createChannel(
'window-link',
(bytes) => otherWindow.postMessage(bytes, 'https://app.example.com'),
(packet) => console.log('from', packet.origin, packet.data.message),
protocolProvider
)
window.addEventListener('message', (event) => {
if (event.origin === 'https://app.example.com') channel.receive(event.data)
})
// the pid stays the same across the steps of one conversation, the sequence number counts them
const pid = crypto.randomUUID()
const data = deserializeData(await createData(pid, 1, { greeting: 'Hello' }))
channel.send('window-a', 'window-b', data)
// pause and drain either direction
channel.stop()
channel.resume()
Node.js: the same channel between threads
import { Worker } from 'node:worker_threads'
import { logger } from '@hyperfrontend/logging'
import { createChannel } from '@hyperfrontend/network-protocol/node/channel'
import { createData, deserializeData } from '@hyperfrontend/network-protocol/node/data'
import { createProtocol } from '@hyperfrontend/network-protocol/node/v1'
const worker = new Worker('./worker.js')
const channel = createChannel(
'thread-link',
(bytes) => worker.postMessage(bytes),
(packet) => handle(packet.data.message),
createProtocol(logger, 5)
)
worker.on('message', (bytes: Uint8Array) => channel.receive(bytes))
const data = deserializeData(await createData(crypto.randomUUID(), 1, { job: 'resize', file: 'a.png' }))
channel.send('parent', 'worker', data)
API Overview
Core Exports
Modular Entry Points (tree-shakeable):
@hyperfrontend/network-protocol/channel- Channel creation, management, and stores@hyperfrontend/network-protocol/routing- Router configuration and topic-based routing@hyperfrontend/network-protocol/security- Security suites (encryption + obfuscation)@hyperfrontend/network-protocol/queue- Message queue creation and management@hyperfrontend/network-protocol/topic- Topic creation and stores
Platform-Specific Protocols:
@hyperfrontend/network-protocol/browser/v1- V1 protocol with obfuscation-only handshake@hyperfrontend/network-protocol/browser/v2- V2 protocol with PSK-encrypted handshake@hyperfrontend/network-protocol/node/v1- Node.js V1 protocol@hyperfrontend/network-protocol/node/v2- Node.js V2 protocol
Protocol Versions
V1: Obfuscation-Only Handshake
The V1 protocol (createObfuscatedHandshakeProtocolFactory) uses time-based obfuscation only for the initial handshake message. During handshake:
- First message: Sent with obfuscation only (no encryption) - the encryption key is transmitted in the packet payload
- Subsequent messages: Encrypted with dynamically captured keys plus time-based obfuscation
This approach is suitable when the transport layer already provides some level of security or when PSK distribution is not feasible.
import { createProtocol } from '@hyperfrontend/network-protocol/browser/v1'
// createProtocol is an alias for createObfuscatedHandshakeProtocolFactory
V2: PSK-Encrypted Handshake
The V2 protocol (createPSKHandshakeProtocolFactory) adds a Pre-Shared Key (PSK) layer for securing the initial handshake:
- First message: Encrypted with the PSK + time-based obfuscation - protects the encryption key during transmission
- Subsequent messages: Encrypted with dynamically captured keys plus time-based obfuscation
This provides defense-in-depth during handshake, protecting the dynamic key exchange from eavesdropping.
import { createProtocol } from '@hyperfrontend/network-protocol/browser/v2'
// createProtocol is an alias for createPSKHandshakeProtocolFactory
// Usage requires a shared key known to both parties
const createMyProtocol = createProtocol(logger, 'my-shared-secret', 60000)
Choosing Between V1 and V2
| Use Case | Recommended Protocol |
|---|---|
| TLS-protected transport | V1 (obfuscation-only) |
| Untrusted transport, can share PSK | V2 (PSK handshake) |
| Key exchange protection critical | V2 (PSK handshake) |
| No PSK distribution mechanism | V1 (obfuscation-only) |
Note: Both protocols use dynamic key encryption for all messages after the handshake. The only difference is how the first message (containing the dynamic encryption key) is protected.
Additional Modules:
/browser/data,/node/data- Data transformation utilities/browser/packet,/node/packet- Packet operations (encrypt, decrypt, obfuscate, etc.)/browser/sender,/node/sender- Outbound message handling/browser/receiver,/node/receiver- Inbound message handling/browser/channel,/node/channel- Platform-specific channel implementations
Main Types
Protocol<T>- Complete protocol implementation with encryption, obfuscation, send/receiveProtocolProvider<T>- Factory function for creating protocol instancesChannel<T>- Named communication channel with queues and routingRouter- Function configuring topic-to-channel subscriptionsTopic- Named message category for routingPacket<T>- Union of all packet types (obfuscated, encrypted, unencrypted)Queue<T>- Message queue with processing and backpressure control
Documentation
Comprehensive Guides
- ARCHITECTURE.md - In-depth architecture guide with composition diagrams, factory reference table, and "How Do I..." quick reference
- src/lib/README.md - Module index with links to all subdomain documentation
Module Documentation
Each module has its own README with purpose, interfaces, factory functions, and usage examples:
| Module | Description | Documentation |
|---|---|---|
| channel/ | Bidirectional communication channels | README |
| packet/ | Packet type hierarchy & transformations | README |
| protocol/ | Protocol composition & v1 implementation | README |
| security/ | Encryption & obfuscation suites | README |
| queue/ | FIFO message processing queues | README |
| sender/ | Outbound message pipeline | README |
| receiver/ | Inbound message pipeline | README |
| data/ | Structured message payloads | README |
| routing/ | Topic-based message routing | README |
| topic/ | Topic store management | README |
Platform Entry Points
- src/browser/README.md - Browser platform documentation
- src/node/README.md - Node.js platform documentation
Integration Tests
Living documentation through executable examples:
channel/channel.integration.spec.ts- Channel composition and bidirectional communicationpacket/packet-transformations.integration.spec.ts- Full packet type transitionspacket/security/encryption.integration.spec.ts- Real encryption/decryption cyclespacket/security/obfuscation.integration.spec.ts- Time-based obfuscation with clock skew handlingsender/sender.integration.spec.ts- Full outbound queue chainreceiver/receiver.integration.spec.ts- Full inbound queue chainsender-receiver.integration.spec.ts- Round-trip message flowsecurity/security-suite.integration.spec.ts- Combined encryption + obfuscationrouting/routing.integration.spec.ts- Topic-based message routingqueue/queue.integration.spec.ts- Queue creation, message flow, stop/resumedata/data.integration.spec.ts- Data creation with real hashing
Compatibility
| Platform | Support |
|---|---|
| Browser | ✅ |
| Node.js | ✅ |
| Web Workers | ✅ |
| Deno, Bun, Cloudflare Workers | ✅ |
Output Formats
| Format | File | Tree-Shakeable |
|---|---|---|
| ESM | *.esm.js | ✅ |
| CJS | *.cjs.js | ❌ |
| IIFE | bundle/v1/index.iife.min.js, bundle/v2/index.iife.min.js | ❌ |
| UMD | bundle/v1/index.umd.min.js, bundle/v2/index.umd.min.js | ❌ |
CDN Usage
This library provides separate bundles for each protocol version:
<!-- Protocol V2 (recommended) -->
<script src="https://unpkg.com/@hyperfrontend/network-protocol/bundle/v2/index.umd.min.js"></script>
<!-- Protocol V1 -->
<script src="https://unpkg.com/@hyperfrontend/network-protocol/bundle/v1/index.umd.min.js"></script>
<script>
// V2
const { createProtocol } = HyperfrontendNetworkProtocolV2
// V1
const { createProtocol } = HyperfrontendNetworkProtocolV1
</script>
Global variables: HyperfrontendNetworkProtocolV1, HyperfrontendNetworkProtocolV2
Part of hyperfrontend
This library is part of the hyperfrontend monorepo.
- Uses @hyperfrontend/cryptography for encryption and time-based password generation
- For simpler cross-window messaging with contracts, see @hyperfrontend/nexus
License
Guides & tutorials for @hyperfrontend/network-protocol
There are none for @hyperfrontend/network-protocol yet.Request one
API Reference§
Module Structure
18 modules · 263 total exports
@hyperfrontend/network-protocol/browser/channel
Browser-side channel management for secure bidirectional communication.
@hyperfrontend/network-protocol/browser/data
Browser-side data encryption, serialization, and schema validation utilities.
@hyperfrontend/network-protocol/browser/packet
Browser-side packet encryption, serialization, and obfuscation with dynamic key support.
@hyperfrontend/network-protocol/browser/receiver
Browser-side inbound packet receiver with deserialization and decryption.
@hyperfrontend/network-protocol/browser/sender
Browser-side outbound packet sender with serialization and encryption.
@hyperfrontend/network-protocol/browser/v1
V1 protocol for browser with dynamic key exchange and time-based obfuscation.
@hyperfrontend/network-protocol/browser/v2
V2 protocol for browser with pre-shared key handshake encryption.
@hyperfrontend/network-protocol/node/channel
Node.js-side channel management for secure bidirectional communication.
@hyperfrontend/network-protocol/node/data
Node.js-side data encryption, serialization, and schema validation utilities.
@hyperfrontend/network-protocol/node/packet
Node.js-side packet encryption, serialization, and obfuscation with dynamic key support.
@hyperfrontend/network-protocol/node/receiver
Node.js-side inbound packet receiver with deserialization and decryption.
@hyperfrontend/network-protocol/node/sender
Node.js-side outbound packet sender with serialization and encryption.
@hyperfrontend/network-protocol/node/v1
V1 protocol for Node.js with dynamic key exchange and time-based obfuscation.
@hyperfrontend/network-protocol/node/v2
V2 protocol for Node.js with pre-shared key handshake encryption.
@hyperfrontend/network-protocol/queue
Message queue factories for encryption, serialization, and obfuscation pipelines.
@hyperfrontend/network-protocol/routing
Packet routing utilities with subscription management and routed packet creators.
@hyperfrontend/network-protocol/security
Security type definitions for encryption and obfuscation suites.
@hyperfrontend/network-protocol/topic
Topic-based message pub/sub management with store creation.