@hyperfrontend/ nexus§
Secure cross-window communication library for micro-frontends with contract-validated messaging, origin-based security policies, and connection lifecycle management.
What is @hyperfrontend/nexus?
Two windows talking over postMessage share a string and nothing else: no agreement on which message types exist, no way to tell whether anyone is listening, no signal when the other side goes away. Nexus puts a broker in front of that. One broker per app manages a channel per counterpart window or frame, and every channel carries a contract, the message types each side sends and accepts, exchanged during a three-way handshake. Types outside the contract are dropped, messages from an origin other than the pinned one are ignored, and the connection state is something you subscribe to instead of infer.
import { createBroker } from '@hyperfrontend/nexus'
const broker = createBroker({
name: 'host-app',
contract: { emitted: [{ type: 'THEME_CHANGED' }], accepted: [{ type: 'CART_UPDATED' }] },
settings: { whitelist: ['https://cart.example.com'] },
})
const cart = broker.addChannel('cart', cartFrame.contentWindow)
cart.on('open', () => cart.send('THEME_CHANGED', { theme: 'dark' }))
cart.onMessage(({ type, data }) => console.log(type, data))
cart.connect()
Key Features
define accepted and emitted message types, with optional JSON Schemas carried for consumers to validate payloads against
a central broker manages multiple independent channels to different windows
whitelist/blacklist filtering plus custom security policy functions
Connection Lifecycle Management
full state machine for connect, disconnect, cancel, deny, and destroy operations
subscribe to lifecycle events (open, close, cancel, deny, invalid, connect-timeout, security events) and user messages
Message Queueing
messages sent before a channel is active are queued, not lost
extend contracts at runtime or merge several into one
Functional API Design
factory functions with closure-based encapsulation, no class hierarchy to subclass
Why Use @hyperfrontend/nexus?
Micro-frontend integrations fail where two teams assumed different message shapes. A contract makes the assumption a value both sides exchange and the runtime enforces:
const contract: IChannelContract = {
emitted: [{ type: 'USER_UPDATED', schema: userJsonSchema }, { type: 'NAVIGATION_REQUEST' }],
accepted: [{ type: 'USER_DATA' }, { type: 'NAVIGATION_COMPLETE' }],
}
Unknown inbound types are dropped and logged, and an accepted entry marked required: true denies the handshake outright when the counterpart cannot emit it. Adding actions stays backward compatible either way.
Origin checks come with it. A whitelist/blacklist pair on the broker settings, or broker.setSecurityPolicy((event) => event.origin.endsWith('.example.com')), vets requests before the channel opens, so no message handler has to remember to test event.origin itself.
One broker holds many channels, which is what a host coordinating several micro-apps needs: call addChannel per frame, then loop the handles to connect or broadcast. Each channel runs its own handshake and lifecycle, so a frame that never answers cannot stall the others, and messages sent before a channel goes active are queued rather than dropped.
Handlers stay small. Subscribe to a single lifecycle event with channel.on('open', handler), or replace a switch over message types with one filtered subscription per type:
import { byType, compose, createMessageFilter } from '@hyperfrontend/nexus'
channel.onMessage(byType('USER_LOGIN')(handleLogin))
channel.onMessage(byType('USER_LOGOUT')(handleLogout))
channel.onMessage(byType('DATA_SYNC')(handleSync))
// compose narrows a single subscription: a message must pass every filter
channel.onMessage(
compose(
byType('DATA_SYNC'),
createMessageFilter((message) => message.data?.priority === 'high')
)(handleUrgentSync)
)
Handshake states, denial reasons, queue behaviour, and the encrypted-envelope negotiation are worked through in the architecture documentation.
Protocol Overview
Nexus implements a three-way handshake protocol (REQUEST → ACCEPT → OPEN) for establishing connections, with graceful disconnection, cancellation, denial, and timeout handling. The summaries below map the territory; the Architecture Documentation covers every flow in depth.
Connection Handshake
Initiation is symmetric: either side may call connect() first, and simultaneous requests resolve deterministically via a broker-id tie-break. Pending handshake messages are re-sent every requestRetryMs (default 500 ms) until answered, and a handshake unanswered past connectTimeoutMs (default 10 000 ms) fires connect-timeout, leaving the channel inactive and reconnectable with its queued messages retained. Each side pins the counterpart's origin during the handshake, and inbound messages from any other origin are dropped. See Protocol Design.
Contract Compatibility
Contracts are exchanged during the handshake, but vocabulary differences never gate the connection: only accepted entries flagged required: true do (each must appear in the counterpart's emitted list), so additive contract evolution stays non-breaking in both directions. A contract may also carry an optional version string that nexus attaches no semantics to; a contractCompat rule in the channel settings can compare the two contracts and deny the pair before it opens. When the responder's rule rejects an incoming request, the deny event fires with the rule's reason and reason: 'incompatible-contract' on both the denying responder and the denied initiator. See Contract Compatibility.
Security Negotiation
Channels can negotiate a sealed envelope during the handshake: register a security provider on the broker (via broker.registerProtocol(version, provider) or the settings.security.protocols bag) and opt the channel in with security: { protocol: 'v3' } or security: { protocol: 'v4' }. The initiator's REQUEST advertises that protocol with plaintext as the fallback, the responder's ACCEPT answers with the outcome, and the initiator's OPEN confirms it; a channel that selected a protocol accepts that protocol or plaintext and nothing else.
Each side attaches its transport as it sends its own handshake answer (the responder with ACCEPT, the initiator with OPEN) and posts the session hello right behind that frame, retrying it every requestRetryMs until the counterpart confirms. Product traffic, including sends queued before the handshake, leaves as Uint8Array frames sealed under the session keys while the handshake actions themselves stay plaintext; once a transport is attached, any other plaintext action is dropped.
The first inbound frame that authenticates confirms the counterpart and fires security-ready; a session nothing confirms within connectTimeoutMs fires security-error with code security-unconfirmed and closes with reason: 'security-unconfirmed'. Negotiation fails open by default, falling back to plaintext with a warning when the counterpart cannot provide the protocol; mode: 'fail-closed' denies the connection instead with reason: 'security-unavailable'.
The transport seam is public: createSecurityTransport plus the SecurityTransport and SecurityProvider types define the boundary a security package implements, and @hyperfrontend/network-protocol (v3: ephemeral session keys agreed over the wire, defeating scripts that can only listen; v4: the same keys bound to a pre-shared key, defeating any script without the key) satisfies it directly. See Security Model.
Disconnection & Cancellation
An active channel closes gracefully through a CLOSE/CLOSE_ACKNOWLEDGED exchange, firing close on both sides; a pending connection can be abandoned by either party through CANCEL/CANCEL_ACKNOWLEDGED, firing cancel. Denials (DENY_CONNECTION) and protocol violations (INVALID_REQUEST) round out the failure verbs, and every connection attempt ends in exactly one of open, close, cancel, deny, or connect-timeout. See Protocol Design.
Security Policies
What these gates are worth, and which controls sit outside the protocol entirely (frame-ancestors, backend authorisation, the pre-shared key), is stated in the Security Model.
Connection-time access control runs before a channel opens: origin whitelist/blacklist settings filter every inbound message (a non-empty whitelist takes precedence), and a custom policy function, broker.setSecurityPolicy((event: MessageEvent) => boolean), vets requests during handshake handling, with rejected requests answered by DENY_CONNECTION. See Security Model.
Logging
All internal output routes through a Logger from @hyperfrontend/logging. Set verbosity with the logLevel setting ('error' | 'warn' | 'log' | 'info' | 'debug' | 'none') or inject a custom logger (Winston, Pino, etc.) via settings.logger; channels inherit the broker's logger, exposed as broker.logger. See Logging System.
Installation
npm install @hyperfrontend/nexus
Quick Start
import { createBroker } from '@hyperfrontend/nexus'
// Define communication contract
const contract = {
emitted: [{ type: 'PING' }],
accepted: [{ type: 'PONG' }],
}
// Create broker
const broker = createBroker({
name: 'main-app',
contract,
settings: { logLevel: 'debug' },
})
// Add channel to iframe
const iframe = document.querySelector('iframe')
const channel = broker.addChannel('child-app', iframe.contentWindow)
// Subscribe to messages
channel.onMessage((message) => {
console.log('Received:', message.type, message.data)
})
// Connect and send
channel.connect()
channel.send('PING', { timestamp: Date.now() })
Using the Default Broker
For quick prototyping, use the pre-configured singleton broker:
import { defaultBroker } from '@hyperfrontend/nexus'
const channel = defaultBroker.addChannel('my-channel', targetWindow)
channel.connect()
channel.send('MESSAGE', { hello: 'world' })
API Overview
Everything starts at createBroker. One broker per app: it owns the single message listener on your window, holds the contract every channel inherits, and carries the origin whitelist and security policy that apply to all of them. Channels are what you actually hold, one per counterpart window, handed out by addChannel(name, targetWindow, settings?) as a ChannelHandle. defaultBroker is a ready-made singleton for a prototype that only ever talks to one frame.
A channel does nothing until connect(), which starts the three-way handshake. That handshake is where the three things worth having get settled at once: the two IChannelContract values (an emitted list and an accepted list of action types) are exchanged, each side pins the counterpart's origin, and the broker's gates vet the request before anything opens. After that the day-to-day surface is two calls. send(type, data) throws at the call site for any type outside your own emitted list, so a typo never travels. onMessage(handler) delivers only the types in your own accepted list; anything else arrives, is logged, and stops there, which is not an event you can subscribe to.
Connection state is an event stream rather than a flag you poll. channel.on(event, handler) subscribes to one ChannelEvent and channel.on(handler) to all of them; both return an unsubscribe function. Every connection attempt ends in exactly one of open, close, cancel, deny or connect-timeout, and a denial (or a close neither side asked for) carries a machine-readable reason beside its message, so a failure is something you branch on rather than parse.
The rest is opt-in. byType and compose replace a switch over message types with one narrow subscription per type; mergeContracts folds several contracts into one; and createSecurityTransport with the SecurityProvider type is the seam a security package implements, which is how @hyperfrontend/network-protocol supplies the sealed v3 and v4 session envelope.
Every setting, event payload, deny reason and security type is in the API reference.
Compatibility
Runs on
- Node.js>=18.0.0supported
- Browserssupported
- Web Workerssupported
Ships as
- ESMTree-shakeable
- CJSNode and older bundlers
- IIFEHyperfrontendNexus
- UMDHyperfrontendNexus
CDN Usage
<!-- unpkg -->
<script src="https://unpkg.com/@hyperfrontend/nexus"></script>
<!-- jsDelivr -->
<script src="https://cdn.jsdelivr.net/npm/@hyperfrontend/nexus"></script>
<script>
const { createBroker, createChannel } = HyperfrontendNexus
</script>
Global variable: HyperfrontendNexus
Peer Dependencies
| Package | Version | Type |
|---|---|---|
| @hyperfrontend/network-protocol | 2.0.0 | Optional |
API Reference§
ƒFunctions
Parameters
| Name | Type | Description |
|---|---|---|
§messageType | string | The message type to filter for |
Returns
( handler: MessageHandler< T>) => MessageHandler< T>Example
Filtering by message type
const pingFilter = byType('ping')
const handler = pingFilter((msg, channel) => {
console.log('Received ping from', channel.name)
})Parameters
| Name | Type | Description |
|---|---|---|
§handler | CancelEventHandler | Handler that only receives CANCEL events |
Returns
EventHandlerParameters
| Name | Type | Description |
|---|---|---|
§handler | CloseEventHandler | Handler that only receives CLOSE events |
Returns
EventHandlerParameters
| Name | Type | Description |
|---|---|---|
§...filters | MessageFilter< | Variable number of filter functions to compose |
Returns
MessageFilter< T>Example
Composing message filters
const combinedFilter = compose(
byType('notification'),
create((msg) => msg.priority === 'high')
)
const handler = combinedFilter((msg) => console.log(msg))Parameters
| Name | Type | Description |
|---|---|---|
§config | CreateBrokerConfig | Broker configuration |
Returns
BrokerHandleExample
Creating a message broker
const broker = createBroker({
name: 'app-broker',
contract: {
emitted: [{ type: 'PING' }],
accepted: [{ type: 'PONG' }],
},
settings: { logLevel: 'warn' },
})Uses functional programming with closures for encapsulation. Returns a public handle with methods while keeping state private.
Parameters
| Name | Type | Description |
|---|---|---|
§config | IChannelConfig | Channel configuration (name, target, settings) |
§deps | ChannelDependencies | Dependencies (action creators, process manager, cleanup) |
Returns
ChannelHandleExample
Creating and using a channel
const channel = createChannel(
{ name: 'my-channel', target: childWindow },
{ actions, processManager, cleanup }
)
channel.connect()
channel.send('greet', { message: 'Hello!' })Parameters
| Name | Type | Description |
|---|---|---|
§eventType | ChannelEvent | The event type to filter for |
Returns
( handler: EventHandler) => EventHandlerExample
Filtering channel events
const openFilter = create('open')
const filteredHandler = openFilter((event, data, channel) => {
console.log('Channel opened:', channel.name)
})If a custom logger is provided, it will be used directly. Otherwise, a new logger will be created using the logging library.
Parameters
| Name | Type | Description |
|---|---|---|
§options | NexusLoggerOptions | Logger configuration options(default: {}) |
Returns
LoggerExample
Configuring logger options
const logger = createLogger({ level: 'debug', prefix: '[my-channel]' })
logger.debug('Channel initialized')createMessageFilter< T>( predicate: MessagePredicate< T>): ( handler: MessageHandler< T>) => MessageHandler< T>
Parameters
| Name | Type | Description |
|---|---|---|
§predicate | MessagePredicate< | Function that tests if message should be handled |
Returns
( handler: MessageHandler< T>) => MessageHandler< T>Example
Creating custom message filters
const highPriorityFilter = create((msg) => msg.priority === 'high')
const filteredHandler = highPriorityFilter((msg, channel) => {
console.log('High priority:', msg)
})Routes to the appropriate transport implementation:
'none': Creates a passthrough transport (no encryption)- Any other protocol: Creates a secure transport driving the provider's
Parameters
| Name | Type | Description |
|---|---|---|
§config | SecurityTransportConfig | Transport configuration |
Returns
SecurityTransportExample
Creating security transports
import { logger } from '@hyperfrontend/logging'
import { createChannel } from '@hyperfrontend/network-protocol/browser/channel'
import { createProtocol } from '@hyperfrontend/network-protocol/browser/v4'
const transport = createSecurityTransport({
protocol: 'v4',
provider: { createChannel, protocolProvider: createProtocol(logger, 'shared-key') },
label: 'checkout-feature',
target: iframe.contentWindow,
getOrigin: () => 'https://feature.example.com',
originId: hostId,
targetId: featureId,
role: 'initiator',
onAction: (action) => handleAction(action),
})Parameters
| Name | Type | Description |
|---|---|---|
§handler | DenyEventHandler | Handler that only receives DENY events |
Returns
EventHandlerParameters
| Name | Type | Description |
|---|---|---|
§handler | InvalidEventHandler | Handler that only receives INVALID events |
Returns
EventHandlerParameters
Parameters
| Name | Type | Description |
|---|---|---|
§logger | Logger | Logger instance to use |
§event | ChannelEvent | Type of channel event that occurred |
§data | unknown | Additional data associated with the event |
Parameters
| Name | Type | Description |
|---|---|---|
§...contracts | IChannelContract[ | The contracts to merge |
Returns
IChannelContractExample
Merging channel contracts
const contract1 = { accepted: [{ type: 'a' }], provided: [{ type: 'b' }] }
const contract2 = { accepted: [{ type: 'c' }], provided: [{ type: 'd' }] }
const merged = mergeContracts(contract1, contract2)
// merged = { accepted: [{ type: 'a' }, { type: 'c' }], emitted: [{ type: 'b' }, { type: 'd' }] }Parameters
| Name | Type | Description |
|---|---|---|
§handler | OpenEventHandler | Handler that only receives OPEN events |
Returns
EventHandler◈Interfaces
Properties
Properties
Registers protocol providers for all channels managed by the broker.
Properties
Properties
readonly security?:BrokerSecurityConfigSecurity configuration for protocol negotiation and encryptionProperties
One cancelled attempt fires exactly one 'cancel' event on each side.
Properties
notify:booleanWhether the counterpart is the side that cancelled: true only on the side that received a CANCEL frame. A cancellation this side initiated reports false, whether or not it sent a frame, so a subscriber can tell the two apart.Properties
Properties
Properties
readonly mode?:"fail-open" | "fail-closed"How the channel behaves when it selects a protocol but the handshake cannot deliver it (the counterpart predates security, offers no common protocol, selects another one, or the negotiated provider is missing). 'fail-open'(default): fall back to plaintext with a warning'fail-closed': refuse the connection with reason'security-unavailable'
readonly protocol?:SecurityProtocolVersionProtocol the channel negotiates; the handshake accepts no other encrypted outcomeProperties
Properties
Properties
Properties
Properties
required?:booleanMarks an accepted action as essential for correct operation: the connection is denied unless the counterpart emits this type. Only meaningful on accepted entries; ignored on emitted.Properties
A contract is self-oriented: it always describes the side that owns it.
emitted lists the message types this side sends, and accepted lists the message types this side is willing to receive. Outgoing messages are validated against emitted; incoming messages are validated against accepted and silently dropped (with a log entry) when not listed. Accepted entries flagged required additionally gate the connection: a counterpart that does not emit them is denied at handshake time.Properties
version?:stringOptional version string announcing the contract cut this side holds. Crossed to the counterpart during the handshake and stored on peerContract; nexus itself attaches no semantics to it, though a channel-supplied compatibility rule may compare the two announcements.Properties
closeTimeoutMs?:numberMilliseconds a polite close waits for the counterpart's acknowledgement before closing anyway (default: 2000)connectTimeoutMs?:numberMilliseconds a connection attempt may remain unanswered before firing 'connect-timeout' (default: 10000)contractCompat?:ContractCompatRule deciding whether the local and counterpart contracts may interoperate; an incompatible pair is denied during the handshakerequestRetryMs?:numberMilliseconds between handshake re-sends while a connection attempt is pending (default: 500)Properties
Properties
Properties
timedAsync:TimedAsyncFnWraps a promise-returning call with timing. Dumps stack trace on error to debug log.Properties
Properties
Properties
Structural mirror of network-protocol's
ChannelOptions shape.Properties
readonly onDrop?:( drop: SecurityPacketDrop) => voidReceives every packet either direction of the pipeline discardsOPEN_CONNECTION action. Confirms which protocol the initiator attached, or that it attached none.
Properties
Properties
The initiator includes this in the
REQUEST_CONNECTION action to advertise its security capabilities and preferences.Properties
The responder includes this in the
ACCEPT_CONNECTION action to communicate the negotiated protocol.Properties
readonly negotiated:SecurityProtocolVersionNegotiated protocol (best match between initiator and responder)Properties
The transported nexus action lives at SecurityPacketData.message; the remaining fields are wire-protocol bookkeeping.
Properties
Structural mirror of network-protocol's
PacketDrop shape.Properties
readonly direction:"inbound" | "outbound"Whether the packet was leaving (outbound) or arriving (inbound)Properties
createChannel and a protocol provider satisfy it directly.Properties
readonly protocolProvider:SecurityProtocolProviderCreates the protocol instance driving the pipelineProperties
Structural mirror of network-protocol's
ProtocolSession shape.Properties
Wraps a security wire pipeline and provides a simple send/receive interface for nexus channels.
Properties
Properties
readonly confirmTimeoutMs?:numberHow long the counterpart has to confirm the session after the transport startsreadonly getOrigin:( ) => stringReturns the origin currently pinned to the channel, or null before pinningreadonly helloRetryMs?:numberInterval between hello retries until the counterpart confirms the sessionreadonly onConfirmed?:( ) => voidOptional handler invoked once, when the counterpart's first frame authenticatesreadonly onFailed?:( error: SecurityTransportError) => voidOptional handler invoked when the session can no longer be confirmedreadonly provider?:SecurityProviderSecurity implementation building the wire pipeline (required for protocols other than 'none')onError handler.Properties
Structural mirror of the network-protocol channel surface nexus drives.
Properties
readonly acceptHello:( frame: Uint8Array) => SecurityHelloOutcomeFeeds the counterpart's hello to the sessionreadonly isHello:( frame: Uint8Array) => booleanWhether a frame is a hello of the channel's protocolreadonly send:( origin: string, target: string, data: SecurityPacketData) => voidSeals and transmits a data envelope from origin to targetStructural mirror of network-protocol's
Protocol shape.Properties
readonly acceptHello:( frame: Uint8Array) => SecurityHelloOutcomeFeeds the counterpart's hello to the sessionreadonly open:( frame: Uint8Array) => Promise< SecurityPacket>Opens a frame into a packet under the session's receiving keyreadonly seal:( packet: SecurityPacket) => Promise< Uint8Array< ArrayBufferLike>>Seals a packet into a frame under the session's sending key◆Types
type ActionType = indexedAccesstype CancelEventHandler = ( event: "cancel", data: CancelEventData, channel: ChannelJSON) => voidtype ChannelEvent = "open" | "closing" | "close" | "cancel" | "deny" | "invalid" | "connect-timeout" | "security-ready" | "security-error"type CloseEventHandler = ( event: "close", data: CloseEventData, channel: ChannelJSON) => voidpeer-reload: the counterpart window now hosts a different instance
security-unconfirmed: the counterpart never confirmed the session keys
type CloseReason = "peer-reload" | "security-unconfirmed"Invoked during the connection handshake alongside the required-actions check, on whichever side holds the rule; an incompatible result denies the connection before it opens, surfacing the reason on the
deny event.type ContractCompat = ( own: IChannelContract, peer: IChannelContract) => ContractCompatibilitytype ContractCompatibility = ContractCompatible | ContractIncompatibletype DenyEventHandler = ( event: "deny", data: DenyEventData, channel: ChannelJSON) => voidinvalid-contract: the counterpart's contract failed structural
missing-required-actions: the counterpart's contract does not emit an
required: true. policy-rejected: the broker'ssecurityPolicyrefused the request.
incompatible-contract: acontractCompatrule rejected the contract
security-unavailable: a fail-closed channel could not obtain an
Any other string is accepted so a counterpart running a newer protocol can report a reason this build does not know yet.
type DenyReason = "invalid-contract" | "missing-required-actions" | "policy-rejected" | "incompatible-contract" | "security-unavailable" | string & { }type EventData = EventEnvelope< "open", OpenEventData> | EventEnvelope< "closing", ClosingEventData> | EventEnvelope< "close", CloseEventData> | EventEnvelope< "cancel", CancelEventData> | EventEnvelope< "deny", DenyEventData> | EventEnvelope< "invalid", InvalidEventData> | EventEnvelope< "connect-timeout", ConnectTimeoutEventData> | EventEnvelope< "security-ready", SecurityReadyEventData> | EventEnvelope< "security-error", SecurityErrorEventData>type EventHandler = ( event: ChannelEvent, data: OpenEventData | CloseEventData | CancelEventData | DenyEventData | InvalidEventData, channel: ChannelJSON) => voidtype IAction = IActionWithContract | IActionWithError | IActionWithData | IActionWithProcess | IActionBasetype InvalidEventHandler = ( event: "invalid", data: InvalidEventData, channel: ChannelJSON) => voidtype LogLevel = "none" | "error" | "warn" | "log" | "info" | "debug"type MessageFilter = ( handler: MessageHandler< T>) => MessageHandler< T>type MessageHandler = ( message: T, channel: ChannelJSON) => voidtype MessagePredicate = ( message: T) => booleantype OpenEventHandler = ( event: "open", data: OpenEventData, channel: ChannelJSON) => voidStructural mirror of network-protocol's
createChannel signature.type SecurityChannelFactory = ( label: string, options: SecurityChannelOptions) => SecurityWireChannelsecurity-error events and transport error handlers. The first six are the wire protocol's verdicts on a single frame; the remaining codes describe the transport around the protocol.
'unsupported-version': the frame carries another protocol's version byte'replayed': the frame's counter is not newer than the last accepted one'authentication-failed': the frame was not sealed under the session keys'malformed': the frame or the envelope it carried is not well-formed'counter-exhausted': the session sealed its last frame'invalid-session': the session keys cannot be derived'hello-rejected': a hello arrived that differs from the one keying the session'security-unconfirmed': the counterpart did not confirm the session before the deadline'transport-error': a packet could not be sealed or handed to the wire'unknown': an error without a recognised code
type SecurityErrorCode = "unsupported-version" | "replayed" | "authentication-failed" | "malformed" | "counter-exhausted" | "invalid-session" | "hello-rejected" | "security-unconfirmed" | "transport-error" | "unknown"'accepted': the hello keyed the session'duplicate': the hello repeats the one that keyed the session'rejected': the hello differs from the one that keyed the session
type SecurityHelloOutcome = "accepted" | "duplicate" | "rejected"type SecurityPolicy = ( event: MessageEvent) => booleanStructural mirror of network-protocol's
ProtocolProvider shape.type SecurityProtocolProvider = ( send: SecuritySendPacket, receive: SecurityReceivePacket, session: SecuritySession) => SecurityWireProtocol'v3': Ephemeral session keys agreed over the wire; defeats passive listeners'v4': Ephemeral session keys bound to a pre-shared key; defeats any script without the key'none': No security, plaintext passthrough
type SecurityProtocolVersion = "none" | "v3" | "v4" | string & { }type SecurityReceivePacket = ( packet: SecurityPacket) => voidtype SecuritySendPacket = ( frame: Uint8Array) => voidThe initiator sent the connection request; the responder accepted it. The roles order the session's key material, so both endpoints must agree.
type SecuritySessionRole = "initiator" | "responder"●Variables
Related reading§
- Architecture
Architecture
How @hyperfrontend/nexus is put together, and why.
- Tutorial
How to make your first cross-window connection
I have a page and an iframe that need to talk to each other, and raw postMessage keeps turning into stringly-typed guesswork that breaks in production.
- 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/network-protocol
Optionally uses @hyperfrontend/network-protocol for the sealed v3/v4 session envelope
- Getting started
Getting Started
Set HyperFrontend up and embed a first feature.
- Architecture
Architecture Guide
How the packages fit together.

