it has the zoomies
so quick story abt how this was made, this was the initial project, then amplify made me write a whole new wisp library, then i scrapped that and wrote this in rust. this has still been faster than all of them. every single time.
Warning
Twisp, and by extension mrrowisp, does NOT work on windows! linux and macos supported tho
- Wisp v1 and v2 protocol support
- TCP and UDP stream multiplexing over WebSocket
- Twisp (terminal over wisp) support for remote shell access
- Password and Ed25519 certificate authentication (v2)
- Hostname blacklist/whitelist filtering
- SOCKS5 proxy support for upstream connections
- Custom DNS server with caching
- WebSocket permessage-deflate compression
- Configurable TCP buffer sizes and flow control
go build -o mrrowispbun add mrrowisp
# or
npm install mrrowispimport { createMrrowisp } from "mrrowisp";
// Basic start and stop
const server = await createMrrowisp()
.port(6001)
.v2(true)
.start();
// later
await server.stop();const server = await createMrrowisp()
.port(6001)
.v2(true)
.udp(true)
.twisp(true)
.motd("mrrow merp purr :3")
.blacklist(["truthsocial.com"]) // idk i'd block this
.dns(["8.8.8.8","1.1.1.1"])
.start();const server = await createMrrowisp()
.port(6001)
.onReady(() => {
console.log("Server is ready!");
})
.onError((err) => {
console.error("Server error:", err);
})
.onExit((code, signal) => {
console.log(`Server exited (code: ${code}, signal: ${signal})`);
})
.onStdout((data) => {
console.log(`[mrrowisp] ${data}`);
})
.onStderr((data) => {
console.error(`[mrrowisp] ${data}`);
})
.start();
server.on("error", (err) => console.error(err));
server.on("exit", (code) => console.log(`Exit: ${code}`));// Load from a config file
const server = await createMrrowisp()
.fromFile("./config.json")
.start();
// Merge multiple sources
const server = await createMrrowisp()
.fromFile("./config.json")
.withConfig({ port: 8080 }) // Override specific values
.start();
// Or from a JSON config
const server = await createMrrowisp()
.fromJSON('{"port": 6001, "enableV2": true}')
.start();const server = await createMrrowisp().port(6001).start();
// Check if server is running
console.log(server.running);
// Access the config
console.log(server.config);
// Access the child process
console.log(server.process.pid);
// Graceful shutdown
await server.stop();
// Force kill
server.kill();
server.kill("SIGTERM");| Method | Description |
|---|---|
fromFile(path) |
Load config from a JSON file |
fromJSON(json) |
Load config from a JSON string |
withConfig(config) |
Merge a partial config object |
port(port) |
Set the server port |
udp(enabled) |
Enable/disable UDP support |
v2(enabled) |
Enable/disable Wisp v2 protocol |
twisp(enabled) |
Enable/disable terminal over wisp |
motd(message) |
Set message of the day |
blacklist(hostnames) |
Set blocked hostnames |
whitelist(hostnames) |
Set whitelisted hostnames |
proxy(url) |
Set SOCKS5 proxy address |
dns(server) |
Set custom DNS server |
onReady(cb) |
Callback when server starts |
onError(cb) |
Callback on errors |
onExit(cb) |
Callback when server exits |
onStdout(cb) |
Callback for stdout data |
onStderr(cb) |
Callback for stderr data |
getConfig() |
Get the current config object |
start() |
Start the server (returns Promise) |
| Method | Description |
|---|---|
stop() |
Graceful shutdown (returns Promise) |
kill(signal?) |
Force kill with optional signal |
on(event, cb) |
Add event listener |
off(event, cb) |
Remove event listener |
running |
Whether the server is running |
config |
The resolved configuration |
process |
The underlying ChildProcess |
./mrrowispCopy example.config.json to config.json and edit as needed:
{
"port": "6001",
"disableUDP": false,
"tcpBufferSize": 65535,
"bufferRemainingLength": 1024,
"tcpNoDelay": true,
"websocketTcpNoDelay": true,
"blacklist": {
"hostnames": []
},
"whitelist": {
"hostnames": []
},
"proxy": "",
"websocketPermessageDeflate": false,
"dnsServer": "",
"enableTwisp": false,
"enableV2": true,
"motd": "",
"passwordAuth": false,
"passwordAuthRequired": false,
"passwordUsers": {},
"certAuth": false,
"certAuthRequired": false,
"certAuthPublicKeys": [],
"enableStreamConfirm": false
}| Option | Type | Description |
|---|---|---|
port |
string | Port to listen on |
disableUDP |
bool | Disable UDP stream support |
tcpBufferSize |
int | TCP read buffer size |
bufferRemainingLength |
uint32 | Flow control buffer threshold |
tcpNoDelay |
bool | Enable TCP_NODELAY on outbound connections |
websocketTcpNoDelay |
bool | Enable TCP_NODELAY on WebSocket connections |
blacklist.hostnames |
[]string | Hostnames to block |
whitelist.hostnames |
[]string | Hostnames to bypass DNS resolution |
proxy |
string | SOCKS5 proxy address (e.g., 127.0.0.1:1080) |
websocketPermessageDeflate |
bool | Enable WebSocket compression |
dnsServer |
string | Custom DNS server (e.g., 8.8.8.8:53) |
enableTwisp |
bool | Enable terminal streams (Unix only) |
enableV2 |
bool | Enable Wisp v2 protocol |
motd |
string | Message of the day sent to v2 clients |
passwordAuth |
bool | Enable password authentication |
passwordAuthRequired |
bool | Require password authentication |
passwordUsers |
object | Username/password map |
certAuth |
bool | Enable Ed25519 certificate authentication |
certAuthRequired |
bool | Require certificate authentication |
certAuthPublicKeys |
[]string | Allowed Ed25519 public keys (hex-encoded) |
enableStreamConfirm |
bool | Send confirmation when streams connect |
- soap phia - writing most of this
- rebecca - greatly helping with implementing wisp v2 and extensions
- ObjectAscended - writing go-wisp, which this was initially based off of
