The websocket library provides WebSocket client connections and a local WebSocket server, backed by IXWebSocket.
Sockets run on background threads and deliver all events to Lua on the main thread.
See Getting started for the shared runtime threading rule.
The client supports ws:// and wss:// (TLS via mbedTLS). The server is plain ws:// only.
type WebSocketConnectOptions = {
headers: { [string]: string }?,
protocols: { string }?,
pingIntervalSecs: number?,
handshakeTimeoutSecs: number?,
autoReconnect: boolean?, -- Defaults to false.
certVerification: boolean?, -- Defaults to true.
caBundle: string?, -- PEM bundle content held in memory.
}
type WebSocketServeOptions = {
host: string?, -- Defaults to "127.0.0.1".
}
type WebSocketReadyState = "connecting" | "open" | "closing" | "closed"Userdata types:
WebSocketConnectionWebSocketServerWebSocketPeer
websocket.connect(url: string, options: WebSocketConnectOptions?) -> (WebSocketConnection?, string?)
websocket.serve(port: number, options: WebSocketServeOptions?) -> (WebSocketServer?, string?)connect starts the socket immediately and returns the connection.
Events arrive on the next frame at the earliest, so attaching callbacks right after connect never misses events.
serve binds and starts listening, or returns nil and an error message. See globals Error shapes.
ws:send(data: string) -> (boolean?, string?)
ws:sendBinary(data: string) -> (boolean?, string?)
ws:ping(payload: string?) -> (boolean?, string?)
ws:close(code: number?, reason: string?) -> ()
ws:readyState() -> WebSocketReadyState
ws:url() -> string
ws:onOpen(callback: () -> ()) -> WebSocketConnection
ws:onMessage(callback: (data: string, isBinary: boolean) -> ()) -> WebSocketConnection
ws:onClose(callback: (code: number, reason: string, remote: boolean) -> ()) -> WebSocketConnection
ws:onError(callback: (message: string) -> ()) -> WebSocketConnectionCallback setters return the connection for chaining.
Automatic reconnection is disabled unless autoReconnect = true is passed.
Dropping the last reference to a connection closes it on garbage collection.
local ws = websocket.connect("wss://echo.websocket.org")
ws:onOpen(function()
ws:send("hello")
end):onMessage(function(data, isBinary)
if isBinary then
print("binary payload:", #data)
else
print("received:", data)
end
end):onClose(function(code, reason, remote)
print("closed:", code, reason)
end):onError(function(message)
print("error:", message)
end)
-- Options example.
local auth = websocket.connect("wss://api.example.com/socket", {
headers = { Authorization = "Bearer " .. token },
pingIntervalSecs = 15,
})server:broadcast(data: string) -> (boolean?, string?)
server:broadcastBinary(data: string) -> (boolean?, string?)
server:clients() -> { WebSocketPeer }
server:port() -> number
server:stop() -> ()
server:onClientConnect(callback: (peer: WebSocketPeer, headers: { [string]: string }) -> ()) -> WebSocketServer
server:onMessage(callback: (peer: WebSocketPeer, data: string, isBinary: boolean) -> ()) -> WebSocketServer
server:onClientDisconnect(callback: (peer: WebSocketPeer, code: number, reason: string) -> ()) -> WebSocketServer
server:onError(callback: (message: string) -> ()) -> WebSocketServerPeer-to-peer setups run serve on one peer and connect on the other:
-- Host peer.
local server = websocket.serve(7777, { host = "0.0.0.0" })
server:onClientConnect(function(peer)
peer:send("welcome")
end):onMessage(function(peer, data)
server:broadcast(data)
end)
-- Joining peer.
local ws = websocket.connect("ws://192.168.1.5:7777")peer:send(data: string) -> (boolean?, string?)
peer:sendBinary(data: string) -> (boolean?, string?)
peer:close(code: number?, reason: string?) -> ()
peer:remoteAddress() -> string? -- "ip:port"
peer:id() -> string? -- Stable per connection. Use as a table key.Peer userdata handed to different callbacks may be distinct Lua values for the same connection.
Use peer:id() to key tables.
send and sendBinary on a disconnected peer return nil and "websocket peer is disconnected".
close on a disconnected peer does nothing.
remoteAddress and id keep returning their last values.
See Limits and errors for connection caps and error strings.
WebSocket traffic is not sandboxed and has no host allowlist. See web Security for the shared egress and TLS rules.
The local server binds to loopback (127.0.0.1) by default.
Passing host = "0.0.0.0" listens on all interfaces, so anything on the LAN can connect.
Only do this intentionally.
TLS on the client:
- Setting
certVerification = falsedisables verification. This is unsafe for production (see web Security). - On platforms without a usable system certificate store for mbedTLS, pass a PEM bundle via
caBundle. - The server side does not support TLS.
For HTTP request caps and TLS options on geode.utils.web, see web Security.
See LuauAPI mod guidelines for loadstring and network abuse rules.
Connections and servers are closed when garbage collected, when close/stop is called, or when the runtime shuts down.
Nothing closes them when an individual script run returns, so keep a reference for as long as the socket should live.
src/bindings/websocket/WebSocketBinding.cppsrc/bindings/websocket/WebSocketConnection.cppsrc/bindings/websocket/WebSocketServer.cppsrc/bindings/websocket/WebSocketInternal.hpptools/luau_codegen/extra_bindings/websocket.dluau