Skip to content

Desktop IPC Transport

Desktop IPC transport is for Electron/Tauri-style architectures where host and remote runtimes live in different processes/webviews.

Typical fit:

  • desktop extension platforms;
  • plugin runtimes isolated from main UI process;
  • native shell orchestration with explicit IPC policies.

@omnicajs/vue-remote still needs the same channel contract. IPC is just the delivery mechanism.

Host Renderer (Desktop UI)
-> createReceiver()
-> createEndpoint(fromDesktopIpc(...))
-> call.run(receiver.receive, hostBridge)
Desktop Main Process / IPC Layer
-> routes messages between host and remote peers
-> applies policy, auth/capability checks, and lifecycle rules
Remote Runtime (isolated renderer/webview/process)
-> createEndpoint(fromDesktopIpc(...))
-> expose run/release

Build a MessageEndpoint adapter over your desktop IPC bridge:

import type { MessageEndpoint } from '@remote-ui/rpc'
type DesktopBridge = {
send(channel: string, payload: unknown): void;
on(channel: string, listener: (payload: unknown) => void): void;
off(channel: string, listener: (payload: unknown) => void): void;
close?(): void;
}
export function fromDesktopIpc(
bridge: DesktopBridge,
channel = 'vue-remote:rpc'
): MessageEndpoint {
const listeners = new Set<(event: MessageEvent) => void>()
const onPayload = (payload: unknown) => {
const event = { data: payload } as MessageEvent
for (const listener of listeners) {
listener(event)
}
}
bridge.on(channel, onPayload)
return {
postMessage(message: any) {
bridge.send(channel, message)
},
addEventListener(event, listener) {
if (event === 'message') listeners.add(listener)
},
removeEventListener(event, listener) {
if (event === 'message') listeners.delete(listener)
},
terminate() {
bridge.off(channel, onPayload)
bridge.close?.()
},
}
}

Then use standard endpoint flow:

const endpoint = createEndpoint<RemoteApi>(fromDesktopIpc(window.desktopBridge))
await endpoint.call.run(receiver.receive, hostBridge)

Electron:

  • expose a narrow IPC API from preload (contextBridge);
  • keep contextIsolation: true;
  • avoid passing unrestricted objects from main process.

Tauri:

  • use command/event channels as a constrained bridge;
  • keep allowlists explicit in capabilities;
  • avoid broad wildcard event pipes for untrusted plugins.
  1. Treat IPC channels as privileged boundaries.
  2. Version protocol and enforce compatibility checks.
  3. Add capability-based routing per extension session.
  4. Ensure teardown closes channels and releases retained references.