* docs: add local full-text search and a contributor README Add @easyops-cn/docusaurus-search-local to the Docusaurus site so the docs get an offline search bar (index built at build time, no external service). English and zh-CN pages are both indexed, and matched terms are highlighted on the target page. Add docs/README.md describing the Docusaurus setup, local dev/build commands, search behavior, directory layout, and gotchas; exclude it from the published site alongside CLAUDE.md. Point the root README's docs/ entry at it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TSzcESzU9947zkGzQ6SMmE * docs: remove dead framework leftovers, fix index generation, complete the nav Delete the VitePress (.vitepress/) and Jekyll (_layouts/, css/) leftovers, the one-shot migration scripts (fix_md_header.py, fix_seealso.py), and the stale keywords.json snapshot; prune the matching .gitignore entries and docusaurus exclude patterns. Rewrite gen_index.py for Docusaurus: it emitted dead .html links and legacy 'layout: doc' frontmatter, choked on non-markdown entries, and dropped nested categories — regenerating an index would have broken it. It now emits the extension-less links the site actually uses, links nested category indexes (restoring apply/* on the zh-CN index), and refuses to run on the docs root. Fix update_index.sh's copy-paste titles (zh-CN efun/build were titled 'APPLY'), stop it clobbering the hand-written lpc/index.md, and cover cli/. Regenerated indexes pick up the missing driver/ffi-plan entry. add_missing_efuns.py now takes the keywords.json path as an argument instead of requiring a stale copy. Move CNAME and the Google site-verification file into static/ so they actually reach the published build output. Complete the sidebar: link the CLI category to cli/index and add the missing portbind/symbol/generate_keywords pages, and expose the previously orphaned stdlib section under Reference. Promote onBrokenLinks to 'throw' now the build is warning-free, and drop the empty Demo section from the landing page. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TSzcESzU9947zkGzQ6SMmE * docs: strip legacy 'layout: doc' frontmatter from all pages Mechanical sweep removing the Jekyll-era 'layout: doc' line from every doc page's frontmatter (Docusaurus ignores it), and the matching line from the templates in docs/CLAUDE.md so new pages don't reintroduce it. No content changes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TSzcESzU9947zkGzQ6SMmE --------- Co-authored-by: Claude <noreply@anthropic.com>
9.5 KiB
| title |
|---|
| WebSocket Support |
WebSocket Support
FluffOS supports WebSocket connections, letting modern web browsers connect directly to your mud without a separate telnet client. Players can reach the game from a web page, and the driver can serve the client itself.
Overview
FluffOS WebSocket support includes:
- Multiple subprotocols —
telnet,ascii, andbinary - Secure WebSocket (WSS) — TLS-encrypted connections
- Built-in web client — shipped in
src/www - Full telnet protocol — option negotiation, GMCP, MSP, MXP, and more over the
telnetsubprotocol - Static file serving — serve the client's HTML/JS/CSS from the driver
- Compression —
permessage-deflatewhen the client supports it
WebSocket Server Configuration
WebSocket listeners are configured like any other port, with the websocket
protocol keyword. A WebSocket port additionally requires the
websocket http dir option, which names the directory of static files to serve.
See the driver configuration for the full port
syntax.
Basic WebSocket Port
# Non-secure WebSocket
external_port_1 : websocket 8080
# Directory of static files (the web client)
websocket http dir : www
Secure WebSocket (WSS)
For production, enable TLS on the port with a matching external_port_N_tls
line. The cert=/key= pair must be on a single line (the config parser reads
each line whole and does not support backslash continuation).
external_port_1 : websocket 8443
external_port_1_tls : cert=etc/cert.pem key=etc/key.pem
websocket http dir : www
Multiple WebSocket Ports
# Regular telnet
port number : 4000
# Non-secure WebSocket (development)
external_port_1 : websocket 8080
# Secure WebSocket (production)
external_port_2 : websocket 8443
external_port_2_tls : cert=etc/cert.pem key=etc/key.pem
websocket http dir : www
Static Files Directory
websocket http dir is passed to the web server as-is, so a relative path is
resolved from the driver's working directory (use an absolute path if that
is ambiguous). It should contain an index.html plus any CSS, JavaScript, and
images. FluffOS ships a full-featured client in src/www.
# Relative to the driver's working directory
websocket http dir : www
# Absolute path
websocket http dir : /var/www/mud
WebSocket Subprotocols
A browser selects a subprotocol as the second argument to new WebSocket().
FluffOS offers three:
telnet
The full telnet protocol over WebSocket — option negotiation plus the mud protocols listed below (GMCP, MSP, MXP, etc.). This is the subprotocol the built-in client uses.
const ws = new WebSocket('ws://mud.example.com:8080', ['telnet']);
ascii
Plain text with no telnet framing — lightweight, useful for simple clients and debugging.
const ws = new WebSocket('ws://mud.example.com:8080', ['ascii']);
binary
Handled identically to the telnet subprotocol; retained for compatibility with
older clients.
const ws = new WebSocket('ws://mud.example.com:8080', ['binary']);
A WebSocket port also serves the static files from websocket http dir over
plain HTTP, so browsing to http://host:port/ returns index.html (this is
ordinary file serving, not a WebSocket connection).
Built-in Web Client
FluffOS ships a modern web client in src/www (index.html). It provides:
- Connection management — configurable address/port,
ws://orwss://, and subprotocol selection - Full telnet handling — IAC commands, subnegotiation, option negotiation, GMCP, MSP, NAWS, terminal type
- A terminal UI — ANSI color, command history, auto-scroll, and a responsive, mobile-friendly layout
Deploying the Web Client
Make the client available under your websocket http dir:
# Copy the client into place
cp -r src/www /path/to/mudlib/www
# Or symlink it
ln -s /path/to/fluffos/src/www /path/to/mudlib/www
websocket http dir : www
external_port_1 : websocket 8080
Then browse to http://localhost:8080/.
Customizing the Web Client
The client is a single index.html. Two useful entry points:
setDefaultConfig()sets the default address, port, and subprotocol.processGMCPData(data)handles incoming GMCP messages — extend it to drive status bars, maps, or custom panels.
setDefaultConfig() {
this.protocolSelect.value = 'ws://';
this.addressInput.value = 'mud.yourdomain.com';
this.portInput.value = '8080';
this.subprotocolSelect.value = 'telnet';
}
GMCP Over WebSocket
Over the telnet subprotocol the client negotiates GMCP (Generic Mud
Communication Protocol), so the standard GMCP efuns work unchanged.
Server-Side GMCP
send_gmcp() sends to the current interactive object, so call it from within
the user's object (it takes a single string containing the package name followed
by JSON). has_gmcp() reports whether a given interactive object negotiated
GMCP (defaulting to the current object).
// Called in the context of the user object
if (has_gmcp()) {
send_gmcp("Char.Vitals { \"hp\": 100, \"mp\": 50, \"maxhp\": 100, \"maxmp\": 50 }");
send_gmcp("Room.Info { \"name\": \"Market Square\", \"area\": \"City\" }");
}
Client-Side GMCP
The web client parses GMCP messages and dispatches them through
processGMCPData():
processGMCPData(data) {
switch (data.module) {
case 'Char.Vitals':
updateStatusBars(data.data);
break;
case 'Room.Info':
updateRoomDisplay(data.data);
break;
}
}
Telnet Protocol Over WebSocket
The telnet subprotocol supports the standard telnet options and the common mud
protocols. Negotiation is handled by the driver's telnet layer, identically to a
raw telnet connection.
Supported telnet options
- ECHO (1) — echo control
- SUPPRESS_GO_AHEAD (3)
- TERMINAL_TYPE (24)
- NAWS (31) — window size
- NEW_ENVIRON (39) — environment variables
- CHARSET (42) — character set negotiation
Supported mud protocols
- MSDP (69) — Mud Server Data Protocol
- MSSP (70) — Mud Server Status Protocol
- MCCP2 (86) — Mud Client Compression Protocol
- MSP (90) — Mud Sound Protocol
- MXP (91) — Mud eXtension Protocol
- ZMP (93) — Zenith Mud Protocol
- GMCP (201) — Generic Mud Communication Protocol
The built-in client performs all negotiation automatically.
Security Considerations
Use WSS in Production
Always serve production traffic over WSS (see TLS Support):
external_port_1 : websocket 8443
external_port_1_tls : cert=/etc/letsencrypt/live/mud.example.com/fullchain.pem key=/etc/letsencrypt/live/mud.example.com/privkey.pem
Validating Player Input
Input from a WebSocket client arrives through the same path as any interactive
connection — the normal command and input handling on the user object — not
through receive_message (which is the apply for output sent to the user).
Validate and rate-limit player input where you process commands, exactly as you
would for a telnet connection; there is nothing WebSocket-specific to do.
Performance
Compression
FluffOS advertises permessage-deflate and uses it with any client that
supports it, reducing bandwidth for large or repetitive output.
Troubleshooting
Connection fails: confirm the port is open through any firewall.
sudo ufw allow 8080/tcp
sudo ufw allow 8443/tcp
Static files 404: verify websocket http dir points at the directory that
contains index.html.
websocket http dir : www
WSS certificate errors: use a certificate the client trusts (e.g. Let's Encrypt); see TLS Support.
GMCP not working: confirm the client negotiated it.
if (!has_gmcp(user)) {
write("Your client does not support GMCP.\n");
}
Enable WebSocket debug logging: the driver has a websocket debug category.
set_debug_level("websocket");
Testing WebSocket Connections
From a browser console:
const ws = new WebSocket('ws://localhost:8080', ['telnet']);
ws.onopen = () => console.log('Connected!');
ws.onmessage = (e) => console.log('Received:', e.data);
ws.send('look\n');
From the command line with wscat:
npm install -g wscat
wscat -c ws://localhost:8080 -s telnet
Reverse Proxying
You can place a WebSocket port behind a web server, for example to serve the mud client alongside a website or to terminate TLS at the proxy:
server {
listen 80;
server_name mud.example.com;
location / {
proxy_pass http://webapp:3000;
}
location /ws {
proxy_pass http://fluffos:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
}
See Also
- TLS Support — secure WebSocket configuration
- Driver Configuration — WebSocket port setup
- send_gmcp — send a GMCP message
- has_gmcp — check GMCP support
References
- RFC 6455 — the WebSocket protocol
- libwebsockets — the WebSocket library FluffOS uses
- GMCP Specification
- Telnet Protocol (RFC 854)
- Telnet Options (IANA)