-
Notifications
You must be signed in to change notification settings - Fork 2
external plugins
Pre-Alpha. This page describes behavior that may change.
An external plugin is a separate process. Ze launches it, sets a handful of environment variables, and the plugin connects back over a single TLS connection and speaks the plugin protocol. Any language that can read and write lines and parse JSON can implement one. The choice to go external (rather than in-tree Go) is the subject of the plugin development index; this page covers the wire protocol and the startup flow.
Every message between the engine and the plugin is a single newline-terminated line.
#<id> <method> [<json-params>]\n # Request
#<id> ok [<json-result>]\n # Success response
#<id> error [<json-error>]\n # Error response
-
<id>is a monotonically increasinguint64correlation id. -
<method>uses YANG-style<module>:<rpc-name>naming (ze-plugin-engine:declare-registration,ze-plugin-callback:configure). - JSON payloads are optional, omitted when empty or null.
- Responses use
okorerroras the verb. Requests use the method name.
A single connection carries every RPC in both directions. MuxConn multiplexes the connection so plugin requests to the engine and engine requests to the plugin do not block each other. A background reader routes incoming lines by verb: ok and error go to the waiting CallRPC caller by id, method-name requests go to the plugin's request handler.
The engine starts the plugin process with a fixed set of environment variables.
| Variable | Purpose |
|---|---|
ZE_PLUGIN_HUB_HOST |
TLS host. Default 127.0.0.1. |
ZE_PLUGIN_HUB_PORT |
TLS port. Default 12700. |
ZE_PLUGIN_HUB_TOKEN |
Per-plugin auth token. Cleared from the environment after the SDK reads it. |
ZE_PLUGIN_CERT_FP |
SHA-256 fingerprint of the engine's TLS certificate, for pinning. |
ZE_PLUGIN_NAME |
The plugin name as configured in Ze. |
The plugin opens a TLS connection to <HOST>:<PORT>, verifies the engine's certificate fingerprint against ZE_PLUGIN_CERT_FP, and authenticates with ZE_PLUGIN_HUB_TOKEN. The token is bound to the plugin name: a plugin cannot authenticate as a different plugin with its own token.
There is a standalone mode where the plugin uses stdin and stdout with inline MuxConn framing instead of TLS. That mode exists for debugging, for the ExaBGP bridge, and for running a plugin under ze exabgp plugin from the command line.
Every plugin goes through five stages before it enters the runtime event loop. The Go SDK handles the flow for you through Plugin.Run(). For a non-Go external plugin, you implement the stages yourself.
-
declare-registration(plugin → engine). The plugin declares its families, commands, dependencies, config roots it wants to receive, and its YANG schema. The engine validates the declaration and repliesok. -
configure(engine → plugin). The engine delivers the plugin's config sections as a list of{root, data}pairs. The plugin parses them and repliesok, or rejects the config with an error (which aborts startup). -
declare-capabilities(plugin → engine). The plugin declares any BGP capabilities it wants the engine to inject into OPEN messages to specific peers. Encoding ishex,b64, ortext. -
share-registry(engine → plugin). The engine sends the full command registry from every other loaded plugin. Informational: the plugin uses it to route command dispatches. -
ready(plugin → engine). The plugin signals readiness with any startup subscriptions (event types, peer filters, format preference). The subscription is attached atomically to the ready signal so no event is lost in the race.
After stage 5, the engine can send deliver-event and execute-command requests at runtime. The plugin can send update-route, emit-event, subscribe-events, dispatch-command, and a handful of codec helpers back. Shutdown is a single bye request from the engine with a reason string.
Stage 1 registration, then a stage-2 configure, then a runtime event delivery.
#1 ze-plugin-engine:declare-registration {"families":[{"name":"ipv4/flow","mode":"both"}],"commands":[{"name":"flowspec status","description":"Show FlowSpec status"}],"wants-config":["bgp"]}
#1 ok
#1 ze-plugin-callback:configure {"sections":[{"root":"bgp","data":"{\"bgp\":{\"peer\":{...}}}"}]}
#1 ok
#42 ze-plugin-callback:deliver-event {"event":"{\"type\":\"state\",\"bgp\":{\"peer\":{\"address\":\"10.0.0.1\"},\"state\":\"up\"}}"}
#42 ok
The correlation id is reset per direction: the engine and the plugin each allocate their own id space, and MuxConn routes by verb.
Everything an in-tree Go plugin can do, with the exception that in-process optimisations (DirectBridge, shared memory access) are not available. From the API surface:
- Receive BGP events as JSON.
- Declare and serve commands (
execute-command). - Encode and decode NLRI for a registered address family.
- Extend the config tree with a YANG schema.
- Verify and apply config diffs at reload time.
- Inject BGP capabilities into OPEN.
- Validate incoming OPEN messages.
- Emit events onto the bus for other plugins to consume.
- Dispatch commands through the engine.
The protocol is text and JSON. Any language that can open a TLS socket (or use stdin/stdout in standalone mode), read newline-terminated lines, and parse JSON can implement it.
The moving parts to get right:
- Line framing: every message is one line terminated with
\n. - Correlation: maintain a monotonic id counter for outgoing requests and route incoming
ok/errorback to the caller. - Multiplexing: the same connection carries requests in both directions. Your reader must distinguish method-name verbs (which are incoming requests) from
ok/error(which are responses to your outgoing requests). - The 5-stage startup flow, in order.
- Cert pinning in the TLS handshake.
A Python implementation that mirrors the ExaBGP programmable model would read events from the connection, write update commands back, and use execute-command as the point where it extends the CLI with its own subcommands. A Rust implementation would use tokio for the async reader. A Ruby implementation would work through OpenSSL::SSL::SSLSocket. None of this requires generated stubs.
- Plugin development index for when to pick external over in-tree Go.
- Go plugins for the SDK-driven path.
- In-tree plugin protocol reference for the full wire protocol and every RPC.
Adapted from main/docs/plugin-development/protocol.md.
Unreviewed draft. This wiki was authored in bulk and has not been reviewed. File corrections on the issue tracker.
- Overview
- YANG Model
- Editor Workflow
- Archive and Rollback
- System
- Interfaces
- BFD
- FIB
- Firewall
- Traffic Control
- L2TP/PPP
- VPP Data Plane
- RPKI
- TACACS+ AAA
- Fleet
- BGP
- Starting and Stopping
- Show Commands
- Monitoring
- Logging
- Operational Reports
- Healthcheck
- MRT Analysis
- Upgrade and Restart
- Storage
- Policy
- Core
- Resilience
- Validation
- Capabilities
- Address Families
- Protocol
- Subsystems
- Infrastructure
- Route Server at an IXP
- Transit Edge with RPKI
- Public Looking Glass
- ExaBGP Migration Walkthrough
- FlowSpec Injection
- Chaos-Tested Peering
- AS Path Topology