# Remote apps and capability ports

Connect an app on another origin through an iframe and a MessagePort with permissions and revocation.

This page describes WU Framework `4.0.0`. Read the [Framework 4.0 and CLI 1.2 migration guide](/docs/guides/releases/) before upgrading an existing project.

<span id="configurar-el-host"></span>

<span class="section-anchor" id="section-1"></span>

## Configure the host

```js
import { wu } from 'wu-framework/core';
import 'wu-framework/ports';
await wu.init({ apps: [{
  name: 'widget', url: 'https://widgets.example/app/', sandbox: 'remote',
  manifest: { entry: 'index.html', wu: { permissions: ['events.emit:widget:*'] } },
  grants: ['events.emit:widget:*'],
  iframe: { title: 'Remote widget', timeout: 15000, callTimeout: 10000 },
}] });
await wu.mount('widget', '#widget-slot');
await wu.update('widget', { theme: 'light' });
```

Create `#widget-slot` in the shell and serve the app from the configured origin. WU creates the iframe and transfers its port to the exact URL origin. The browser enforces cross-origin isolation; port messages execute through the `widget` facade with its requested and granted permissions.

<span id="conectar-el-documento-remoto"></span>

<span class="section-anchor" id="section-2"></span>

## Connect the remote document

```js
import { connect } from 'wu-framework/remote';

const remote = await connect({ hostOrigin: 'https://shell.example' });
const off = remote.onProps(props => {
  document.documentElement.dataset.theme = props.theme;
});
await remote.emit('widget:ready', { ready: true });
await remote.resize(document.body.scrollHeight);

window.addEventListener('pagehide', () => {
  off();
  remote.close();
}, { once: true });
```

The remote document uses its own renderer and must accept only the actual shell origin in `hostOrigin`. Calls to the host are asynchronous: await `store.get`, `store.set`, `emit` and RPC. `onProps` receives updates; `props` holds the latest values. `resize` requests a height and `whenClosed` observes closure. Data must support structured clone.

The iframe defaults to `allow-scripts allow-same-origin allow-forms`; configure it with `iframe.sandbox` and `iframe.allow`. `wu.remoteOf(name)` returns its window. `hide` retains the iframe; `unmount` removes it and revokes the port. Connection failures are `WU_REMOTE_TIMEOUT` and `WU_REMOTE_LOAD_FAILED`; a remote handler exceeding its deadline produces `WU_PORT_CALL_TIMEOUT`.

<span id="cabeceras-de-ambos-orígenes"></span>

<span class="section-anchor" id="section-3"></span>

## Headers on both origins

```json
{ "security": { "frameOrigins": ["https://widgets.example"] } }
```

In WU CLI `1.0.0`, `security.frameOrigins` adds these origins to `frame-src`, not to `script-src` or `connect-src`. The remote response must allow the shell through `frame-ancestors` and must not block it with `X-Frame-Options`. Configure its renderer and the `wu-framework/remote` entry in the remote build too.

<span id="crear-un-puerto-manualmente"></span>

<span class="section-anchor" id="section-4"></span>

## Create a port manually

```js
const capability = await wu.ports.mint('widget', { props: { theme: 'dark' } });
worker.postMessage({ wu: 1, type: 'port', app: 'widget' }, [capability.port]);
await capability.ready;
capability.push({ theme: 'light' });
capability.close('finished');
const active = wu.ports.list();
const revoked = wu.ports.revoke('widget');
```

The host creates `worker` and registers `widget` with grants beforehand; the receiver must connect the remote protocol. The port can be transferred and closed. `close` revokes that port; `revoke` closes the app ports. Pending calls reject with `WU_PORT_CLOSED`. Message communication does not directly share the store object between documents.

<span id="validación-de-origen-en-40"></span>

<span class="section-anchor" id="section-5"></span>

## Origin validation in 4.0

`connect(options)` and `waitForPort(options)` require `hostOrigin`; omitting it produces `WU_REMOTE_HOST_ORIGIN_REQUIRED`. `"*"` knowingly accepts any origin. In an iframe or popup, the sender must also be `parent` or `opener`; `source` can specify another window. The `connect(port)` overload accepts an already received port.

The app’s origin is the origin of its `url`. An `entry` on another origin fails with `WU_REMOTE_ORIGIN_MISMATCH`. A remote on the shell’s origin fails with `WU_REMOTE_SAME_ORIGIN`, unless `iframe.allowSameOrigin: true`; that exception loses cross-origin isolation. `wu.detach()` rejects remotes with `WU_WINDOW_REMOTE_UNSUPPORTED`.

The host keeps offering the port after `load` until it receives a response, so the remote can await work before connecting. Over the port, `consume()` permits only methods in the declared `shape`; without a `shape`, it excludes `Object.prototype` methods. Unmount and `destroy()` revoke the core’s ports.