@hyperfrontend/ network-protocol§
Production-grade network protocol for secure, real-time cross-window and cross-process communication with a session-keyed authenticated envelope, 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, a callback for delivered messages, a protocol provider, and the session the two ends agreed on, and you get a channel back.
Each end mints a random nonce and an ephemeral P-256 key pair, advertises them in a 99-byte hello frame, and derives two AES-GCM-256 keys from the agreement, one per direction. Every message is sealed under the sending key with a counter that serves as the nonce and as the replay check; every inbound frame is opened under the receiving key and arrives as a typed packet with an origin, a target, and a payload that has already been checked for the fields it claims to have.
Two protocols share that wire format. v3 keys the session from the agreement alone, which defeats anything that can only listen. v4 mixes a pre-shared key into the schedule, stretched once per session, so a script without the key can neither read frames nor produce frames the counterpart accepts.
Outbound and inbound are each one FIFO stage that finishes a frame before pulling the next, so the async crypto cannot reorder your sends, and either direction can be stopped and resumed. A frame that fails to open (replayed, forged, malformed, from another session) is dropped inside its stage, reported through onDrop with a machine-readable code, 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 registers this library's exports as a security provider and owns the handshake, the origin policy, the hello retries, 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, the page side of a page-to-worker link:
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/v3'
const worker = new Worker('./peer.js')
const pageId = crypto.randomUUID()
const workerId = crypto.randomUUID()
const channel = createChannel('worker-bridge', {
send: (frame) => worker.postMessage(frame, [frame.buffer]), // your transport, outbound
receive: (packet) => render(packet.origin, packet.data.message), // opened, validated, in order
protocolProvider: createProtocol(logger),
session: {
protocol: 'v3',
role: 'initiator',
localId: pageId,
peerId: workerId,
},
})
worker.addEventListener('message', ({ data }) => {
if (channel.isHello(data))
channel.acceptHello(data) // the worker's public material keys the session
else channel.receive(data) // everything else is a sealed frame
})
worker.postMessage(await channel.hello()) // this side's public material, in the clear by design
// pid is a UUID v4 naming the conversation, 1 is the step number within it
const pid = crypto.randomUUID()
const data = deserializeData(await createData(pid, 1, { type: 'PING' }))
channel.send(pageId, workerId, data) // waits inside the seal stage until the worker's hello has arrived
The worker mirrors this with role: 'responder' and the two identities swapped; how the identities reach it is up to your transport (nexus carries them in its handshake).
Key Features
Session-keyed authenticated envelope
Each session is keyed from an ephemeral P-256 agreement, expanded with HKDF-SHA256 into one AES-GCM-256 key per direction; every frame carries its header as additional authenticated data
Two protocols, one wire format
v3keys the session from the agreement alone;v4mixes in a pre-shared key stretched once per session with PBKDF2-SHA256 (600,000 iterations)Replay rejection before decryption
Frame counters start at 1 and must increase; a frame whose counter is not above the last accepted one is refused before any key is touched
The first hello keys the session, a repeat of it is a duplicate, anything else is rejected, and a live session is never rekeyed
Isomorphic design
Identical APIs for browser (
postMessage) and Node.js (IPC); a browser-composed side and a Node-composed side produce identical frames and interoperateOne seal stage outbound and one open stage inbound, each an array-backed FIFO that finishes a frame before pulling the next, with independent stop/resume
onDropreceives every packet either stage discards, with the direction, the stage, the reason, and the protocol error that caused itSix protocol error codes (
unsupported-version,replayed,authentication-failed,malformed,counter-exhausted,invalid-session) survive the drop reportPub/sub message distribution with dynamic subscription resolution and WeakMap-based channel tracking
Named channels with UUID tracking, lifecycle control (stop/resume), and per-direction queue depth
Each message carries a generated JSON Schema and a hash of it, so the receiver can check the shape it was sent
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 framing format, replay protection, 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.
A real key schedule, and you can read all of it
Each side contributes a 32-byte nonce and an ephemeral P-256 public key in its hello. The salt is the two nonces in role order, the input key material is the ECDH shared secret (v3) or the shared secret followed by a PBKDF2 stretch of the pre-shared key (v4), and two keys come out of HKDF-SHA256 under info strings that name the protocol and both identities. Each side seals with its own direction's key and opens with the other's, raw material is zeroed after derivation, and the AES-GCM nonce is the frame counter, unique by construction. What each protocol promises is stated claim by claim in the architecture guide; neither hides the hello, because public keys and nonces are public by design.
Ordering and backpressure come from the queues, not from a promise
Each direction is one FIFO stage that awaits one frame before pulling the next, so crypto timing cannot shuffle your sends and the replay counter stays exact. channel.stop() pauses both directions, channel.resume() drains them, and channel.outbound.queue.size tells you how far behind you are. Failures are contained: a packet that will not seal or a frame that will not open fails inside its stage, goes to the logger you injected and to onDrop, 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 text-codec 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. A session keyed by a browser-composed initiator and a Node-composed responder works, because both sides derive the same keys from the same bytes.
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
A session needs two things agreed before it starts: which side is the initiator and which the responder, and the two identities (localId on one side is peerId on the other). Both are bound into the derived keys. Identities are public; here the host picks them and tells the guest in plaintext.
Browser: cross-window messages with v4
The host, in the top window:
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/v4'
const guest = document.querySelector('iframe').contentWindow
const guestOrigin = 'https://app.example.com'
const hostId = crypto.randomUUID()
const guestId = crypto.randomUUID()
// createProtocol(logger, sharedKey) returns a provider; the key must be at least 16 characters and should be generated, not chosen
const channel = createChannel('window-link', {
send: (frame) => guest.postMessage(frame, guestOrigin, [frame.buffer]),
receive: (packet) => console.log('from', packet.origin, packet.data.message),
protocolProvider: createProtocol(logger, sharedKey),
session: {
protocol: 'v4',
role: 'initiator',
localId: hostId,
peerId: guestId,
},
})
window.addEventListener('message', ({ origin, data }) => {
if (origin !== guestOrigin) return
if (channel.isHello(data)) channel.acceptHello(data)
else channel.receive(data)
})
guest.postMessage({ hostId, guestId }, guestOrigin) // identities, in the clear
guest.postMessage(await channel.hello(), guestOrigin) // public material, in the clear
// 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(hostId, guestId, data) // sealed once the guest's hello has keyed the session
// pause and drain either direction
channel.stop()
channel.resume()
The guest, inside the iframe:
import type { Channel } from '@hyperfrontend/network-protocol/browser/channel'
import { logger } from '@hyperfrontend/logging'
import { createChannel } from '@hyperfrontend/network-protocol/browser/channel'
import { createProtocol } from '@hyperfrontend/network-protocol/browser/v4'
const hostOrigin = 'https://host.example.com'
let channel: Channel | null = null
window.addEventListener('message', ({ origin, data }) => {
if (origin !== hostOrigin) return
if (channel === null) {
// the first message carries the identities; everything after it is a frame
const { hostId, guestId } = data
channel = createChannel('window-link', {
send: (frame) => window.parent.postMessage(frame, hostOrigin, [frame.buffer]),
receive: (packet) => console.log('from', packet.origin, packet.data.message),
protocolProvider: createProtocol(logger, sharedKey),
session: {
protocol: 'v4',
role: 'responder',
localId: guestId,
peerId: hostId,
},
})
channel.hello().then((hello) => window.parent.postMessage(hello, hostOrigin))
return
}
if (channel.isHello(data)) channel.acceptHello(data)
else channel.receive(data)
})
A guest holding a different key derives different keys: every frame from the host fails to authenticate and is reported through onDrop with code authentication-failed, and nothing is delivered.
Node.js: the same channel between threads with v3
The main thread:
import { randomUUID } from 'node:crypto'
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/v3'
const mainId = randomUUID()
const workerId = randomUUID()
const worker = new Worker('./worker.js', { workerData: { mainId, workerId } })
const channel = createChannel('thread-link', {
send: (frame) => worker.postMessage(frame, [frame.buffer]),
receive: (packet) => handle(packet.data.message),
protocolProvider: createProtocol(logger),
session: {
protocol: 'v3',
role: 'initiator',
localId: mainId,
peerId: workerId,
},
})
worker.on('message', (frame: Uint8Array) => (channel.isHello(frame) ? channel.acceptHello(frame) : channel.receive(frame)))
worker.postMessage(await channel.hello())
const data = deserializeData(
await createData(randomUUID(), 1, {
job: 'resize',
file: 'a.png',
})
)
channel.send(mainId, workerId, data)
The worker (worker.js):
import { parentPort, workerData } from 'node:worker_threads'
import { logger } from '@hyperfrontend/logging'
import { createChannel } from '@hyperfrontend/network-protocol/node/channel'
import { createProtocol } from '@hyperfrontend/network-protocol/node/v3'
const { mainId, workerId } = workerData
const channel = createChannel('thread-link', {
send: (frame) => parentPort.postMessage(frame, [frame.buffer]),
receive: (packet) => handle(packet.data.message),
protocolProvider: createProtocol(logger),
session: {
protocol: 'v3',
role: 'responder',
localId: workerId,
peerId: mainId,
},
})
parentPort.on('message', (frame: Uint8Array) => (channel.isHello(frame) ? channel.acceptHello(frame) : channel.receive(frame)))
parentPort.postMessage(await channel.hello())
Hello frames are posted by copy and sealed frames by transfer: the protocol hands out the same hello bytes on every call, so a transport may re-post it until the counterpart answers, while each sealed frame owns its buffer and is never needed again once sent.
API Overview
Nothing is exported from the package root: every import names a subpath, and the subpaths sit on two axes. One is the platform that supplies the crypto and text primitives, /browser or /node. The other is the protocol version that keys the session, /v3 or /v4. A browser subpath and its Node twin export the same names with the same signatures, and a session between the two works because both sides derive their keys from the same bytes, so moving code between a page and a worker, or between a renderer and a main process, is an edit to the import path and nothing else.
Two calls get a link running. createProtocol takes your logger, and on /v4 the pre-shared key as well, and hands back a provider. createChannel takes a label and an options object holding the send and receive callbacks that reach your transport, that provider, and the session the two ends agreed on.
const channel = createChannel(label, {
send,
receive,
protocolProvider,
session,
onDrop,
})
// session: { protocol: 'v3' | 'v4', role: 'initiator' | 'responder', localId, peerId }
What comes back is a Channel, and for most code it is the only object in play: the hello exchange your transport carries, send(origin, target, data) for traffic, stop and resume for both directions at once, and outbound and inbound for per-direction queue depth. Nothing rejects. A frame that will not seal or will not open is dropped inside its own stage and reported to your onDrop as a PacketDrop, while the rest of the pipeline keeps running.
The version is the security decision, and it is the only thing that differs between /v3 and /v4. v3 keys the session from the ephemeral agreement alone, which defeats anything that can only listen but says nothing about who the counterpart is. v4 mixes in a pre-shared key, stretched once when the session is keyed, so a script without that key can neither read frames nor produce frames the far side accepts; it carries two extra exports for that key, isValidSharedKey and MIN_SHARED_KEY_LENGTH, and otherwise mirrors /v3. Both are built on the same session protocol: the hello exchange, the key schedule, and the seal and open pipeline.
Four subpaths name no platform because they hold no crypto. /queue is the FIFO stage both pipelines are built from, /routing and /topic are the pub/sub layer that names message categories and decides which channels a topic's messages reach, and /security holds the session types and the six ProtocolErrorCode values a drop can carry. The remaining per-platform subpaths are for taking one piece rather than a whole channel: /data builds the message envelope, stamping a conversation id, a sequence number and a schema hash onto your payload, /packet holds the packet builders and validators, and /sender and /receiver are the outbound and inbound halves on their own.
Every option, type and validator is in the API reference. The claims above are exercised end to end by the session envelope integration test, which wires two channels in memory over v3 and v4 and checks delivery in both directions, sealed bytes only on the wire, order across a burst, replay and foreign-session rejection reported as drops, a key mismatch delivering nothing, and a browser-composed side talking to a Node-composed side.
Compatibility
Runs on
- Node.js>=18.0.0supported
- Browserssupported
- Web Workerssupported
Ships as
- ESMTree-shakeable
- CJSNode and older bundlers
- IIFE2 bundles
- UMD2 bundles
CDN Usage
This library provides separate bundles for each protocol; the package's default CDN file is the v4 UMD bundle:
<!-- v4 (the package default) -->
<script src="https://unpkg.com/@hyperfrontend/network-protocol/bundle/v4/index.umd.min.js"></script>
<!-- v3 -->
<script src="https://unpkg.com/@hyperfrontend/network-protocol/bundle/v3/index.umd.min.js"></script>
<script>
// v4
const { createProtocol: createV4 } = HyperfrontendNetworkProtocolV4
// v3
const { createProtocol: createV3 } = HyperfrontendNetworkProtocolV3
</script>
Global variables: HyperfrontendNetworkProtocolV3, HyperfrontendNetworkProtocolV4
Architecture Highlights§
The key schedule binds both nonces, both public keys, the protocol id, and both identities into the derived keys, so a frame from any other session fails to authenticate. The cost is one ECDH agreement plus one HKDF expansion per direction per session (plus one PBKDF2 stretch for v4), then one AES-GCM operation per message in each direction; the stretch runs once, when the session is keyed, so it lands on the handshake and not on traffic.
The architecture guide works through the hello exchange, the wire format, the injected platform primitives, and what each protocol does and does not claim.
API Reference§
Module Structure
18 modules · 237 total exports
Browser-side channel management for secure bidirectional communication.
ƒ Functions
Parameters
| Name | Type | Description |
|---|---|---|
§protocol | unknown | The protocol object to inspect |
Returns
stringExample
Reporting a protocol missing its opener
getFirstInvalidProtocolProperty({ seal, send, receive, getLogger })
// => 'open'Parameters
| Name | Type | Description |
|---|---|---|
§channel | unknown | The value to check |
Returns
booleanChannelExample
Guarding a channel before storing it
isValidChannel(createChannel('comms', options))
// => trueParameters
| Name | Type | Description |
|---|---|---|
§label | string | The label string to validate |
Returns
booleanExample
Validating channel labels
isValidLabel('channel-1') // => true
isValidLabel('') // => falseParameters
| Name | Type | Description |
|---|---|---|
§receiver | unknown | The receiver to validate |
Returns
booleanExample
Validating receiver functions
isValidReceiver((packet) => handlePacket(packet)) // => true
isValidReceiver('not-a-function') // => falseParameters
| Name | Type | Description |
|---|---|---|
§sender | unknown | The sender to validate |
Returns
booleanExample
Validating sender functions
isValidSender((packet) => transmit(packet)) // => true
isValidSender(null) // => falseParameters
| Name | Type | Description |
|---|---|---|
§session | unknown | The value to check |
Returns
unknownProtocolSessionExample
Guarding a session before binding a protocol to it
isValidSession({ protocol: 'v3', role: 'initiator', localId, peerId })
// => true◈ Interfaces
Properties
Properties
Properties
readonly create:( label: string, options: ChannelOptions< T>) => Channel< T>Creates and returns a new channelProperties
Properties
Properties
Properties
readonly direction:"inbound" | "outbound"Whether the packet was leaving (outbound) or arriving (inbound)Properties
Properties
◆ Types
type ChannelCreater = ( label: string, options: ChannelOptions< T>) => Channel< T>accepted: the peer's material is now known and the session can be keyedduplicate: the same material was already accepted; a retry, ignoredrejected: the frame is not a hello this session can use
type HelloOutcome = "accepted" | "duplicate" | "rejected"type PacketDropHandler = ( drop: PacketDrop) => voidtype PacketDropStage = "seal" | "open"type ProtocolProvider = ( send: SendPacketFn, receive: ReceivePacketFn< T>, session: ProtocolSession) => Protocol< T>type SessionRole = "initiator" | "responder"● Variables
Browser-side data envelope factory and validators; the schema hash comes from the Web Crypto API.
ƒ Functions
Parameters
| Name | Type | Description |
|---|---|---|
§value | string | The string value to cast as JSONString |
Returns
JSONString< T>Example
Casting a validated string
const jsonStr = asJSONString<User>('{"name":"Alice"}')Parameters
| Name | Type | Description |
|---|---|---|
§serialized | SerializedData< | The serialized data to deserialize |
Returns
Data< T>Example
Deserializing data with JSON message
const data = deserializeData({ ...metadata, message: '{"action":"update"}' })
// => { ...metadata, message: { action: 'update' } }Parameters
| Name | Type | Description |
|---|---|---|
§value | unknown | The value to check |
Returns
unknownExample
Checking JSON string types
isJSONString('{"key":"value"}')
// => true
isJSONString({ key: 'value' })
// => falseParameters
| Name | Type | Description |
|---|---|---|
§id | unknown | The value to validate as a data message ID |
Returns
booleanExample
Validating data message IDs
isValidId('550e8400-e29b-41d4-a716-446655440000')
// => true
isValidId('invalid-id')
// => falseParameters
| Name | Type | Description |
|---|---|---|
§message | T | The message to validate |
Returns
booleanExample
Validating message serializability
isValidMessage({ name: 'Alice', items: [1, 2, 3] })
// => true
isValidMessage({ callback: () => {} })
// => false (functions are not serializable)Parameters
| Name | Type | Description |
|---|---|---|
§pid | unknown | The value to validate as a protocol ID |
Returns
booleanExample
Validating protocol IDs
isValidPid('550e8400-e29b-41d4-a716-446655440000')
// => true
isValidPid('not-a-uuid')
// => falseParameters
| Name | Type | Description |
|---|---|---|
§schema | unknown | The value to validate as a JSON Schema |
Returns
booleanExample
Validating JSON schemas
isValidSchema({ type: 'object', properties: { name: { type: 'string' } } })
// => true
isValidSchema({ type: 'invalid-type' })
// => falseParameters
| Name | Type | Description |
|---|---|---|
§schemaHash | unknown | The value to validate as a schema hash |
Returns
booleanExample
Validating schema hashes
isValidSchemaHash('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855')
// => true
isValidSchemaHash('invalid-hash')
// => falseParameters
| Name | Type | Description |
|---|---|---|
§sequence | unknown | The value to validate as a sequence number |
Returns
booleanExample
Validating sequence numbers
isValidSequence(1)
// => true
isValidSequence(0)
// => falseParameters
| Name | Type | Description |
|---|---|---|
§data | unknown | The value to validate as unencrypted data |
Returns
booleanExample
Validating complete unencrypted data objects
isValidUnencryptedData({
pid: '550e8400-e29b-41d4-a716-446655440000',
id: '6ba7b810-9dad-11d1-80b4-00c04fd430c8',
sequence: 1,
message: { action: 'update' },
schema: { type: 'object' },
schemaHash: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
})
// => trueParameters
| Name | Type | Description |
|---|---|---|
§jsonString | JSONString< | The JSON string to parse |
Returns
TExample
Parsing a JSON string to typed object
const user = parseJSONString<User>('{"name":"Alice"}')
// => { name: 'Alice' }Returns
SerializedData< T>Example
Serializing data for transmission
const serialized = serializeData({ ...metadata, message: { action: 'update' } })
// => { ...metadata, message: '{"action":"update"}' }◈ Interfaces
Properties
Properties
Properties
◆ Types
type DataCreater = ( pid: string, sequence: number, message: T) => Promise< SerializedData< T>>type JSONString = string & JSONStringBrand< T>type SchemaCreater = ( data: unknown) => Schema● Variables
Browser-side packet types, builders, and validators.
ƒ Functions
Parameters
Returns
PacketBaseExample
Creating a packet base
const base = createPacketBase(
'550e8400-e29b-41d4-a716-446655440000',
'6ba7b810-9dad-11d1-80b4-00c04fd430c8'
)
// => { origin: '550e8400-...', target: '6ba7b810-...' }createUnencryptedPacket< T>( origin: string, target: string, data: Data< T>): UnencryptedPacket< T>
Parameters
Returns
UnencryptedPacket< T>Example
Creating an unencrypted packet
const packet = createUnencryptedPacket(originId, targetId, await createData(pid, 1, { action: 'ping' }))
// => { origin, target, data: { pid, id, sequence, message, schema, schemaHash } }Parameters
| Name | Type | Description |
|---|---|---|
§origin | unknown | The value to validate as a packet origin |
Returns
booleanExample
Validating packet origins
isValidOrigin('550e8400-e29b-41d4-a716-446655440000')
// => true
isValidOrigin('not-a-uuid')
// => falseParameters
| Name | Type | Description |
|---|---|---|
§target | unknown | The value to validate as a packet target |
Returns
booleanExample
Validating packet targets
isValidTarget('6ba7b810-9dad-11d1-80b4-00c04fd430c8')
// => true
isValidTarget('invalid')
// => falseParameters
| Name | Type | Description |
|---|---|---|
§packet | unknown | The value to check |
Returns
unknownUnencryptedPacketExample
Validating a packet before sealing
isValidUnencryptedPacket({ origin, target, data: { pid, id, sequence: 1, message: { action: 'ping' }, schema, schemaHash } })
// => trueParameters
| Name | Type | Description |
|---|---|---|
§packet | unknown | The value to check |
Returns
unknownUint8Array with at least one byteExample
Validating wire packets
isValidWirePacket(new Uint8Array([1, 2, 3]))
// => true
isValidWirePacket({ data: 'not binary' })
// => false◈ Interfaces
Properties
Properties
Properties
readonly direction:"inbound" | "outbound"Whether the packet was leaving (outbound) or arriving (inbound)◆ Types
type Packet = UnencryptedPacket< T> | WirePackettype WirePacket = Uint8Arraytype PacketDropHandler = ( drop: PacketDrop) => voidtype PacketDropStage = "seal" | "open"type PacketOpener = ( packet: WirePacket) => Promise< UnencryptedPacket< T>>type PacketSealer = ( packet: UnencryptedPacket< T>) => Promise< WirePacket>Browser-side inbound pipeline: frames in, opened packets out.
◈ Interfaces
Properties
Properties
◆ Types
onDrop receives each frame the opener rejectstype CreateReceiver = ( label: string, receiver: ReceivePacketFn< T>, logger: Logger, open: PacketOpener< T>, onDrop?: PacketDropHandler) => Receivertype ReceiveFn = ( packet: Uint8Array) => voidtype ReceivePacketFn = ( packet: UnencryptedPacket< T>) => voidtype ReceiverFactory = CreateReceiver● Variables
A frame the opener rejects (forged, replayed, foreign, or malformed) is reported through
onDrop and never delivered.Browser-side outbound pipeline: packets in, sealed frames out.
◈ Interfaces
Properties
Properties
◆ Types
onDrop receives each packet the sealer rejectstype CreateSender = ( label: string, sender: SendPacketFn, logger: Logger, seal: PacketSealer< T>, onDrop?: PacketDropHandler) => Sender< T>type SendFn = ( origin: string, target: string, data: Data< T>) => voidtype SendPacketFn = ( packet: Uint8Array) => void● Variables
send validates the origin, target, and data envelope synchronously and throws in the caller's frame on a malformed packet; everything after that is asynchronous, and a packet the sealer rejects is reported through onDrop rather than thrown.V3 protocol for the browser: a session envelope keyed by an ephemeral P-256 agreement in the handshake and no shared secret, so a listener cannot read the traffic and the peer is unauthenticated.
ƒ Functions
Returns
ProtocolProviderStoreExample
Creating and using a protocol provider store
const store = createProtocolProviderStore()
store.add('websocket', myProtocolProvider)
const provider = store.getByName('websocket')v3 keys each session from an ephemeral key agreement alone: a party that only listens to the hello exchange and the traffic cannot read it, while a party that can post its own hello to a window before the peer's arrives can stand in for the peer. It authenticates frames, rejects replays, and binds traffic to one session, but does not authenticate the peer.
Parameters
| Name | Type | Description |
|---|---|---|
§crypto | SessionCrypto | The platform primitives |
Returns
( logger: Logger) => ProtocolProvidercreateProtocol(logger), which returns the provider a channel binds to a sessionExample
Composing v3 in a browser entry
export const createProtocol = createV3ProtocolFactory(crypto)
const protocolProvider = createProtocol(logger)Parameters
| Name | Type | Description |
|---|---|---|
§name | string | The name to validate |
Returns
booleanExample
Validating protocol names
isValidName('websocket')
// => true
isValidName('')
// => falseParameters
| Name | Type | Description |
|---|---|---|
§protocol | unknown | The protocol object to validate |
Returns
ValidProtocolResultExample
Validating a protocol object
const result = isValidProtocol(myProtocol)
// => { seal: true, open: true, hello: true, isHello: true, acceptHello: true, send: true, receive: true, getLogger: true }
const invalid = isValidProtocol({})
// => { seal: false, open: undefined, ... }Parameters
| Name | Type | Description |
|---|---|---|
§protocolProvider | unknown | The value to validate as a protocol provider |
Returns
booleanExample
Validating a protocol provider function
isValidProtocolProvider(() => protocol)
// => true
isValidProtocolProvider('not-a-function')
// => falseParameters
| Name | Type | Description |
|---|---|---|
§receive | unknown | The value to validate as a receive function |
Returns
booleanExample
Validating a receive function
isValidReceiveFn((packet) => console.log(packet))
// => true
isValidReceiveFn(null)
// => falseParameters
| Name | Type | Description |
|---|---|---|
§send | unknown | The value to validate as a send function |
Returns
booleanExample
Validating a send function
isValidSendFn((packet) => websocket.send(packet))
// => true
isValidSendFn('not-a-function')
// => false◈ Interfaces
Properties
Properties
readonly add:( name: string, protocolProvider: ProtocolProvider< T>) => voidRegisters a new provider with the given nameProperties
readonly expandKey:( ikm: Uint8Array, salt: Uint8Array, info: Uint8Array, usages: unknown) => Promise< CryptoKey>HKDF expansion into a usage-restricted AES-GCM keyreadonly open:( key: CryptoKey, nonce: Uint8Array, additionalData: Uint8Array, sealed: Uint8Array) => Promise< Uint8Array< ArrayBufferLike>>AES-GCM open under a key, nonce, and additional datareadonly seal:( key: CryptoKey, nonce: Uint8Array, additionalData: Uint8Array, plaintext: Uint8Array) => Promise< Uint8Array< ArrayBufferLike>>AES-GCM seal under a key, nonce, and additional datareadonly stretchPassword:( password: string, salt: Uint8Array, options?: StretchOptions) => Promise< Uint8Array< ArrayBufferLike>>PBKDF2 password stretching into raw bits◆ Types
type ValidProtocolResult = mapped● Variables
V4 protocol for the browser: the session envelope keyed by an ephemeral P-256 agreement plus a stretched shared secret, so an outsider cannot join and a key that leaks later cannot open past sessions.
ƒ Functions
createV4ProtocolFactory( crypto: SessionCrypto): ( logger: Logger, sharedKey: string) => ProtocolProvider
v4 keys each session from the same ephemeral key agreement as v3 with the shared key stretched once and mixed in, so a party without the key cannot complete the agreement: product traffic is confidential and authentic against anyone who lacks the key, and a key that leaks later does not expose earlier sessions. A party that can run a hello exchange against this side can test key guesses offline afterwards, which is why the key must be generated, not chosen.
Parameters
| Name | Type | Description |
|---|---|---|
§crypto | SessionCrypto | The platform primitives |
Returns
( logger: Logger, sharedKey: string) => ProtocolProvidercreateProtocol(logger, sharedKey), which returns the provider a channel binds to a sessionExample
Composing v4 in a browser entry
export const createProtocol = createV4ProtocolFactory(crypto)
const protocolProvider = createProtocol(logger, sharedKey)Returns
ProtocolProviderStoreExample
Creating and using a protocol provider store
const store = createProtocolProviderStore()
store.add('websocket', myProtocolProvider)
const provider = store.getByName('websocket')Parameters
| Name | Type | Description |
|---|---|---|
§name | string | The name to validate |
Returns
booleanExample
Validating protocol names
isValidName('websocket')
// => true
isValidName('')
// => falseParameters
| Name | Type | Description |
|---|---|---|
§protocol | unknown | The protocol object to validate |
Returns
ValidProtocolResultExample
Validating a protocol object
const result = isValidProtocol(myProtocol)
// => { seal: true, open: true, hello: true, isHello: true, acceptHello: true, send: true, receive: true, getLogger: true }
const invalid = isValidProtocol({})
// => { seal: false, open: undefined, ... }Parameters
| Name | Type | Description |
|---|---|---|
§protocolProvider | unknown | The value to validate as a protocol provider |
Returns
booleanExample
Validating a protocol provider function
isValidProtocolProvider(() => protocol)
// => true
isValidProtocolProvider('not-a-function')
// => falseParameters
| Name | Type | Description |
|---|---|---|
§receive | unknown | The value to validate as a receive function |
Returns
booleanExample
Validating a receive function
isValidReceiveFn((packet) => console.log(packet))
// => true
isValidReceiveFn(null)
// => falseParameters
| Name | Type | Description |
|---|---|---|
§send | unknown | The value to validate as a send function |
Returns
booleanExample
Validating a send function
isValidSendFn((packet) => websocket.send(packet))
// => true
isValidSendFn('not-a-function')
// => false◈ Interfaces
Properties
Properties
readonly add:( name: string, protocolProvider: ProtocolProvider< T>) => voidRegisters a new provider with the given nameProperties
readonly expandKey:( ikm: Uint8Array, salt: Uint8Array, info: Uint8Array, usages: unknown) => Promise< CryptoKey>HKDF expansion into a usage-restricted AES-GCM keyreadonly open:( key: CryptoKey, nonce: Uint8Array, additionalData: Uint8Array, sealed: Uint8Array) => Promise< Uint8Array< ArrayBufferLike>>AES-GCM open under a key, nonce, and additional datareadonly seal:( key: CryptoKey, nonce: Uint8Array, additionalData: Uint8Array, plaintext: Uint8Array) => Promise< Uint8Array< ArrayBufferLike>>AES-GCM seal under a key, nonce, and additional datareadonly stretchPassword:( password: string, salt: Uint8Array, options?: StretchOptions) => Promise< Uint8Array< ArrayBufferLike>>PBKDF2 password stretching into raw bits◆ Types
type ValidProtocolResult = mapped● Variables
Node.js-side channel management for secure bidirectional communication.
ƒ Functions
Parameters
| Name | Type | Description |
|---|---|---|
§protocol | unknown | The protocol object to inspect |
Returns
stringExample
Reporting a protocol missing its opener
getFirstInvalidProtocolProperty({ seal, send, receive, getLogger })
// => 'open'Parameters
| Name | Type | Description |
|---|---|---|
§channel | unknown | The value to check |
Returns
booleanChannelExample
Guarding a channel before storing it
isValidChannel(createChannel('comms', options))
// => trueParameters
| Name | Type | Description |
|---|---|---|
§label | string | The label string to validate |
Returns
booleanExample
Validating channel labels
isValidLabel('channel-1') // => true
isValidLabel('') // => falseParameters
| Name | Type | Description |
|---|---|---|
§receiver | unknown | The receiver to validate |
Returns
booleanExample
Validating receiver functions
isValidReceiver((packet) => handlePacket(packet)) // => true
isValidReceiver('not-a-function') // => falseParameters
| Name | Type | Description |
|---|---|---|
§sender | unknown | The sender to validate |
Returns
booleanExample
Validating sender functions
isValidSender((packet) => transmit(packet)) // => true
isValidSender(null) // => falseParameters
| Name | Type | Description |
|---|---|---|
§session | unknown | The value to check |
Returns
unknownProtocolSessionExample
Guarding a session before binding a protocol to it
isValidSession({ protocol: 'v3', role: 'initiator', localId, peerId })
// => true◈ Interfaces
Properties
Properties
Properties
readonly create:( label: string, options: ChannelOptions< T>) => Channel< T>Creates and returns a new channelProperties
Properties
Properties
Properties
readonly direction:"inbound" | "outbound"Whether the packet was leaving (outbound) or arriving (inbound)Properties
Properties
◆ Types
type ChannelCreater = ( label: string, options: ChannelOptions< T>) => Channel< T>accepted: the peer's material is now known and the session can be keyedduplicate: the same material was already accepted; a retry, ignoredrejected: the frame is not a hello this session can use
type HelloOutcome = "accepted" | "duplicate" | "rejected"type PacketDropHandler = ( drop: PacketDrop) => voidtype PacketDropStage = "seal" | "open"type ProtocolProvider = ( send: SendPacketFn, receive: ReceivePacketFn< T>, session: ProtocolSession) => Protocol< T>type SessionRole = "initiator" | "responder"● Variables
Node.js-side data envelope factory and validators; the schema hash comes from the Node.js crypto module.
ƒ Functions
Parameters
| Name | Type | Description |
|---|---|---|
§value | string | The string value to cast as JSONString |
Returns
JSONString< T>Example
Casting a validated string
const jsonStr = asJSONString<User>('{"name":"Alice"}')Parameters
| Name | Type | Description |
|---|---|---|
§serialized | SerializedData< | The serialized data to deserialize |
Returns
Data< T>Example
Deserializing data with JSON message
const data = deserializeData({ ...metadata, message: '{"action":"update"}' })
// => { ...metadata, message: { action: 'update' } }Parameters
| Name | Type | Description |
|---|---|---|
§value | unknown | The value to check |
Returns
unknownExample
Checking JSON string types
isJSONString('{"key":"value"}')
// => true
isJSONString({ key: 'value' })
// => falseParameters
| Name | Type | Description |
|---|---|---|
§id | unknown | The value to validate as a data message ID |
Returns
booleanExample
Validating data message IDs
isValidId('550e8400-e29b-41d4-a716-446655440000')
// => true
isValidId('invalid-id')
// => falseParameters
| Name | Type | Description |
|---|---|---|
§message | T | The message to validate |
Returns
booleanExample
Validating message serializability
isValidMessage({ name: 'Alice', items: [1, 2, 3] })
// => true
isValidMessage({ callback: () => {} })
// => false (functions are not serializable)Parameters
| Name | Type | Description |
|---|---|---|
§pid | unknown | The value to validate as a protocol ID |
Returns
booleanExample
Validating protocol IDs
isValidPid('550e8400-e29b-41d4-a716-446655440000')
// => true
isValidPid('not-a-uuid')
// => falseParameters
| Name | Type | Description |
|---|---|---|
§schema | unknown | The value to validate as a JSON Schema |
Returns
booleanExample
Validating JSON schemas
isValidSchema({ type: 'object', properties: { name: { type: 'string' } } })
// => true
isValidSchema({ type: 'invalid-type' })
// => falseParameters
| Name | Type | Description |
|---|---|---|
§schemaHash | unknown | The value to validate as a schema hash |
Returns
booleanExample
Validating schema hashes
isValidSchemaHash('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855')
// => true
isValidSchemaHash('invalid-hash')
// => falseParameters
| Name | Type | Description |
|---|---|---|
§sequence | unknown | The value to validate as a sequence number |
Returns
booleanExample
Validating sequence numbers
isValidSequence(1)
// => true
isValidSequence(0)
// => falseParameters
| Name | Type | Description |
|---|---|---|
§data | unknown | The value to validate as unencrypted data |
Returns
booleanExample
Validating complete unencrypted data objects
isValidUnencryptedData({
pid: '550e8400-e29b-41d4-a716-446655440000',
id: '6ba7b810-9dad-11d1-80b4-00c04fd430c8',
sequence: 1,
message: { action: 'update' },
schema: { type: 'object' },
schemaHash: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
})
// => trueParameters
| Name | Type | Description |
|---|---|---|
§jsonString | JSONString< | The JSON string to parse |
Returns
TExample
Parsing a JSON string to typed object
const user = parseJSONString<User>('{"name":"Alice"}')
// => { name: 'Alice' }Returns
SerializedData< T>Example
Serializing data for transmission
const serialized = serializeData({ ...metadata, message: { action: 'update' } })
// => { ...metadata, message: '{"action":"update"}' }◈ Interfaces
Properties
Properties
Properties
◆ Types
type DataCreater = ( pid: string, sequence: number, message: T) => Promise< SerializedData< T>>type JSONString = string & JSONStringBrand< T>type SchemaCreater = ( data: unknown) => Schema● Variables
Node.js-side packet types, builders, and validators.
ƒ Functions
Parameters
Returns
PacketBaseExample
Creating a packet base
const base = createPacketBase(
'550e8400-e29b-41d4-a716-446655440000',
'6ba7b810-9dad-11d1-80b4-00c04fd430c8'
)
// => { origin: '550e8400-...', target: '6ba7b810-...' }createUnencryptedPacket< T>( origin: string, target: string, data: Data< T>): UnencryptedPacket< T>
Parameters
Returns
UnencryptedPacket< T>Example
Creating an unencrypted packet
const packet = createUnencryptedPacket(originId, targetId, await createData(pid, 1, { action: 'ping' }))
// => { origin, target, data: { pid, id, sequence, message, schema, schemaHash } }Parameters
| Name | Type | Description |
|---|---|---|
§origin | unknown | The value to validate as a packet origin |
Returns
booleanExample
Validating packet origins
isValidOrigin('550e8400-e29b-41d4-a716-446655440000')
// => true
isValidOrigin('not-a-uuid')
// => falseParameters
| Name | Type | Description |
|---|---|---|
§target | unknown | The value to validate as a packet target |
Returns
booleanExample
Validating packet targets
isValidTarget('6ba7b810-9dad-11d1-80b4-00c04fd430c8')
// => true
isValidTarget('invalid')
// => falseParameters
| Name | Type | Description |
|---|---|---|
§packet | unknown | The value to check |
Returns
unknownUnencryptedPacketExample
Validating a packet before sealing
isValidUnencryptedPacket({ origin, target, data: { pid, id, sequence: 1, message: { action: 'ping' }, schema, schemaHash } })
// => trueParameters
| Name | Type | Description |
|---|---|---|
§packet | unknown | The value to check |
Returns
unknownUint8Array with at least one byteExample
Validating wire packets
isValidWirePacket(new Uint8Array([1, 2, 3]))
// => true
isValidWirePacket({ data: 'not binary' })
// => false◈ Interfaces
Properties
Properties
readonly direction:"inbound" | "outbound"Whether the packet was leaving (outbound) or arriving (inbound)◆ Types
type Packet = UnencryptedPacket< T> | WirePackettype PacketDropHandler = ( drop: PacketDrop) => voidtype PacketDropStage = "seal" | "open"type PacketOpener = ( packet: WirePacket) => Promise< UnencryptedPacket< T>>type PacketSealer = ( packet: UnencryptedPacket< T>) => Promise< WirePacket>type WirePacket = Uint8ArrayNode.js-side inbound pipeline: frames in, opened packets out.
◈ Interfaces
Properties
Properties
◆ Types
onDrop receives each frame the opener rejectstype CreateReceiver = ( label: string, receiver: ReceivePacketFn< T>, logger: Logger, open: PacketOpener< T>, onDrop?: PacketDropHandler) => Receivertype ReceiveFn = ( packet: Uint8Array) => voidtype ReceivePacketFn = ( packet: UnencryptedPacket< T>) => voidtype ReceiverFactory = CreateReceiver● Variables
A frame the opener rejects (forged, replayed, foreign, or malformed) is reported through
onDrop and never delivered.Node.js-side outbound pipeline: packets in, sealed frames out.
◈ Interfaces
Properties
Properties
◆ Types
onDrop receives each packet the sealer rejectstype CreateSender = ( label: string, sender: SendPacketFn, logger: Logger, seal: PacketSealer< T>, onDrop?: PacketDropHandler) => Sender< T>type SendFn = ( origin: string, target: string, data: Data< T>) => voidtype SendPacketFn = ( packet: Uint8Array) => void● Variables
send validates the origin, target, and data envelope synchronously and throws in the caller's frame on a malformed packet; everything after that is asynchronous, and a packet the sealer rejects is reported through onDrop rather than thrown.V3 protocol for Node.js: a session envelope keyed by an ephemeral P-256 agreement in the handshake and no shared secret, so a listener cannot read the traffic and the peer is unauthenticated.
ƒ Functions
Returns
ProtocolProviderStoreExample
Creating and using a protocol provider store
const store = createProtocolProviderStore()
store.add('websocket', myProtocolProvider)
const provider = store.getByName('websocket')v3 keys each session from an ephemeral key agreement alone: a party that only listens to the hello exchange and the traffic cannot read it, while a party that can post its own hello to a window before the peer's arrives can stand in for the peer. It authenticates frames, rejects replays, and binds traffic to one session, but does not authenticate the peer.
Parameters
| Name | Type | Description |
|---|---|---|
§crypto | SessionCrypto | The platform primitives |
Returns
( logger: Logger) => ProtocolProvidercreateProtocol(logger), which returns the provider a channel binds to a sessionExample
Composing v3 in a browser entry
export const createProtocol = createV3ProtocolFactory(crypto)
const protocolProvider = createProtocol(logger)Parameters
| Name | Type | Description |
|---|---|---|
§name | string | The name to validate |
Returns
booleanExample
Validating protocol names
isValidName('websocket')
// => true
isValidName('')
// => falseParameters
| Name | Type | Description |
|---|---|---|
§protocol | unknown | The protocol object to validate |
Returns
ValidProtocolResultExample
Validating a protocol object
const result = isValidProtocol(myProtocol)
// => { seal: true, open: true, hello: true, isHello: true, acceptHello: true, send: true, receive: true, getLogger: true }
const invalid = isValidProtocol({})
// => { seal: false, open: undefined, ... }Parameters
| Name | Type | Description |
|---|---|---|
§protocolProvider | unknown | The value to validate as a protocol provider |
Returns
booleanExample
Validating a protocol provider function
isValidProtocolProvider(() => protocol)
// => true
isValidProtocolProvider('not-a-function')
// => falseParameters
| Name | Type | Description |
|---|---|---|
§receive | unknown | The value to validate as a receive function |
Returns
booleanExample
Validating a receive function
isValidReceiveFn((packet) => console.log(packet))
// => true
isValidReceiveFn(null)
// => falseParameters
| Name | Type | Description |
|---|---|---|
§send | unknown | The value to validate as a send function |
Returns
booleanExample
Validating a send function
isValidSendFn((packet) => websocket.send(packet))
// => true
isValidSendFn('not-a-function')
// => false◈ Interfaces
Properties
Properties
readonly add:( name: string, protocolProvider: ProtocolProvider< T>) => voidRegisters a new provider with the given nameProperties
readonly expandKey:( ikm: Uint8Array, salt: Uint8Array, info: Uint8Array, usages: unknown) => Promise< CryptoKey>HKDF expansion into a usage-restricted AES-GCM keyreadonly open:( key: CryptoKey, nonce: Uint8Array, additionalData: Uint8Array, sealed: Uint8Array) => Promise< Uint8Array< ArrayBufferLike>>AES-GCM open under a key, nonce, and additional datareadonly seal:( key: CryptoKey, nonce: Uint8Array, additionalData: Uint8Array, plaintext: Uint8Array) => Promise< Uint8Array< ArrayBufferLike>>AES-GCM seal under a key, nonce, and additional datareadonly stretchPassword:( password: string, salt: Uint8Array, options?: StretchOptions) => Promise< Uint8Array< ArrayBufferLike>>PBKDF2 password stretching into raw bits◆ Types
type ValidProtocolResult = mapped● Variables
V4 protocol for Node.js: the session envelope keyed by an ephemeral P-256 agreement plus a stretched shared secret, so an outsider cannot join and a key that leaks later cannot open past sessions.
ƒ Functions
Returns
ProtocolProviderStoreExample
Creating and using a protocol provider store
const store = createProtocolProviderStore()
store.add('websocket', myProtocolProvider)
const provider = store.getByName('websocket')createV4ProtocolFactory( crypto: SessionCrypto): ( logger: Logger, sharedKey: string) => ProtocolProvider
v4 keys each session from the same ephemeral key agreement as v3 with the shared key stretched once and mixed in, so a party without the key cannot complete the agreement: product traffic is confidential and authentic against anyone who lacks the key, and a key that leaks later does not expose earlier sessions. A party that can run a hello exchange against this side can test key guesses offline afterwards, which is why the key must be generated, not chosen.
Parameters
| Name | Type | Description |
|---|---|---|
§crypto | SessionCrypto | The platform primitives |
Returns
( logger: Logger, sharedKey: string) => ProtocolProvidercreateProtocol(logger, sharedKey), which returns the provider a channel binds to a sessionExample
Composing v4 in a browser entry
export const createProtocol = createV4ProtocolFactory(crypto)
const protocolProvider = createProtocol(logger, sharedKey)Parameters
| Name | Type | Description |
|---|---|---|
§name | string | The name to validate |
Returns
booleanExample
Validating protocol names
isValidName('websocket')
// => true
isValidName('')
// => falseParameters
| Name | Type | Description |
|---|---|---|
§protocol | unknown | The protocol object to validate |
Returns
ValidProtocolResultExample
Validating a protocol object
const result = isValidProtocol(myProtocol)
// => { seal: true, open: true, hello: true, isHello: true, acceptHello: true, send: true, receive: true, getLogger: true }
const invalid = isValidProtocol({})
// => { seal: false, open: undefined, ... }Parameters
| Name | Type | Description |
|---|---|---|
§protocolProvider | unknown | The value to validate as a protocol provider |
Returns
booleanExample
Validating a protocol provider function
isValidProtocolProvider(() => protocol)
// => true
isValidProtocolProvider('not-a-function')
// => falseParameters
| Name | Type | Description |
|---|---|---|
§receive | unknown | The value to validate as a receive function |
Returns
booleanExample
Validating a receive function
isValidReceiveFn((packet) => console.log(packet))
// => true
isValidReceiveFn(null)
// => falseParameters
| Name | Type | Description |
|---|---|---|
§send | unknown | The value to validate as a send function |
Returns
booleanExample
Validating a send function
isValidSendFn((packet) => websocket.send(packet))
// => true
isValidSendFn('not-a-function')
// => false◈ Interfaces
Properties
Properties
readonly add:( name: string, protocolProvider: ProtocolProvider< T>) => voidRegisters a new provider with the given nameProperties
readonly expandKey:( ikm: Uint8Array, salt: Uint8Array, info: Uint8Array, usages: unknown) => Promise< CryptoKey>HKDF expansion into a usage-restricted AES-GCM keyreadonly open:( key: CryptoKey, nonce: Uint8Array, additionalData: Uint8Array, sealed: Uint8Array) => Promise< Uint8Array< ArrayBufferLike>>AES-GCM open under a key, nonce, and additional datareadonly seal:( key: CryptoKey, nonce: Uint8Array, additionalData: Uint8Array, plaintext: Uint8Array) => Promise< Uint8Array< ArrayBufferLike>>AES-GCM seal under a key, nonce, and additional datareadonly stretchPassword:( password: string, salt: Uint8Array, options?: StretchOptions) => Promise< Uint8Array< ArrayBufferLike>>PBKDF2 password stretching into raw bits◆ Types
type ValidProtocolResult = mapped● Variables
FIFO message processing queues for the seal and open pipeline stages.
ƒ Functions
Messages are processed strictly one at a time in arrival order; the next one starts only after the handler's promise settles. That ordering is what lets a protocol assign a monotonically increasing counter to each frame it seals or opens.
A handler that rejects loses only its own message: the rejection is contained, the next message starts, and the queue stays resumable. Reporting the failure is the handler's job.
Parameters
| Name | Type | Description |
|---|---|---|
§processMessage | MessageHandler< | The handler function to process each message |
§autoStart | boolean | Whether to automatically start processing messages (default: true)(default: true) |
Returns
Queue< T>Example
Creating a message processing queue
const queue = createQueue(async (message) => {
await processMessage(message)
})
queue.addMessage({ type: 'ping', data: {} })◈ Interfaces
Properties
Properties
Properties
◆ Types
type MessageHandler = ( message: T) => Promise< void> | voidtype OpenQueueCreater = ( label: string, open: PacketOpener, logger: Logger, onSuccess: ( packet: UnencryptedPacket) => void, onFail: QueueFailureHandler) => Queue< WirePacket>type QueueFailureHandler = ( raw: unknown, reason: string, cause?: unknown) => voidtype QueueOperation = PacketSealer | PacketOpenertype SealQueueCreater = ( label: string, seal: PacketSealer, logger: Logger, onSuccess: ( packet: WirePacket) => void, onFail: QueueFailureHandler) => Queue< UnencryptedPacket>● Variables
Bytes that are not a wire packet, an open that throws (a forged, replayed, or foreign frame), or an open that yields an invalid packet are reported through
onFail and the queue moves on. Processing one frame at a time is what keeps a session's replay counter exact.A packet that is not a valid plaintext packet, a seal that throws, or a seal that yields something other than wire bytes is reported through
onFail and the queue moves on.Packet routing utilities with subscription management and routed packet creators.
ƒ Functions
createRoutedUnencryptedPacket< T>( topicId: string, origin: string, target: string, data: Data< T>): RoutedUnencryptedPacket
Parameters
Returns
RoutedUnencryptedPacketExample
Creating a routed unencrypted packet
const routedPacket = createRoutedUnencryptedPacket(
'550e8400-e29b-41d4-a716-446655440000',
'origin-uuid',
'target-uuid',
{ message: 'hello' }
)
// => { topicId: '550e8400-...', packet: { origin, target, data } }Parameters
| Name | Type | Description |
|---|---|---|
§topicId | string | The topic identifier for routing the packet |
§packet | WirePacket | The wire packet to route |
Returns
RoutedWirePacketExample
Creating a routed wire packet
const routedPacket = createRoutedWirePacket(
'550e8400-e29b-41d4-a716-446655440000',
wirePacket
)
// => { topicId: '550e8400-...', packet: wirePacket }◈ Interfaces
Properties
Properties
Properties
isDynamic:booleanControls subscription fetching behavior: true: Fetches subscriptions anew for each message, useful for dynamic subscription setups.false: Fetches subscriptions once and caches them, ideal for stable setups.
subscriptions:SubscriptionsMaps channels to subscribed topics, determining message routing based on topic.◆ Types
This function is invoked to determine routing configurations, including whether subscriptions are fetched dynamically for each message, and how messages are routed based on topics to specific channels.
type Router = ( channels: Channel[ ], topics: Topic[ ]) => RoutingOptionsThe types and errors a security protocol implements: the per-session seal and open operations, the session, the hello exchange outcome, and the rejection codes.
ƒ Functions
Parameters
| Name | Type | Description |
|---|---|---|
§code | ProtocolErrorCode | Why the protocol rejected the input |
§message | string | Human-readable detail |
Returns
ProtocolErrorcode survives the pipeline's drop reportExample
Rejecting a replayed frame
throw createProtocolError('replayed', 'Frame counter 7 is not above the last accepted counter 9')Parameters
| Name | Type | Description |
|---|---|---|
§error | unknown | Any thrown value |
Returns
ProtocolErrorCodeExample
Mapping a drop to its cause
getProtocolErrorCode(drop.cause)
// => 'authentication-failed'◈ Interfaces
Properties
Properties
Properties
◆ Types
type PacketOpener = ( packet: WirePacket) => Promise< UnencryptedPacket< T>>type PacketSealer = ( packet: UnencryptedPacket< T>) => Promise< WirePacket>type ProtocolErrorCode = indexedAccessaccepted: the peer's material is now known and the session can be keyedduplicate: the same material was already accepted; a retry, ignoredrejected: the frame is not a hello this session can use
type HelloOutcome = "accepted" | "duplicate" | "rejected"type SessionRole = "initiator" | "responder"● Variables
unsupported-version: the frame's version byte is not this protocol'sreplayed: the frame's counter is not above the last accepted oneauthentication-failed: the frame's tag does not verify under the session's keysmalformed: the frame authenticated but does not carry a packetcounter-exhausted: the session has sealed every counter value it can representinvalid-session: the session cannot be keyed from the material it holds
Topic-based message pub/sub management with store creation.
ƒ Functions
Returns
TopicStoreExample
Creating and using a topic store
const store = createTopicStore()
store.create('notifications')
const topic = store.getByName('notifications')
// => { name: 'notifications', id: '550e8400-...' }◈ Interfaces
Properties
Properties
Related reading§
- Architecture
Architecture
How @hyperfrontend/network-protocol is put together, and why.
- How-to
How to migrate from v1 and v2 to v3 and v4
My host and feature pin protocol v1 or v2, and the current releases only build and negotiate v3 and v4. I need to move both sides without losing the mode I had, and know what the app will observe once the channel is keyed per session.
- Troubleshooting
Troubleshooting a secured channel
A channel that pins a security envelope is not behaving as I expect: messages go missing, a handshake is refused, the session closes on its own, a call throws about the shared key, or an error carries a code I do not recognise.
- Package
@hyperfrontend/cryptography
Uses @hyperfrontend/cryptography for the key agreement, key derivation, password stretching, and AES-GCM sealing
- Package
@hyperfrontend/nexus
For simpler cross-window messaging with contracts, see @hyperfrontend/nexus
- Getting started
Getting Started
Set HyperFrontend up and embed a first feature.
- Architecture
Architecture Guide
How the packages fit together.

