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.
- Wisp v1 and v2 (
enableV2) over WebSocket - Optional Twisp (
enableTwisp) - TCP and UDP stream support (
allowTCP,allowUDP) - Hostname/port allow and block lists
- Direct IP, private IP, and loopback egress controls
- Flood protection: per-source, per-destination, SYN flood detection
- IP reputation with on-disk persistence and decay
- Bandwidth and connection rate limits per IP
- Optional password authentication
- Optional upstream HTTP/SOCKS proxy
- Reverse-proxy real-IP parsing (
CF-Connecting-IP,X-Forwarded-For, ...) - Optional static file serving alongside the
/wispendpoint
Requires Go 1.25+.
go build -o mrrowisp main.goOr build cross-platform binaries into ./bin/:
./build.shdocker build -t mrrowisp .
docker run -p 6001:6001 -v $(pwd)/config.json:/app/config.json mrrowisppnpm install mrrowisp./mrrowisp --config config.jsonFlags:
--config <path|json>– path to a config file or an inline JSON string--port <n>– override the port from config--allow-loopback– overrideallowLoopbackIPs
If 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 fromdist/config.jsonstart(count = 1)– spawn N worker processes, each on its own portroute(req, socket, head)– proxy a WebSocket upgrade to the next workerstop()–SIGTERMall workerskill()–SIGKILLall workers
The 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.