Native messaging
Browser extensions talk to programs on the computer through native messaging: the extension calls
connectNative or sendNativeMessage with a host name, and the browser starts the program that
name is registered for and exchanges messages with it over standard input and output. Sabine
registers such a host for your app with every browser it knows about.
Registering a host
Section titled “Registering a host”Pass a NativeMessagingHost to .native_messaging_host(...). The host program ships with the app
as a resource, so resource_path finds it in the bundle once the
app is packaged:
use sabine::prelude::*;use sabine::{NativeMessagingHost, resource_path};
fn main() { SabineWindow::main(|window| { let window = window.app(); let Some(folder) = resource_path("browser-bridge") else { return Ok(window); }; let program = if cfg!(windows) { "notes-bridge.exe" } else { "notes-bridge" }; Ok(window.native_messaging_host(NativeMessagingHost { id: "com.example.notes".into(), name: "Notes browser bridge".into(), executable: folder.join(program), allowed_origins: vec!["chrome-extension://abcdefghijklmnopabcdefghijklmnop/".into()], allowed_extensions: vec!["notes@example.com".into()], })) });}Name the folder the host program is built into under [resources] in Sabine.toml:
[resources]browser-bridge = "bridge/bin"Build the host program into that folder before bundling, for example with
cargo install --path bridge --root bridge. The bundle copies resources as they are, keeping the
program executable, and signs the programs among them on macOS and Windows along with the app.
| Field | Meaning |
|---|---|
id |
The host name extensions pass to connectNative, exactly as written. |
name |
A human-readable description, written to the browser manifests. |
executable |
The program the browser starts. It must exist; Sabine records its absolute path. |
allowed_origins |
The Chromium-based extensions allowed to connect, as exact chrome-extension://<id>/ origins. |
allowed_extensions |
The Firefox add-on ids allowed to connect, such as notes@example.com. |
Chromium-based browsers and Firefox-based browsers get separate manifests, each listing only its own extensions. An empty list grants no extensions of that kind access.
Sabine checks the host before it writes anything, and a host it can’t register stops the launch with an error naming the problem:
idcontains lowercase letters, digits and underscores, with single dots between nonempty parts, such ascom.example.notes. Sabine rejects other ids instead of renaming them.- Every
allowed_originsentry ischrome-extension://, a 32-character extension id and a trailing/. allowed_extensionsentries are explicit add-on ids, without spaces or wildcards.executableis a file, with a path that is valid Unicode.
Sabine writes the registration each time the app launches, so it stays correct when the app moves
or updates. Development runs, such as sabine dev, register no native messaging hosts.
The host program
Section titled “The host program”The host is a separate program you ship with the app, as above. The browser starts it when an extension connects, and sends each message as a 32-bit length in native byte order followed by that many bytes of JSON; the host replies the same way on standard output. A minimal host that answers every message:
use std::io::{self, Read, Write};
fn main() -> io::Result<()> { let mut input = io::stdin().lock(); let mut output = io::stdout().lock(); let mut length = [0; 4]; while input.read_exact(&mut length).is_ok() { let mut message = vec![0; u32::from_ne_bytes(length) as usize]; input.read_exact(&mut message)?; let reply = br#"{"ok":true}"#; output.write_all(&(reply.len() as u32).to_ne_bytes())?; output.write_all(reply)?; output.flush()?; } Ok(())}Browsers and where the manifests go
Section titled “Browsers and where the manifests go”Registrations are per user. Sabine writes manifests for Chrome and its beta, dev and canary channels, Chromium, Edge, Brave, Vivaldi, Opera, Helium, Firefox and LibreWolf. Zen reads Firefox’s manifests, so it is covered too.
Chromium-based browsers read <id>.json from a NativeMessagingHosts folder in their
configuration folder, such as ~/.config/google-chrome/NativeMessagingHosts. Firefox and
LibreWolf read it from ~/.mozilla/native-messaging-hosts and
~/.librewolf/native-messaging-hosts.
Browsers installed from Flatpak or Snap keep their configuration inside their sandbox, in
~/.var/app/<flatpak id>/ and ~/snap/<snap name>/common/. Sabine writes manifests there too
for the browsers whose folders exist.
An app running from an AppImage exists at its mount point only while it runs, so a host
program inside the AppImage is registered through a small launcher in
~/.local/share/sabine/native-messaging/<id>. The launcher starts the AppImage file, which
hands the browser’s arguments to the host program instead of starting the app.
The manifests are written to %LOCALAPPDATA%\Sabine\native-messaging\chromium\<id>.json and
...\firefox\<id>.json, and each browser family’s registry key under the current user points
to its manifest:
HKCU\Software\Google\Chrome\NativeMessagingHosts\<id>, which Chrome’s channels, Vivaldi, Opera and Helium readHKCU\Software\Chromium\NativeMessagingHosts\<id>HKCU\Software\Microsoft\Edge\NativeMessagingHosts\<id>HKCU\Software\BraveSoftware\Brave-Browser\NativeMessagingHosts\<id>HKCU\Software\Mozilla\NativeMessagingHosts\<id>, which Firefox forks read
Each browser reads <id>.json from a NativeMessagingHosts folder in its folder under
~/Library/Application Support, such as Google/Chrome/NativeMessagingHosts,
Microsoft Edge/NativeMessagingHosts or Mozilla/NativeMessagingHosts.
Removing hosts
Section titled “Removing hosts”sabine uninstall, and the uninstallers of Windows .exe and MSI packages, remove the manifests
whose program is inside the app’s installation, along with the Windows registry keys that still
point to them. A host name another installation has registered since keeps its manifest.
Removing a deb, rpm or AppImage, or moving a macOS app to the Trash, leaves the manifests in each user’s browser folders. Browsers report the host as missing until the app is installed and launched again.
Related pages
Section titled “Related pages”- Installing covers
sabine installandsabine uninstall. - Security model covers what the app exposes to outside programs.