Repository files navigation

Wormhole

English | 简体中文

@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.

Why Wormhole

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

Package Layout

  • @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

Feature Summary

Wormhole v1 includes:

  • control:hello / control:ack / control:ready bootstrap lifecycle
  • postMessage bootstrap plus MessageChannel runtime transport
  • route negotiation through route-intent and route-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

Installation

pnpm add @i-xor/wormhole rxjs

React and Vue wrappers expect their respective peer dependencies to be installed in the consuming application.

Quick Start

Host

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
})

Child

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'})

Native Custom Element

<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% and height: 100%
  • when the child reports control:resize, the element and internal iframe expand to max(100%, contentHeight)
  • the host page should still provide a reliable height chain for the embedding container

React Wrapper

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}/>

Vue Wrapper

<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>

Runtime Model

Capability Ownership

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:

CapabilityTypical authority
routehost
contexthost
authhost
permissionhost
resizechild
lifecyclechild
rpcshared
eventshared

Navigation

For delegated routing:

  1. the child emits route-intent
  2. the host validates and commits the route
  3. the host sends route-commit
  4. the child converges without re-emitting the same route back to the host

Recovery

  • host and child emit control:heartbeat every 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

Before Leave

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

Child-to-Child Coordination

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.

Security Model

  • bootstrap must validate origin and 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

Development

pnpm install
pnpm build
pnpm test

Project Policies

Protocol Reference

See PROTOCOL.md for the message model, lifecycle rules, and integration contract in more detail.

About

Wormhole,是一个微前端框架,基于 iframe 和一整套主子协议实现。

Resources

Code of conduct

Contributing

Security policy

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Wormhole

English | 简体中文

@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.

Why Wormhole

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

Package Layout

  • @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

Feature Summary

Wormhole v1 includes:

  • control:hello / control:ack / control:ready bootstrap lifecycle
  • postMessage bootstrap plus MessageChannel runtime transport
  • route negotiation through route-intent and route-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

Installation

pnpm add @i-xor/wormhole rxjs

React and Vue wrappers expect their respective peer dependencies to be installed in the consuming application.

Quick Start

Host

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
})

Child

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'})

Native Custom Element

<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% and height: 100%
  • when the child reports control:resize, the element and internal iframe expand to max(100%, contentHeight)
  • the host page should still provide a reliable height chain for the embedding container

React Wrapper

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}/>

Vue Wrapper

<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>

Runtime Model

Capability Ownership

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:

CapabilityTypical authority
routehost
contexthost
authhost
permissionhost
resizechild
lifecyclechild
rpcshared
eventshared

Navigation

For delegated routing:

  1. the child emits route-intent
  2. the host validates and commits the route
  3. the host sends route-commit
  4. the child converges without re-emitting the same route back to the host

Recovery

  • host and child emit control:heartbeat every 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

Before Leave

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

Child-to-Child Coordination

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.

Security Model

  • bootstrap must validate origin and 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

Development

pnpm install
pnpm build
pnpm test

Project Policies

Protocol Reference

See PROTOCOL.md for the message model, lifecycle rules, and integration contract in more detail.

About

Wormhole,是一个微前端框架,基于 iframe 和一整套主子协议实现。

Resources

Code of conduct

Contributing

Security policy

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Wormhole

English | 简体中文

@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.

Why Wormhole

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

Package Layout

  • @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

Feature Summary

Wormhole v1 includes:

  • control:hello / control:ack / control:ready bootstrap lifecycle
  • postMessage bootstrap plus MessageChannel runtime transport
  • route negotiation through route-intent and route-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

Installation

pnpm add @i-xor/wormhole rxjs

React and Vue wrappers expect their respective peer dependencies to be installed in the consuming application.

Quick Start

Host

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
})

Child

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'})

Native Custom Element

<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% and height: 100%
  • when the child reports control:resize, the element and internal iframe expand to max(100%, contentHeight)
  • the host page should still provide a reliable height chain for the embedding container

React Wrapper

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}/>

Vue Wrapper

<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>

Runtime Model

Capability Ownership

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:

CapabilityTypical authority
routehost
contexthost
authhost
permissionhost
resizechild
lifecyclechild
rpcshared
eventshared

Navigation

For delegated routing:

  1. the child emits route-intent
  2. the host validates and commits the route
  3. the host sends route-commit
  4. the child converges without re-emitting the same route back to the host

