How Browser Extensions Use Native Messaging to Send Downloads to Motrix

Browser extensions send downloads to Motrix by invoking chrome.runtime.sendNativeMessage('app.motrix.bridge', payload), which spawns the motrix-native-host binary to forward the request via local IPC to Motrix's internal Aria2 download engine.

Motrix is an open-source, Electron-based download manager that exposes a native-messaging host, enabling Chrome, Edge, and Firefox extensions to delegate file downloads directly to the application. This architecture eliminates the need for background HTTP servers or manual link copying, allowing the browser to hand off download metadata via stdin to a trusted native process. According to the agalwood/Motrix source code, the bridge consists of a Rust-based native host, a dynamically generated manifest file, and a local Unix socket or Windows named pipe for inter-process communication.

Architecture Overview

The native-messaging integration involves five primary components that handle the journey from browser click to active download:

Component Role Key Source File
Browser Extension Captures download URLs and forwards JSON messages via the browser's Native Messaging API. src/renderer/extension/*
Native-Messaging Manifest Registers the host binary with the browser, defining the app.motrix.bridge identifier and allowed extensions. src/main/bridge/native-messaging-installer.ts
Native Host Binary A Rust executable (motrix-native-host) that validates browser messages and relays them to Motrix over local IPC. packages/native-host/src/main.rs
Motrix Core Listens on a local socket, receives the download command, and creates an Aria2 task via aria2.addUri. src/main/engine/aria2-service.ts
Installation Helper Writes the manifest to browser-specific directories (Chrome, Edge, Firefox) during Motrix's first run. src/main/bridge/native-messaging-installer.ts

Message Flow

