Guides / Renderer API (window.ow)

Renderer API (window.ow)

The kernel injects window.ow into every document it loads. This object is your direct line to the native kernel — there is no preload script and no IPC to a Node process by default.

// Call any native module function
const text = await ow.invoke<string>('fs', 'readText', '/etc/hostname')

Surface

ow.invoke<T>(module: string, fn: string, ...args: unknown[]): Promise<T>
  // rejects with Error('ow: timeout <module>/<fn>') if the kernel does not
  // answer within 30 s

ow.invokeSync(module: string, fn: string, ...args: unknown[]): unknown
  // ⚠️ blocks the renderer. Bootstrap only. Uses ow-sync:// internally.

ow.readShared(handle: { id: string; size: number }): Promise<ArrayBuffer>
  // reads a shared-memory region published by the kernel (payloads ≥ 256 KB)
  // without going through JSON/base64

ow.on(name: string, cb: (payload: unknown) => void): () => void
  // subscribe to events; returns an unsubscribe function

ow.emit(name: string, payload?: unknown): void
  // JS → native + other JS listeners

ow.emitTo(targetWindowId: number, name: string, payload?: unknown): void
  // directed window → window message

ow.findInPage(text: string, opts?: { matchCase?: boolean; backwards?: boolean })
  : { matches: number; active: number }
  // JS helper around window.find

window.__owWindowId: number
  // the id of the current window

src/ow.d.ts in a scaffolded app declares this surface for TypeScript.

Calling modules

ow.invoke(module, fn, ...args) marshals args as a JSON array and calls the matching native function. The kernel resolves the module (a .owm loaded with dlopen, or a builtin linked into the kernel) and runs the function.

// builtin modules (linked into the kernel)
await ow.invoke('dialog', 'showOpenDialog', { properties: ['openFile'] })
await ow.invoke('session', 'cookiesGet', 'https://example.com')

// stock .owm modules
await ow.invoke('fs', 'writeFile', '/tmp/a.txt', 'hello')
await ow.invoke('screen', 'getAllDisplays')

// your own modules (native/*.cpp)
await ow.invoke('files', 'readText', '/etc/hostname')

Errors from native code reject the promise with the returned message.

Synchronous calls

ow.invokeSync uses a blocking XHR to ow-sync:// and is only safe before the app has interactive state. It exists for bootstrap tasks (for example reading a synchronous config before the first paint). Do not call it from event handlers.

const theme = ow.invokeSync('theme', 'isDark') as boolean

Large binary payloads

When a function returns a payload of 256 KB or more, the kernel does not embed it in JSON. Instead it publishes the bytes to a shared-memory region and returns a handle:

type ShmHandle = { __ow_shm: { id: string; size: number } }

Read it with ow.readShared, which returns an ArrayBuffer served from the mmap — no base64, no extra kernel copy:

const res = await ow.invoke<{ __ow_shm?: ShmHandle['__ow_shm']; b64?: string }>(
  'fs', 'readFile', '/tmp/big.iso',
)
if (res.__ow_shm) {
  const buf = await ow.readShared(res.__ow_shm)   // ArrayBuffer
}

Functions documented as returning __ow_shm include fs.readFile, fs.read, net.request (large bodies), clipboard.readImage, capturer.captureScreen, and window.capturePage.

Events

Events flow from the kernel to the renderer:

const off = ow.on('fs.watch', (payload) => {
  // payload: { watcherId, events: [{ type, path }] }
})
// later
off()

ow.emit(name, payload) goes the other way (JS → native) and also notifies other JS listeners in the same document. ow.emitTo(targetWindowId, name, payload) sends an event to a specific other window.

DOM attributes the kernel understands

The bridge scans documents for a few attributes related to custom title bars:

<div data-ow-drag>…</div>                         <!-- window drag region -->
<button data-ow-no-drag>…</button>                <!-- excluded from drag -->
<div data-ow-resize="bottom-right">…</div>        <!-- manual resize handle -->

Resize edges: left | right | top | bottom | top-left | top-right | bottom-left | bottom-right.

When to use the main process instead

ow.invoke is the right tool for almost everything: it is the shortest path to native code. Reach for ow.invoke('node', 'call', { fn, args }) only when you need actual Node — a database driver, a native addon, or an existing Node library. See IPC and Main process.

Next steps

Edit this page on GitHub ↗