Skip to content

Wire Protocol

Plugins using the Go SDK don't need to know the wire protocol. This section is for authors implementing plugins in other languages.

Overview

Plugins use two communication channels:

Channel Format Purpose
stdin/stdout Line-delimited JSON Lifecycle events, target pushes
Unix socket Length-prefixed msgpack Request-response hooks

stdin/stdout — Lifecycle & Pushes

Prox → Plugin: configure

Sent once after the plugin starts, and again on config reload.

{
  "method": "configure",
  "params": {
    "route_id": "gateway:0",
    "match": { "domain": "*.**", "path": "/ws" }
  }
}

Plugin → Prox: ready

Sent after configure to declare request-response capabilities. This tells prox to connect to the plugin's Unix socket for hook calls.

{
  "method": "ready",
  "params": {
    "socket": "/tmp/prox-p-12345.sock",
    "hooks": ["on_request", "on_response", "on_connect"]
  }
}

Available hook names: on_request, on_response, on_connect.

Plugin → Prox: set_targets

Push new targets to the route's balancer. Sent whenever the target pool changes.

Flat mode:

{
  "method": "set_targets",
  "params": {
    "route_id": "gateway:0",
    "targets": ["10.0.1.1:3505", "10.0.1.2:3505"]
  }
}

Grouped mode:

{
  "method": "set_targets",
  "params": {
    "route_id": "gateway:0",
    "groups": {
      "de": ["de-node1.internal:8080", "de-node2.internal:8080"],
      "us": ["us-node1.internal:8080"]
    }
  }
}
  • targets — flat replacement list (not a diff). Previous targets are discarded.
  • groups — keyed replacement map. Each key gets its own sub-balancer.
  • Use targets OR groups, not both in the same message.

Unix Socket — Request-Response Hooks

The socket uses length-prefixed msgpack frames:

[4 bytes: payload length, big-endian][msgpack payload]

Frame Structure

Each frame is an Envelope containing a hook type and the hook-specific data:

Envelope {
  hook: string    // "on_request", "on_response", "on_connect"
  data: bytes     // msgpack-encoded hook payload
}

on_request

Request payload:

Field msgpack key Type
RouteID r string
Method m string
Path p string
Query q string
Domain d string
Host ho string
Proto pr string
RemoteAddr a string
ContentLength cl int64
Headers h map[string]string
Body bd bytes
MatchDomain md string
MatchGlob mg string
MatchPath mp string
Vars v map[string]string

Response payload:

Field msgpack key Type
Allow ok bool
Drop dr bool
Status s int
Body b string
Headers h map[string]string

on_response

Request payload — a pair of request info + upstream response:

ResponsePair {
  req:  RequestInfo         // same as on_request payload
  resp: UpstreamResponse {
    status:  int               // msgpack key: "s"
    headers: map[string]string // msgpack key: "h"
  }
}

Response payload:

Field msgpack key Type
Status s int (0 = no change)
Headers h map[string]string (add/override)
Remove rm []string (headers to remove)

on_connect

Request payload:

Field msgpack key Type
RouteID r string
Domain d string
RemoteAddr a string

Response payload:

Field msgpack key Type
Allow ok bool

Minimal Plugin Example (Bash)

#!/bin/bash
while IFS= read -r line; do
    method=$(echo "$line" | jq -r '.method')
    if [ "$method" = "configure" ]; then
        route_id=$(echo "$line" | jq -r '.params.route_id')
        echo '{"result":"ok"}'
        # Push initial targets
        echo "{\"method\":\"set_targets\",\"params\":{\"route_id\":\"$route_id\",\"targets\":[\"10.0.1.1:3505\"]}}"
    fi
done

Note

Bash plugins can only do push-based target discovery. Request-response hooks require a Unix socket server with msgpack framing — use the Go SDK or implement the socket protocol in your language of choice.