@i-xor/wormhole is a browser-native micro-frontend protocol and runtime built on top of Web Components, iframes, postMessage, MessageChannel, and RxJS.
Wormhole does not try to hide iframe boundaries. Instead, it turns host/child coordination into an explicit contract for navigation, permissions, events, RPC, lifecycle, and recovery.
Most iframe integrations fail for the same reasons:
- the host and child do not agree on who owns the top-level route
- authentication and permission state are pushed around ad hoc
- child applications reconnect poorly after runtime disruption
- cross-application messaging becomes an implicit global event bus
Wormhole addresses those problems by defining:
- a typed bootstrap handshake
- a dedicated runtime channel
- capability ownership between host and child
- explicit route-intent and route-commit flow
- session-bound message signing
- child-to-child coordination through a host-governed broker
@i-xor/wormhole- top-level exports@i-xor/wormhole/client- child-side runtime client@i-xor/wormhole/host- host-side session and broker@i-xor/wormhole/element- native<wormhole-app>custom element@i-xor/wormhole/react- React wrapper@i-xor/wormhole/vue- Vue 3 wrapper@i-xor/wormhole/protocol- protocol types, constants, validation, envelope factories@i-xor/wormhole/rx- low-level runtime stream helpers
Wormhole v1 includes:
control:hello / control:ack / control:readybootstrap lifecyclepostMessagebootstrap plusMessageChannelruntime transport- route negotiation through
route-intentandroute-commit - structured event publish and RPC request/response
- context, auth, permission, and capability snapshots
- session-bound acknowledgements for critical runtime messages
- heartbeat and reconnect handling
- before-leave negotiation for dirty-state or long-running work
- host-governed child-to-child broker forwarding
- React and Vue wrapper components on top of the native custom element
Current non-goals for v1:
- visual devtools
- deny policies on top of granted capabilities
pnpm add @i-xor/wormhole rxjsReact and Vue wrappers expect their respective peer dependencies to be installed in the consuming application.
import{WormholeHostBroker,WormholeHostSession}from'@i-xor/wormhole/host'constbroker=newWormholeHostBroker()consthost=newWormholeHostSession({app: 'orders-app',targetOrigin: 'http://localhost:3101',grantedCapabilities: ['route','context','auth','permission','rpc','event','broker'],initialContext: {locale: 'en-US',theme: 'light'},initialAuth: {authenticated: true,transport: 'delegated',sessionId: 'session-1',subject: {id: 'user-1',tenantId: 'workspace-1',displayName: 'Alex'}},initialPermissions: {roles: ['workspace-admin'],permissions: ['APP:ORDER:READ']},
broker
})import{WormholeClient}from'@i-xor/wormhole/client'constwormhole=newWormholeClient({app: 'orders-app',instanceId: 'orders-app:1',reconnectDelayMs: 250})wormhole.bootstrap(window.parent,'http://localhost:3000',{routeMode: 'delegated',capabilities: ['route','context','auth','permission','rpc','event','broker']})wormhole.registerBeforeLeaveHandler(()=>({blocked: hasDirtyForm(),reason: 'form-dirty'}))wormhole.publishToApp('inventory-app','inventory.item.highlight',{itemId: 'item-1'})<wormhole-appapp="orders-app"
src="http://localhost:3101/"
route-mode="delegated"
sandbox="allow-same-origin allow-scripts allow-forms"
referrer-policy="strict-origin-when-cross-origin"
></wormhole-app>import{defineWormholeAppElement}from'@i-xor/wormhole/element'defineWormholeAppElement()wormhole-app is designed to fill its container by default:
- the custom element itself uses
width: 100%andheight: 100% - when the child reports
control:resize, the element and internal iframe expand tomax(100%, contentHeight) - the host page should still provide a reliable height chain for the embedding container
import{Wormhole}from'@i-xor/wormhole/react'<Wormholeapp="orders-app"src="http://localhost:3101/"context={{locale: 'en-US'}}auth={authSnapshot}permissions={permissionSnapshot}capabilityOwnership={{route: 'host',auth: 'host',permission: 'host'}}onReady={onReady}onEvent={onEvent}onError={onError}onResize={onResize}/><template>
<Wormhole
app="orders-app"
src="http://localhost:3101/"
:context="{ locale: 'en-US' }"
:auth="authSnapshot"
:permissions="permissionSnapshot"
:capability-ownership="{ route: 'host', auth: 'host', permission: 'host' }"
@ready="onReady"
@event="onEvent"
@error="onError"
@resize="onResize"
/>
</template>Wormhole separates granted capabilities from capability authority. A child may be allowed to participate in a flow without owning the final decision.
Default ownership in embedded mode:
| Capability | Typical authority |
|---|---|
route | host |
context | host |
auth | host |
permission | host |
resize | child |
lifecycle | child |
rpc | shared |
event | shared |
For delegated routing:
- the child emits
route-intent - the host validates and commits the route
- the host sends
route-commit - the child converges without re-emitting the same route back to the host
- host and child emit
control:heartbeatevery 15 seconds by default - either side marks the runtime channel as stale after 45 seconds without activity
- the child recreates the runtime channel from the last successful bootstrap parameters
Use requestBeforeLeave() when the host needs a child application to confirm whether navigation away from the current page is safe.
Common cases:
- unsaved form data
- unfinished bulk actions
- in-flight review or approval flows
Children never connect to each other directly. All cross-child publish and RPC traffic flows through the host-side broker so the host remains the authority for routing, isolation, and auditing.
- bootstrap must validate
originand expected source - runtime payloads must remain structured-clone safe
- host objects must never be passed by reference into the child
- auth snapshots should expose summaries rather than raw bearer tokens whenever possible
- runtime messages can be signed with a session-bound
messageAuthKey - the default iframe sandbox is intentionally explicit and should be narrowed further per application if needed
pnpm install
pnpm build
pnpm test- Contribution guide:
CONTRIBUTING.md - Security policy:
SECURITY.md - Code of conduct:
CODE_OF_CONDUCT.md
See PROTOCOL.md for the message model, lifecycle rules, and integration contract in more detail.