A Wisp server written in Go, with a Node.js wrapper for spawning and load-balancing across multiple worker processes.
mrrowisp supports Wisp v1, v2, and optionally Twisp (PTY over Wisp). It includes flood protection, per-source/per-destination rate limiting, IP scoring, an egress allow/deny policy, a DNS cache, and reverse-proxy IP parsing.
enableV2) over WebSocketenableTwisp)allowTCP, allowUDP)CF-Connecting-IP, X-Forwarded-For, ...)/wisp endpointRequires Go 1.25+.
go build -o mrrowisp main.go
Or build cross-platform binaries into ./bin/:
./build.sh
docker build -t mrrowisp .
docker run -p 6001:6001 -v $(pwd)/config.json:/app/config.json mrrowisp
pnpm install mrrowisp
./mrrowisp --config config.json
Flags:
--config <path|json> – path to a config file or an inline JSON string--port <n> – override the port from config--allow-loopback – override allowLoopbackIPsIf no --config is supplied, the built-in defaults from
wisp.DefaultConfig() are used.
See example.config.json for the full list of supported options.
The wrapper spawns one or more mrrowisp Go processes and the built-in load
balancing routes incoming WebSocket upgrades across them.
import { Mrrowisp } from "mrrowisp";
import { createServer } from "node:http";
const wisp = new Mrrowisp({ port: 6001, logLevel: "info" });
await wisp.start(4); // spawn 4 workers, which would route to 6001, 6002, 6003, and 6004 or similar automatically
const server = createServer();
server.on("upgrade", (req, socket, head) => wisp.route(req, socket, head));
server.listen(8080);
API:
new Mrrowisp(partialConfig?) – overrides merged onto defaults loaded from
dist/config.jsonstart(count = 1) – spawn N worker processes, each on its own portroute(req, socket, head) – proxy a WebSocket upgrade to the next workerstop() – SIGTERM all workerskill() – SIGKILL all workersThe full schema lives in example.config.json and wisp/config.go. Featured
keys:
| Key | Default | Description |
|---|---|---|
port | 6001 | TCP port to listen on |
allowTCP / allowUDP | true | Permit TCP/UDP stream types |
allowDirectIP | false | Allow connecting to literal IPs |
allowPrivateIPs | false | Allow RFC1918 destinations |
allowLoopbackIPs | false | Allow loopback destinations |
enableV2 | true | Enable Wisp v2 extensions |
enableTwisp | false | Enable Twisp |
passwordAuth(Required) | false | Password auth |
parseRealIP | true | Honor trustedHeaders from trustedProxies |
bandwidthLimitKbps | 0 | Per-IP bandwidth limit (0 = off) |
floodProtection | enabled | See example.config.json |
reputation | enabled | Persistent IP reputation scoring |
BSD-3-Clause. See LICENSE.
26 commits
1 commits
Go
87.0%
TypeScript
11.4%
Shell
1.1%
A Wisp server written in Go, with a Node.js wrapper for spawning and load-balancing across multiple worker processes.
mrrowisp supports Wisp v1, v2, and optionally Twisp (PTY over Wisp). It includes flood protection, per-source/per-destination rate limiting, IP scoring, an egress allow/deny policy, a DNS cache, and reverse-proxy IP parsing.
enableV2) over WebSocketenableTwisp)allowTCP, allowUDP)CF-Connecting-IP, X-Forwarded-For, ...)/wisp endpointRequires Go 1.25+.
go build -o mrrowisp main.go
Or build cross-platform binaries into ./bin/:
./build.sh
docker build -t mrrowisp .
docker run -p 6001:6001 -v $(pwd)/config.json:/app/config.json mrrowisp
pnpm install mrrowisp
./mrrowisp --config config.json
Flags:
--config <path|json> – path to a config file or an inline JSON string--port <n> – override the port from config--allow-loopback – override allowLoopbackIPsIf no --config is supplied, the built-in defaults from
wisp.DefaultConfig() are used.
See example.config.json for the full list of supported options.
The wrapper spawns one or more mrrowisp Go processes and the built-in load
balancing routes incoming WebSocket upgrades across them.
import { Mrrowisp } from "mrrowisp";
import { createServer } from "node:http";
const wisp = new Mrrowisp({ port: 6001, logLevel: "info" });
await wisp.start(4); // spawn 4 workers, which would route to 6001, 6002, 6003, and 6004 or similar automatically
const server = createServer();
server.on("upgrade", (req, socket, head) => wisp.route(req, socket, head));
server.listen(8080);
API:
new Mrrowisp(partialConfig?) – overrides merged onto defaults loaded from
dist/config.jsonstart(count = 1) – spawn N worker processes, each on its own portroute(req, socket, head) – proxy a WebSocket upgrade to the next workerstop() – SIGTERM all workerskill() – SIGKILL all workersThe full schema lives in example.config.json and wisp/config.go. Featured
keys:
| Key | Default | Description |
|---|---|---|
port | 6001 | TCP port to listen on |
allowTCP / allowUDP | true | Permit TCP/UDP stream types |
allowDirectIP | false | Allow connecting to literal IPs |
allowPrivateIPs | false | Allow RFC1918 destinations |
allowLoopbackIPs | false | Allow loopback destinations |
enableV2 | true | Enable Wisp v2 extensions |
enableTwisp | false | Enable Twisp |
passwordAuth(Required) | false | Password auth |
parseRealIP | true | Honor trustedHeaders from trustedProxies |
bandwidthLimitKbps | 0 | Per-IP bandwidth limit (0 = off) |
floodProtection | enabled | See example.config.json |
reputation | enabled | Persistent IP reputation scoring |
BSD-3-Clause. See LICENSE.
26 commits
1 commits
Go
87.0%
TypeScript
11.4%
Shell
1.1%