Recovery

  • host and child emit control:heartbeat every 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

Before Leave

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

Child-to-Child Coordination

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.

Security Model

  • bootstrap must validate origin and 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

Development

pnpm install
pnpm build
pnpm test

Project Policies

Protocol Reference

See PROTOCOL.md for the message model, lifecycle rules, and integration contract in more detail.

About

Wormhole,是一个微前端框架,基于 iframe 和一整套主子协议实现。

Resources

Code of conduct

Contributing

Security policy

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Wormhole

English | 简体中文

@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.

Why Wormhole

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

Package Layout

  • @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

Feature Summary

Wormhole v1 includes:

  • control:hello / control:ack / control:ready bootstrap lifecycle
  • postMessage bootstrap plus MessageChannel runtime transport
  • route negotiation through route-intent and route-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

Installation

pnpm add @i-xor/wormhole rxjs

React and Vue wrappers expect their respective peer dependencies to be installed in the consuming application.

Quick Start

Host

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
})

Child

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'})

Native Custom Element

<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% and height: 100%
  • when the child reports control:resize, the element and internal iframe expand to max(100%, contentHeight)
  • the host page should still provide a reliable height chain for the embedding container

React Wrapper

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}/>

Vue Wrapper

<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>

Runtime Model

Capability Ownership

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:

CapabilityTypical authority
routehost
contexthost
authhost
permissionhost
resizechild
lifecyclechild
rpcshared
eventshared

Navigation

For delegated routing:

  1. the child emits route-intent
  2. the host validates and commits the route
  3. the host sends route-commit
  4. the child converges without re-emitting the same route back to the host

Recovery

  • host and child emit control:heartbeat every 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

Before Leave

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

Child-to-Child Coordination

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.

Security Model

  • bootstrap must validate origin and 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

Development

pnpm install
pnpm build
pnpm test

Project Policies

Protocol Reference

See PROTOCOL.md for the message model, lifecycle rules, and integration contract in more detail.

About

Wormhole,是一个微前端框架,基于 iframe 和一整套主子协议实现。

Resources

Code of conduct

Contributing

Security policy

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Wormhole

English | 简体中文

@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.

Why Wormhole

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

Package Layout

  • @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

Feature Summary

Wormhole v1 includes:

  • control:hello / control:ack / control:ready bootstrap lifecycle
  • postMessage bootstrap plus MessageChannel runtime transport
  • route negotiation through route-intent and route-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

Installation

pnpm add @i-xor/wormhole rxjs

React and Vue wrappers expect their respective peer dependencies to be installed in the consuming application.

Quick Start

Host

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
})

Child

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'})

Native Custom Element

<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% and height: 100%
  • when the child reports control:resize, the element and internal iframe expand to max(100%, contentHeight)
  • the host page should still provide a reliable height chain for the embedding container

React Wrapper

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}/>

Vue Wrapper

<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>

Runtime Model

Capability Ownership

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:

CapabilityTypical authority
routehost
contexthost
authhost
permissionhost
resizechild
lifecyclechild
rpcshared
eventshared

Navigation

For delegated routing:

  1. the child emits route-intent
  2. the host validates and commits the route
  3. the host sends route-commit
  4. the child converges without re-emitting the same route back to the host

Recovery

  • host and child emit control:heartbeat every 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

Before Leave

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

Child-to-Child Coordination

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.

Security Model

  • bootstrap must validate origin and 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

Development

pnpm install
pnpm build
pnpm test

Project Policies

Protocol Reference

See PROTOCOL.md for the message model, lifecycle rules, and integration contract in more detail.

About

Wormhole,是一个微前端框架,基于 iframe 和一整套主子协议实现。

Resources

Code of conduct

Contributing

Security policy

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Wormhole

English | 简体中文

@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.

Why Wormhole

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

Package Layout

  • @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

Feature Summary

Wormhole v1 includes:

  • control:hello / control:ack / control:ready bootstrap lifecycle
  • postMessage bootstrap plus MessageChannel runtime transport
  • route negotiation through route-intent and route-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

Installation

pnpm add @i-xor/wormhole rxjs

React and Vue wrappers expect their respective peer dependencies to be installed in the consuming application.

Quick Start

Host

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
})

Child

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'})

Native Custom Element