When a user initiates a download through the extension, the following sequence occurs:

  1. Extension Invokes Native Messaging: The extension calls chrome.runtime.sendNativeMessage('app.motrix.bridge', payload) with a JSON object containing the URL, filename, and optional parameters.

  2. Browser Spawns Host: The browser locates app.motrix.bridge.json (installed in the browser's NativeMessagingHosts directory), launches the executable specified in the "path" field, and pipes the JSON payload to its stdin.

  3. Host Validates Input: The motrix-native-host binary reads the single-line JSON message from stdin, deserializes it, and validates required fields (url, filename).

  4. IPC Forwarding: The host opens a connection to Motrix's local IPC endpoint—a Unix domain socket at $XDG_RUNTIME_DIR/motrix.sock on Linux/macOS or a Windows named pipe (\\.\pipe\motrix-ipc).

  5. Download Creation: Motrix's main process receives the message, parses the command, and invokes addDownload() in src/main/engine/aria2-service.ts to create an Aria2 task.

  6. Response Return: The native host writes a JSON response (success or error) to stdout, which the browser relays back to the extension's callback function.

All communication remains local to the machine; no external network requests are required for the handshake.

Implementation Details

Browser Extension Integration

Browser extensions interact with Motrix using the standard chrome.runtime.sendNativeMessage API. The extension must declare the nativeMessaging permission in its manifest and use the exact host name app.motrix.bridge as registered by Motrix.

// background.js - Extension side implementation
function sendToMotrix(url, filename, options = {}) {
  const payload = {
    url: url,
    filename: filename,
    options: options  // e.g., { referer: "https://example.com/" }
  };

  chrome.runtime.sendNativeMessage(
    'app.motrix.bridge',  // Must match the manifest name
    payload,
    (response) => {
      if (chrome.runtime.lastError) {
        console.error('Native messaging error:', chrome.runtime.lastError);
      } else {
        console.log('Motrix queued download:', response);
      }
    }
  );
}

Native-Messaging Manifest

During installation, Motrix executes the logic in src/main/bridge/native-messaging-installer.ts to generate app.motrix.bridge.json and copy the native binary into the application resources. The manifest specifies the host binary path and allowed extension IDs.

{
  "name": "app.motrix.bridge",
  "description": "Motrix download bridge",
  "path": "/Applications/Motrix.app/Contents/Resources/bin/motrix-native-host",
  "type": "stdio",
  "allowed_origins": [
    "chrome-extension://your-extension-id/",
    "chrome-extension://edge-extension-id/"
  ]
}

On macOS, the installer writes this file to ~/Library/Application Support/Google/Chrome/NativeMessagingHosts/. On Linux, it targets ~/.config/google-chrome/NativeMessagingHosts/ and ~/.mozilla/native-messaging-hosts/. Windows installations use registry keys under HKEY_CURRENT_USER\Software\Google\Chrome\NativeMessagingHosts\.

Native Host Binary

The native host is a Rust executable compiled from packages/native-host/src/main.rs. It handles the stdio-based protocol required by browser native messaging: reading length-prefixed JSON messages from stdin and writing responses to stdout.

use std::io::{self, BufRead, Write};
use serde::Deserialize;

#[derive(Deserialize)]
struct DownloadRequest {
    url: String,
    filename: String,
    #[serde(default)]
    options: serde_json::Value,
}

fn main() -> io::Result<()> {
    // Native messaging protocol: read JSON line from stdin
    let stdin = io::stdin();
    let request: DownloadRequest = serde_json::from_reader(stdin.lock())?;

    // Connect to Motrix via local Unix socket
    let mut stream = std::os::unix::net::UnixStream::connect("/tmp/motrix.sock")?;
    
    let cmd = serde_json::json!({
        "cmd": "add",
        "url": request.url,
        "filename": request.filename,
        "options": request.options
    });
    
    stream.write_all(cmd.to_string().as_bytes())?;
    stream.flush()?;

    // Return success response to browser
    let response = serde_json::json!({ "status": "ok", "task": "queued" });
    println!("{}", response);
    Ok(())
}

The actual production binary includes additional error handling, logging, and cross-platform IPC abstraction for Windows named pipes.

Motrix IPC Listener

Motrix's main Electron process creates a local server to receive commands from the native host. The implementation in src/main/engine/aria2-service.ts parses the incoming JSON and interfaces with the Aria2 RPC client.

import net from 'net';
import { addDownload } from './aria2-service';

const IPC_SOCKET_PATH = process.platform === 'win32' 
  ? '\\\\.\\pipe\\motrix-ipc' 
  : `${process.env.XDG_RUNTIME_DIR || '/tmp'}/motrix.sock`;

const server = net.createServer((socket) => {
  let data = '';
  socket.on('data', chunk => data += chunk);
  socket.on('end', () => {
    try {
      const msg = JSON.parse(data);
      if (msg.cmd === 'add') {
        // Forward to Aria2 engine
        addDownload(msg.url, { out: msg.filename, ...msg.options });
        socket.write(JSON.stringify({ status: 'queued' }));
      }
    } catch (e) {
      socket.write(JSON.stringify({ status: 'error', message: e.message }));
    }
    socket.end();
  });
});

server.listen(IPC_SOCKET_PATH, () => {
  console.log(`Native messaging IPC listening on ${IPC_SOCKET_PATH}`);
});

Key Source Files

The native-messaging bridge is implemented across several critical files in the agalwood/Motrix repository:

Summary

  • Browser extensions communicate with Motrix using the standard Native Messaging API, calling chrome.runtime.sendNativeMessage with the host name app.motrix.bridge.
  • The motrix-native-host Rust binary acts as the intermediary, reading JSON from browser stdin and forwarding commands to Motrix via local IPC.
  • Manifest installation is handled automatically by src/main/bridge/native-messaging-installer.ts, which registers the host with Chrome, Edge, and Firefox during Motrix's initial launch.
  • Motrix receives download requests through a local Unix socket or Windows named pipe, eliminating network overhead and keeping communication confined to the local machine.
  • The download is ultimately created by aria2-service.ts, which wraps the Aria2 RPC interface to start the actual file transfer.

Frequently Asked Questions

What is the native messaging host name used by Motrix?

Motrix registers its native host under the identifier app.motrix.bridge. Browser extensions must use this exact string when calling chrome.runtime.sendNativeMessage or browser.runtime.sendNativeMessage to establish communication with the download manager.

Which browsers support sending downloads to Motrix via native messaging?

The implementation in src/main/bridge/native-messaging-installer.ts supports Google Chrome, Microsoft Edge, and Mozilla Firefox across macOS, Linux, and Windows. The installer writes the appropriate manifest files to each browser's NativeMessagingHosts directory (or registry on Windows) during the first run of Motrix.

How does Motrix handle the download request after receiving it from the browser?

Once the native host forwards the message via local IPC, Motrix's main process parses the JSON command in the IPC listener and invokes the addDownload() function defined in src/main/engine/aria2-service.ts. This function constructs an Aria2 RPC payload and calls aria2.addUri with the URL, output filename, and any additional options (such as referer headers) provided by the extension.

Where is the native messaging manifest installed on different operating systems?

According to the source code in native-messaging-installer.ts, Motrix installs app.motrix.bridge.json to the following locations:

  • macOS: ~/Library/Application Support/Google/Chrome/NativeMessagingHosts/ (Chrome/Edge) and ~/Library/Application Support/Mozilla/NativeMessagingHosts/ (Firefox)
  • Linux: ~/.config/google-chrome/NativeMessagingHosts/ and ~/.mozilla/native-messaging-hosts/
  • Windows: Registry keys under HKEY_CURRENT_USER\Software\Google\Chrome\NativeMessagingHosts\ and corresponding Firefox registry paths.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →