Skip to content

Latest commit

 

History

History
94 lines (73 loc) · 3.87 KB

File metadata and controls

94 lines (73 loc) · 3.87 KB

IRC over WebSocket

The optional WebSocket listener is a second transport into the same IRC adapter. It does not run a gateway process and it does not have separate users, channels, history, cursors, or permissions.

Configuration

IRC_WEBSOCKET_LISTEN enables the listener. The endpoint path is /irc and query parameters are rejected so credentials cannot be smuggled into commonly logged URLs. Without TLS it follows the same safe default as the IRC listener: loopback is allowed and a non-loopback address is rejected unless IRC_ALLOW_INSECURE_REMOTE=true is explicitly set. A remote plaintext override does not advertise SASL PLAIN.

Deployments can set the same address and origin allowlist in the versioned [listeners] and [websocket] TOML sections. See server configuration; the environment examples below remain supported as explicit overrides.

The same exact origin list authorizes the reference web client to perform authenticated avatar GET requests against a separate control-listener port. It does not enable cross-origin mutations or any other control API read. See account avatars.

When IRC_TLS_CERT and IRC_TLS_KEY are configured, both the ordinary IRC and WebSocket listeners use that identity. The WebSocket URL is then wss://, not ws://. The current process-level TLS setting cannot make one listener plaintext and the other encrypted.

Browsers always send an Origin header. List every web application allowed to connect in the comma-separated IRC_WEBSOCKET_ORIGINS value:

IRC_WEBSOCKET_ORIGINS=https://chat.example.net,http://localhost:5173

Entries are exact serialized HTTP(S) origins: scheme, host, and optional port, with no path, query, fragment, wildcard, or user information. An absent or empty allowlist denies every request carrying Origin. Native and terminal clients normally omit that header and remain allowed. Origin checks are an abuse and cross-site boundary, not authentication; private servers should still require accounts and TLS. A reverse proxy must preserve the browser's Origin header.

Wire behavior

The handshake must request at least one of the standard IRCv3 subprotocols:

  • text.ircv3.net sends and receives WebSocket text messages.
  • binary.ircv3.net sends and receives WebSocket binary messages containing UTF-8 IRC data.

Both are supported. If a client offers both, the server selects the first supported token in the client's preference order. A message of the wrong WebSocket type closes the connection with status 1003.

Every WebSocket message contains exactly one IRC protocol line and must not contain CR or LF. Conversely, every server IRC line is one WebSocket message without a line terminator. Embedded line endings close the connection with status 1002. The same bounded IRC line limit used by the stream listener is enforced during WebSocket assembly, including fragmented messages. Only UTF-8 is accepted because the server advertises UTF8ONLY.

The implementation follows the IRCv3 WebSocket transport specification.

Minimal browser connection

const irc = new WebSocket("wss://irc.example.net:8097/irc", [
  "text.ircv3.net",
  "binary.ircv3.net",
]);

irc.addEventListener("open", () => {
  irc.send("CAP LS 302");
  irc.send("NICK browser");
  irc.send("USER browser 0 * :Browser client");
  irc.send("CAP END");
});

irc.addEventListener("message", ({ data }) => {
  console.log(data);
});

This is deliberately only transport setup. A real client should negotiate capabilities, use SASL on WSS for durable identity, retain its device credential securely, page history by message ID, and advance read and delivery markers only in response to the appropriate user/client state.

The repository's reference web client implements those behaviors without a gateway and documents its browser-storage trust boundary.