You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This guide explains how to create plugins for ze. Plugins extend ze's functionality by handling BGP events, providing commands, managing address families, and injecting custom capabilities.
Quick Start (5 minutes)
1. Create a Plugin
package main
import (
"context""fmt""os""codeberg.org/thomas-mangin/ze/pkg/plugin/sdk"
)
funcmain() {
// Connect to the engine via TLS using environment variables.// Reads ZE_PLUGIN_HUB_HOST (default 127.0.0.1), ZE_PLUGIN_HUB_PORT// (default 12700), and ZE_PLUGIN_HUB_TOKEN (required).p, err:=sdk.NewFromEnv("my-plugin")
iferr!=nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
deferp.Close()
// Register event handler (receives BGP events as JSON strings).p.OnEvent(func(eventstring) error {
fmt.Println("event:", event)
returnnil
})
// Register config handler (receives config sections during startup).p.OnConfigure(func(sections []sdk.ConfigSection) error {
for_, s:=rangesections {
fmt.Printf("config root=%s data=%s\n", s.Root, s.Data)
}
returnnil
})
// Subscribe to updates at startup (race-free -- registered atomically// with the "ready" signal before routes start flowing).p.SetStartupSubscriptions([]string{"update"}, nil, "")
// Run the 5-stage startup protocol and enter the event loop.// Returns nil on clean shutdown (bye received).iferr:=p.Run(context.Background(), sdk.Registration{
Families: []sdk.FamilyDecl{{Name: "ipv4/unicast", Mode: "both"}},
}); err!=nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
}
2. Build and Test
go build -o my-plugin
ze plugin validate binary ./my-plugin
3. Configure Ze
plugin {
hub {
server local {
ip 127.0.0.1;
port 12700;
secret "shared-token";
}
}
}
process my-plugin {
run "./my-plugin";
}
The engine sets these environment variables before launching the plugin process:
Variable
Purpose
ZE_PLUGIN_HUB_HOST
TLS host to connect to (default 127.0.0.1)
ZE_PLUGIN_HUB_PORT
TLS port to connect to (default 12700)
ZE_PLUGIN_HUB_TOKEN
Per-plugin auth token (unique per plugin, cleared from env after read)
ZE_PLUGIN_CERT_FP
SHA-256 fingerprint of the engine's TLS certificate (for cert pinning)
ZE_PLUGIN_NAME
Plugin name as configured in ze
Each plugin receives its own unique auth token. The token is bound to the plugin name: a plugin cannot use its token to authenticate as a different plugin. The token is automatically cleared from the OS environment after the SDK reads it, so it is not visible in /proc/<pid>/environ.
When ZE_PLUGIN_CERT_FP is set, the SDK verifies the engine's TLS certificate fingerprint during the handshake, preventing man-in-the-middle attacks.
What Can Plugins Do?
Handle BGP events -- Receive UPDATE, OPEN, peer state changes as JSON
Provide commands -- Expose custom commands via the API
Manage address families -- Encode/decode NLRI for custom address families
Extend configuration -- Add config sections with YANG schemas
Validate config changes -- Accept or reject config during reload
Apply config diffs -- React to config changes at runtime
Inject capabilities -- Add BGP capabilities to OPEN messages
Validate OPEN messages -- Accept or reject peer sessions
Emit events -- Push events for other plugins to consume
Dispatch commands -- Invoke commands on other plugins through the engine
Plugins communicate via a single bidirectional TLS connection using the #<id> <verb> [<json>] wire format. MuxConn multiplexes concurrent RPCs by distinguishing responses (ok/error) from requests (method name as verb).
For internal plugins (running as goroutines inside the engine), the connection is a net.Pipe, and after startup a DirectBridge bypasses the pipe for event delivery.