3 releases (breaking)
| new 0.3.0 | Jun 5, 2026 |
|---|---|
| 0.2.0 | May 27, 2026 |
| 0.1.0 | May 17, 2026 |
#1866 in Network programming
175KB
3K
SLoC
garter
Plugin-chain runner for SIP003 / SIP003u shadowsocks plugins.
garter lets you compose multiple SIP003 plugins into a chain and run them
as one. It handles the SIP003 environment variables (SS_REMOTE_HOST,
SS_REMOTE_PORT, SS_LOCAL_HOST, SS_LOCAL_PORT, SS_PLUGIN_OPTIONS),
per-link port allocation, lifecycle (graceful shutdown propagation), and
byte-level diagnostics.
What's in the box
ChainPlugin— the core trait. Anything implementing it can be a link in the chain. The crate shipsBinaryPlugin(wraps an external SIP003 plugin executable) andTapPlugin(instruments byte counts + TTFB on the wire).ChainRunner— composes a sequence ofChainPlugins into a single end-to-end transport.CountingStream/StreamCounters— wire-level instrumentation for anytokio::io::AsyncRead + AsyncWritestream.parse_plugin_options— parses the SIP003;-separated options string into a typedHashMap.Mode— selects chain direction (Client/Server); see below.
Modes
ChainRunner supports two SIP003 chain directions selected via
.mode(...):
Mode::Client(default) — data flows from the SS client's listener (SS_LOCAL_*) through the chain to the SS server's public endpoint (SS_REMOTE_*).Mode::Server— data flows from the public-facing endpoint (SS_REMOTE_*) through the chain back to a localssserver(SS_LOCAL_*). The plugin add-order stays the same in both modes (data-source-side first); in Server mode garter inverts the address wiring — including which plugin position faces the public endpoint. Per-plugin readiness is declared by each link (see the sitrep protocol below) and aggregated by the runner.
Use Mode::from_plugin_options(env.plugin_options.as_deref()) to derive
the mode automatically from the SIP003 server keyword in
SS_PLUGIN_OPTIONS.
sitrep protocol
A SIP003 plugin reports its startup to the host that spawned it — readiness, the actual bound listen address, the transports it serves, and any typed start failure — over sitrep, a one-way, line-delimited JSON control stream on the plugin's stdout. It replaces connect-probing the plugin's port and scraping its logs.
garter is the reference implementation of sitrep, but the protocol is
independently adoptable: any SIP003 / SIP003u plugin or host may
implement it without depending on garter.
See SITREP.md for the full normative specification.
Example
use garter::{BinaryPlugin, ChainRunner, Mode, PluginEnv};
#[tokio::main]
async fn main() -> garter::Result<()> {
let env = PluginEnv::from_env()?;
// Detect SIP003 chain mode from SS_PLUGIN_OPTIONS (`server` keyword
// = Server; default = Client). Same parse used by v2ray-plugin and
// other SIP003 plugins.
let mode = Mode::from_plugin_options(env.plugin_options.as_deref());
let chain = ChainRunner::new()
.mode(mode)
.add(Box::new(BinaryPlugin::new("v2ray-plugin", Some("host=example.com;tls"))))
.add(Box::new(BinaryPlugin::new("obfs-local", Some("obfs=tls"))));
chain.run(env).await
}
See the SIP003 spec for the broader context on shadowsocks
plugins, and the hole repository
for a worked example (the galoshes plugin embeds garter to compose a
YAMUX-multiplexed v2ray-plugin transport).
License
Licensed under Apache-2.0.
Dependencies
~7–40MB
~585K SLoC