<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% and height: 100%
  • when the child reports control:resize, the element and internal iframe expand to max(100%, contentHeight)
  • the host page should still provide a reliable height chain for the embedding container

React Wrapper

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}/>

Vue Wrapper

<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>

Runtime Model

Capability Ownership

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:

CapabilityTypical authority
routehost
contexthost
authhost
permissionhost
resizechild
lifecyclechild
rpcshared
eventshared

Navigation

For delegated routing:

  1. the child emits route-intent
  2. the host validates and commits the route
  3. the host sends route-commit
  4. the child converges without re-emitting the same route back to the host

Recovery

  • host and child emit control:heartbeat every 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

Before Leave

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

Child-to-Child Coordination

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.

Security Model

  • bootstrap must validate origin and 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

Development

pnpm install
pnpm build
pnpm test

Project Policies

Protocol Reference

See PROTOCOL.md for the message model, lifecycle rules, and integration contract in more detail.

About

Wormhole,是一个微前端框架,基于 iframe 和一整套主子协议实现。

Resources

Code of conduct

Contributing

Security policy

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Wormhole

English | 简体中文

@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.

Why Wormhole

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

Package Layout

  • @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

Feature Summary

Wormhole v1 includes:

  • control:hello / control:ack / control:ready bootstrap lifecycle
  • postMessage bootstrap plus MessageChannel runtime transport
  • route negotiation through route-intent and route-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

Installation

pnpm add @i-xor/wormhole rxjs

React and Vue wrappers expect their respective peer dependencies to be installed in the consuming application.

Quick Start

Host

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
})

Child

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'})

Native Custom Element

<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% and height: 100%
  • when the child reports control:resize, the element and internal iframe expand to max(100%, contentHeight)
  • the host page should still provide a reliable height chain for the embedding container

React Wrapper

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}/>

Vue Wrapper

<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>

Runtime Model

Capability Ownership

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:

CapabilityTypical authority
routehost
contexthost
authhost
permissionhost
resizechild
lifecyclechild
rpcshared
eventshared

Navigation

For delegated routing:

  1. the child emits route-intent
  2. the host validates and commits the route
  3. the host sends route-commit
  4. the child converges without re-emitting the same route back to the host

Recovery

  • host and child emit control:heartbeat every 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

Before Leave

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

Child-to-Child Coordination

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.

Security Model

  • bootstrap must validate origin and 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

Development

pnpm install
pnpm build
pnpm test

Project Policies

Protocol Reference

See PROTOCOL.md for the message model, lifecycle rules, and integration contract in more detail.

About

Wormhole,是一个微前端框架,基于 iframe 和一整套主子协议实现。

Resources

Code of conduct

Contributing

Security policy

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Wormhole

English | 简体中文

@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.

Why Wormhole

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

Package Layout

  • @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

Feature Summary

Wormhole v1 includes:

  • control:hello / control:ack / control:ready bootstrap lifecycle
  • postMessage bootstrap plus MessageChannel runtime transport
  • route negotiation through route-intent and route-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

Installation

pnpm add @i-xor/wormhole rxjs

React and Vue wrappers expect their respective peer dependencies to be installed in the consuming application.

Quick Start

Host

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
})

Child

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'})

Native Custom Element

<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% and height: 100%
  • when the child reports control:resize, the element and internal iframe expand to max(100%, contentHeight)
  • the host page should still provide a reliable height chain for the embedding container

React Wrapper

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}/>

Vue Wrapper

<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>

Runtime Model

Capability Ownership

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:

CapabilityTypical authority
routehost
contexthost
authhost
permissionhost
resizechild
lifecyclechild
rpcshared
eventshared

Navigation

For delegated routing:

  1. the child emits route-intent
  2. the host validates and commits the route
  3. the host sends route-commit
  4. the child converges without re-emitting the same route back to the host

Recovery

  • host and child emit control:heartbeat every 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

Before Leave

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

Child-to-Child Coordination

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.

Security Model

  • bootstrap must validate origin and 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

Development

pnpm install
pnpm build
pnpm test

Project Policies

Protocol Reference

See PROTOCOL.md for the message model, lifecycle rules, and integration contract in more detail.

About

Wormhole,是一个微前端框架,基于 iframe 和一整套主子协议实现。

Resources

Code of conduct

Contributing

Security policy

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages