Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Native plugins

A plugin is a shared library holding an endpoint — and optionally a middleware — that this binary never compiled: a proprietary broker, an in-house transport. Loading one registers it under its own name, after which routes address it like any built-in connector.

Pulsar is compiled in, so it is not a plugin here. Address it directly, as pulsar: { url: "pulsar://localhost:6650", topic: ..., subscription: ... } in a route, or pulsar://localhost:6650?topic=... from copy. Passing libmq_bridge_pulsar.{so,dylib} to --plugin or plugins: fails at startup with `pulsar` ... is already registered by another factory — that rejection is deliberate, since a second factory under a live name would silently reroute traffic. Other hosts that did not compile it in, such as mq-bridge-py, do load it as a plugin.

This is the runtime counterpart to custom endpoints: a custom endpoint is registered programmatically by code you compile in, a plugin is loaded from a file named in the config.

Loading

List the libraries under plugins:. Paths go through the usual placeholder expansion, so ${VAR} works:

plugins:
  - "${MQB_PLUGIN_DIR}/libmq_bridge_acme.so"

routes:
  orders:
    input:
      custom:
        name: acme
        config: { url: "acme://localhost:9000" }
    output:
      file:
        path: "orders.jsonl"

The name is the one the plugin exports, not the file name. A plugin providing a middleware registers that under the same name, usable in any middlewares: chain.

Plugins are loaded only from trusted startup configuration. The UI and POST /config may reorder or repeat the same canonical paths, but cannot add, remove, or retarget a plugin. Edit the startup config and restart the process for every plugin change. Loaded native libraries remain mapped and registered for the process lifetime; they are never unloaded.

Every build can load plugins; there is no cargo feature to enable.

From the CLI

--plugin <path> loads a library without touching the config, repeatable, and valid on every subcommand:

mqb --plugin ./libmq_bridge_acme.so --config config.yml

It combines with plugins: rather than replacing it. Listing the same library both ways is harmless — a library already loaded is not loaded twice.

copy takes plugin endpoints too: once a factory is registered, its name works as a URI scheme, with the query params becoming its config fields.

mqb copy --plugin ./libmq_bridge_acme.so \
  --from "acme://localhost:9000?stream=orders" --to "file:///tmp/orders.jsonl"

The URI carries strings only — url is the part before the ? (override it with an explicit ?url=), and every other param is passed to the factory as a string field. Use config mode for a factory whose config needs numbers, booleans, or nested objects.

Failure modes

Loading is strict, because the alternative is a confusing “unknown endpoint” much later, once a route asks for something nobody registered:

ConditionResult
File missing or unreadablestartup fails naming the path
Built against an incompatible ABI major versionrejected
Declares neither an endpoint nor a middlewarerejected
Name already taken by a different factoryrejected — traffic would silently reroute

At startup a bad path aborts the process. A runtime config whose canonical plugin set differs from the startup set is rejected before route validation, storage changes, or saving.

Security

A plugin is native code loaded into this process, with the same privileges — it is not sandboxed. Treat the startup configuration and CLI flags as trusted inputs, and treat the libraries they name exactly like any other native dependency. Runtime config updates cannot load a new library.

Writing one is covered in Writing a plugin.