Docs Bridge
Alpha documentation — APIs may change between 0.x releases.

Control bridge access

Vidra separates what the native host implements from what the WebView may use. Registering a module makes it available to the host; access remains denied until vidra.config.ts grants the generated method or event token.

This default-deny policy limits the damage a compromised web bundle can cause. It applies equally to generated proxies and calls through vidra.unsafe. Host-initiated JavaScript contracts are outside this policy.

Grant native methods

Built-in method tokens come from @vidra-dev/sdk/config:

import { defineConfig, native } from "@vidra-dev/sdk/config";

export default defineConfig({
  bridge: {
    allow: [
      native.app.getInfo,
      native.clipboard.getText,
      native.notifications.show,
    ],
  },
});

For an application contract, import its generated access catalog:

import { defineConfig } from "@vidra-dev/sdk/config";
import { native } from "./ui/src/generated/access.js";

export default defineConfig({
  bridge: {
    allow: [native.greeting.greet],
  },
});

The generated tokens provide autocomplete and fail TypeScript checking when a contract or member is renamed. String contract names are not accepted here.

Grant events

An event must be registered by the native host and granted before Vidra sends it to the WebView:

import { defineConfig, events } from "@vidra-dev/sdk/config";

export default defineConfig({
  bridge: {
    events: [
      events.appWindow.resized,
      events.connectivity.changed,
    ],
  },
});

Use events from ./ui/src/generated/access.js for application-defined event contracts.

Scope filesystem methods

Filesystem methods cannot be added to the general allow list. Pair each method with a symbolic native root:

import {
  defineConfig,
  filesystemRoots,
  native,
  within,
} from "@vidra-dev/sdk/config";

export default defineConfig({
  bridge: {
    filesystem: [
      {
        root: within(filesystemRoots.documents, "MyApp"),
        allow: [
          native.filesystem.readText,
          native.filesystem.writeText,
          native.filesystem.listDirectory,
        ],
      },
    ],
  },
});

Available roots are appData, cache, documents, and downloads. within(root, relativePath) narrows a root; .. traversal is rejected while loading the config.

Filesystem calls still pass fully qualified OS paths. The host resolves the symbolic root on the user’s machine and rejects paths outside it, including existing symbolic links that escape the configured scope.

Build-time policy

vidra build and vidra dev execute vidra.config.ts in the CLI, validate it, and write a normalized policy into the native application. The WebView cannot change that policy at runtime.

The config may also be a callback:

import { defineConfig, native } from "@vidra-dev/sdk/config";

export default defineConfig(({ mode, target }) => ({
  bridge: {
    allow: [
      native.app.getInfo,
      ...(mode === "development" ? [native.clipboard.getText] : []),
      ...(target === "windows" ? [native.appWindow.setTitle] : []),
    ],
  },
}));

Changing grants during vidra dev rebuilds and relaunches the host because the installed policy is immutable.

Compatibility and updates

The generated frontend records the policy fingerprint it expects. Startup fails before bridge traffic if it does not match the native host.

Web-bundle update entries carry the same fingerprint, so a bundle is considered only when its required access exactly matches the installed policy. Increasing or reducing access therefore requires a native release before a web-only bundle can depend on the new policy.

vidra.capabilities() reports only methods and events that are both registered and granted. Use it for runtime feature detection, not as a replacement for declaring the policy.