relay

module
v1.5.1-0...-475df33 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 10, 2026 License: MIT

README

Relay

Modern web IRC client designed for self-hosting

OUCHnet's official hard fork of The Lounge

WebsiteDocker

Features

  • Modern features brought to IRC. Push notifications, link previews, new message markers, and more bring IRC to the 21st century.
  • Always connected. Remains connected to IRC servers while you are offline.
  • Cross platform. The server is a single statically-linked Go binary; it just works wherever it's copied.
  • Responsive interface. The client works smoothly on every desktop, smartphone and tablet.
  • Synchronized experience. Always resume where you left off no matter what device.
  • IRCv3 typing notifications. See who is typing in real time via the IRCv3 +typing client tag. A space is reserved in the UI so the layout does not shift when the indicator appears. The notification can be toggled per-user from the Notifications settings tab.
  • IRCv3 message IDs. Relay preserves server-supplied msgid tags from the message-tags capability for message replies, reactions, history, and searches. When a server or bouncer does not provide an ID for one of your own messages, Relay assigns a local-only fallback ID so replies and reactions still work in Relay.
  • IRCv3 labeled responses. When an IRC server advertises both labeled-response and batch, Relay adds unique label tags to its upstream commands so compatible servers and bouncers can correlate replies, errors, acknowledgements, and self-echoes reliably. Networks without these capabilities continue using Relay's existing untagged behavior; there is no user-facing setting to configure.
  • IRC command aliases. Use /alias <name> <command> to define personal command shortcuts. Aliases are managed through a dedicated Settings tab and appear in /command autocomplete.
  • Theme import/export. Export your current CSS theme to a file and import custom themes from external CSS files. Imported themes become first-class selectable themes with full delete support, accessible from a dedicated Themes settings tab.
  • Upload management. A new Uploads settings tab lists all files you have uploaded, with the ability to delete individual files. Only available on private authenticated instances.
  • Upload expiration setting. Configure per-user file upload expiration time directly from the Settings UI. Expiry is enforced at serve time and a background cleaner runs every minute to remove expired files.
  • Channel list refresh. A refresh button on the /list channel list lets you re-fetch the server's channel list on demand.
  • Public mode hardening. When running in public mode, lockNetwork is forced on and link prefetching is disabled to prevent information leakage and unauthorized network changes. Theme upload and delete are also blocked.
  • WebAuthn (FIDO2) passwordless login. Register hardware security keys (YubiKey, passkeys, Touch ID, etc.) from the Security Keys settings tab. Sign in with a single button click and a key touch — no username or password required. Multiple keys per account are supported. Only available on private authenticated instances.
  • Optional OIDC sign-in. Private-mode instances can optionally expose a "Sign in with single sign-on" button backed by any standards-compliant OIDC provider, such as Authentik, Authelia, or Keycloak. This is additive only: local Relay username/password login remains available as the break-glass fallback, users must already exist in Relay, and the safest provider setup is usually a dedicated mapped claim such as relay_username.
  • Admin settings panel. A web-based admin panel lets instance owners configure server-wide settings and restart the server without touching config files. Accessible to admin accounts from a dedicated Admin tab in Settings.
  • Account registration. Private-mode instances can optionally allow self-registration via a /register sign-up page. When SMTP is enabled, new users must verify their email with a one-time code before their account is created (code expires in 15 minutes, 5 attempts allowed). Without SMTP, accounts are created immediately on submit. Both allowRegistration and SMTP settings are configured from the admin settings panel. Registration is disabled by default.
  • Jitsi voice/video calling. Integrates Jitsi Meet for in-channel voice and video calls. A call button appears in channel toolbars; calls open in a resizable, fullscreen-capable panel inside the client. The Jitsi server URL is configured from the admin settings panel. Mobile-responsive layout is included.
  • KLIPY GIF picker. Search and send GIFs directly from the chat input via the KLIPY API. The API key is stored server-side and requests are proxied through the server, so it's never exposed to the browser. Enabled and configured from the admin settings panel.
  • First-run web setup. On a fresh install with no user accounts, the server prints a one-time setup token to its console/log output and serves a /setup page instead of sign-in, so you can create the initial admin account from the browser without CLI access. The setup page and token are permanently disabled the instant the account is created — it can never be reopened. The relay add CLI command still works as before if you prefer it.
  • npm package manager. The client build uses npm instead of yarn. All install and script commands use npm.
  • Go backend. The server is a single Go binary (relay), not a Node.js process — no runtime dependencies, no node_modules in production. Only building the client (client/, a Vue 3 SPA) still uses Node/npm; running the server does not.
  • PostgreSQL message storage. Message history can use PostgreSQL instead of SQLite. Select PostgreSQL from the Admin settings panel or set messageStorage: [postgres] in config.yaml; the connection string and pool settings are available in both places. PostgreSQL full-text search uses its indexed tsvector equivalent to SQLite FTS5. Existing SQLite history can be copied explicitly with relay storage import-sqlite [username] after PostgreSQL is configured. The source SQLite files are never removed.

Known limitations

  • ZNC and message replies/reactions. ZNC never negotiates echo-message (or anything like it) with the upstream IRC server — its server-facing capability list omits it entirely, regardless of what the network supports. Both echo-message and the legacy znc.in/self-message cap it offers to clients are implemented as a purely local echo: ZNC replays the message you just sent back to your attached clients itself, with no round trip to the server and no server-assigned message ID ever generated. Relay assigns a local-only ID so replies and reactions work inside Relay, but ZNC and other IRC clients cannot resolve that synthetic ID.
  • soju and message replies/reactions. soju negotiates real upstream echo-message only when the IRC network it is bouncing to also advertises labeled-response. Without that capability, soju fabricates the echo locally and does not provide a server-assigned message ID. Relay can still support replies and reactions locally with a synthetic ID, but other IRC clients cannot resolve it; enabling labeled-response on the IRC server restores genuine server-assigned IDs for cross-client compatibility.

The easiest way to run Relay is via the OUCHnet Docker image:

docker pull git.thc420.dev/ouch/relay:latest
docker run -d \
  --name relay \
  -p 9000:9000 \
  -v ~/.relay:/var/opt/relay \
  --restart always \
  git.thc420.dev/ouch/relay:latest

Existing data from ~/.thelounge//var/opt/thelounge (pre-rename installs) is picked up automatically if the new path is empty — no manual migration needed.

Or with Docker Compose (see docker-compose.yml):

docker compose up -d

Adding the first admin user

Option 1: Web setup (recommended). The first time Relay starts with no user accounts, it prints a one-time setup token to the container logs:

docker logs relay

Look for a line like:

No users exist yet. To create the first admin account, open the web UI and enter this one-time setup token: <token>

Open the web UI in your browser — you'll land on a setup page automatically. Enter a username, password, and the token from the logs to create the admin account. The setup page and token are permanently disabled the instant the account is created, so this only works once per install.

Option 2: Command line. If you'd rather not use the web setup page, create the initial admin account with relay add --admin:

Plain Docker:

docker exec -it relay relay add --admin <username>

Docker Compose:

docker compose exec relay relay add --admin <username>

Both commands prompt for a password. The new account will be available immediately — no restart needed. Using this method before ever opening the web UI means the setup page will never appear for that install.

Installation and usage

Building Relay from source requires Node.js LTS (or more recent, for the client build) and a recent Go toolchain (for the server). Running the built server does not require Node.js at all — the relay binary is self-contained, with one exception: relay migrate-config shells out to a small Node script to read a legacy Node-backend config.js, so a node binary needs to be on PATH if you're migrating an old install.

Running from source

The following commands build and run Relay from source:

git clone https://git.thc420.dev/ouch/relay.git
cd relay
npm install
npm run build:client
go build -o relay ./cmd/relay
./relay start

⚠️ While it is the most recent codebase, this is not production-ready! Run at your own risk. It is also not recommended to run this as root.

Development setup

Simply follow the instructions to run Relay from source above, on your own fork.

Before submitting any change, make sure to:

  • Run npm test to execute linters and the vitest suite (covers the client and its tests)
    • Run npm run format:prettier if linting fails
  • Run go test ./cmd/... ./internal/... and go vet ./cmd/... ./internal/... if you change anything in cmd/ or internal/
  • Run npm run build:client if you change or add anything in client/js or client/components
    • The built files will be output to public/ by Vite

For local development, run two terminals: npm run watch to rebuild public/ on every client change, and go run ./cmd/relay start --dev to run the server against it (--dev relaxes the CSP so the freshly rebuilt client assets load correctly; there is no backend hot-module-reloading — restart that command after a Go change).

To ensure that you don't commit files that fail the linting, you can install a pre-commit git hook. Execute npm run githooks-install to do so.

Directories

Path Synopsis
cmd
relay command
Command relay is the Go port of the Relay (The Lounge fork) backend.
Command relay is the Go port of the Relay (The Lounge fork) backend.
internal
auth
Package auth implements local password auth, sessions, WebAuthn, and OIDC login.
Package auth implements local password auth, sessions, WebAuthn, and OIDC login.
changelog
Package changelog polls for new releases and notifies connected clients.
Package changelog polls for new releases and notifies connected clients.
clientcert
Package clientcert generates and manages per-network client TLS certificates for SASL EXTERNAL.
Package clientcert generates and manages per-network client TLS certificates for SASL EXTERNAL.
config
Package config loads and layers the server configuration (embedded defaults, config.yaml, admin-overrides.json, CLI overrides).
Package config loads and layers the server configuration (embedded defaults, config.yaml, admin-overrides.json, CLI overrides).
emailverifier
Package emailverifier implements email-verified self-registration.
Package emailverifier implements email-verified self-registration.
encryption
Package encryption implements Relay's versioned IRC message encryption format.
Package encryption implements Relay's versioned IRC message encryption format.
httpapi
Package httpapi is Stage 13's HTTP surface: the Go equivalent of the Express half of server/server.ts (everything that isn't the WebSocket route itself, which internal/session already owns).
Package httpapi is Stage 13's HTTP surface: the Go equivalent of the Express half of server/server.ts (everything that isn't the WebSocket route itself, which internal/session already owns).
identd
Package identd implements the RFC1413 identd server and oidentd config generation.
Package identd implements the RFC1413 identd server and oidentd config generation.
incommands
Package incommands ports server/plugins/inputs/*.ts: the client-initiated slash commands (/join, /msg, /nick, etc).
Package incommands ports server/plugins/inputs/*.ts: the client-initiated slash commands (/join, /msg, /nick, etc).
integration
Package integration provides end-to-end test harnesses that boot a real server and drive it over WebSocket.
Package integration provides end-to-end test harnesses that boot a real server and drive it over WebSocket.
ircbridge
Package ircbridge wraps the external irc.Connection with multi-subscriber fan-out for the bouncer's per-network event stream.
Package ircbridge wraps the external irc.Connection with multi-subscriber fan-out for the bouncer's per-network event stream.
irchandlers
Package irchandlers ports server/plugins/irc-events/*.ts: the reactive logic that turns events off an ircbridge.Bridge into Network/Chan/User state changes, constructed Msg values pushed through Chan.PushMessage, and any additional hub broadcasts a Node handler emits directly via `client.emit(...)`.
Package irchandlers ports server/plugins/irc-events/*.ts: the reactive logic that turns events off an ircbridge.Bridge into Network/Chan/User state changes, constructed Msg values pushed through Chan.PushMessage, and any additional hub broadcasts a Node handler emits directly via `client.emit(...)`.
model
Package model defines the Network, Chan, Msg, User, and Prefix domain types.
Package model defines the Network, Chan, Msg, User, and Prefix domain types.
netguard
Package netguard provides safe outbound HTTP primitives for server-side fetches to user-controlled URLs.
Package netguard provides safe outbound HTTP primitives for server-side fetches to user-controlled URLs.
packages
Package packages implements `relay install`/`uninstall`/`upgrade`/`outdated`: fetch-extract-verify installation of theme packages, with no transitive dependency resolution (there is no Go equivalent of npm's dependency graph, and per the migration plan's decision, no equivalent of Node's generic onServerStart plugin API either - Go has no safe, portable way to dynamically load arbitrary third-party code the way Node's import() does, so an "installed package" here is always a theme).
Package packages implements `relay install`/`uninstall`/`upgrade`/`outdated`: fetch-extract-verify installation of theme packages, with no transitive dependency resolution (there is no Go equivalent of npm's dependency graph, and per the migration plan's decision, no equivalent of Node's generic onServerStart plugin API either - Go has no safe, portable way to dynamically load arbitrary third-party code the way Node's import() does, so an "installed package" here is always a theme).
partyline
Package partyline implements the server-side synthetic cross-account chat feature (partyline.ts): a virtual, IRC-less "network" every local account gets, whose one channel fans messages/reactions out to every other local account instead of an IRC server.
Package partyline implements the server-side synthetic cross-account chat feature (partyline.ts): a virtual, IRC-less "network" every local account gets, whose one channel fans messages/reactions out to every other local account instead of an IRC server.
pathutil
Package pathutil holds small filesystem-path helpers shared across internal packages that otherwise have no reason to depend on each other.
Package pathutil holds small filesystem-path helpers shared across internal packages that otherwise have no reason to depend on each other.
prefetchstore
Package prefetchstore ports storage.ts: a generic, content-hash-deduped file store.
Package prefetchstore ports storage.ts: a generic, content-hash-deduped file store.
publicmode
Package publicmode centralizes the public-mode security gates shared across subsystems.
Package publicmode centralizes the public-mode security gates shared across subsystems.
session
Package session is Stage 11's socket-event contract wiring: the Go equivalent of server/client.ts plus the per-connection portion of server/server.ts (everything from `sockets.on("connect", ...)` down to the individual `socket.on(...)` handler bodies).
Package session is Stage 11's socket-event contract wiring: the Go equivalent of server/client.ts plus the per-connection portion of server/server.ts (everything from `sockets.on("connect", ...)` down to the individual `socket.on(...)` handler bodies).
shortlinks
Package shortlinks implements the embedded link-shortener service.
Package shortlinks implements the embedded link-shortener service.
store
Package store defines the shared shape internal/store/sqlite, internal/store/postgres, and internal/store/textlog implement, mirroring server/plugins/ messageStorage/types.d.ts's MessageStorage/SearchableMessageStorage interfaces.
Package store defines the shared shape internal/store/sqlite, internal/store/postgres, and internal/store/textlog implement, mirroring server/plugins/ messageStorage/types.d.ts's MessageStorage/SearchableMessageStorage interfaces.
store/cleaner
Package cleaner implements the background message-retention cleanup job.
Package cleaner implements the background message-retention cleanup job.
store/postgres
Package postgres implements shared PostgreSQL-backed message storage.
Package postgres implements shared PostgreSQL-backed message storage.
store/sqlite
Package sqlite implements sqlite-backed message storage and migrations.
Package sqlite implements sqlite-backed message storage and migrations.
store/textlog
Package textlog implements write-only human-readable per-channel log files.
Package textlog implements write-only human-readable per-channel log files.
sts
Package sts persists IRCv3 Strict Transport Security policies.
Package sts persists IRCv3 Strict Transport Security policies.
themes
Package themes implements theme discovery, installation, and custom theme uploads.
Package themes implements theme discovery, installation, and custom theme uploads.
uploads
Package uploads implements token-authenticated file upload handling.
Package uploads implements token-authenticated file upload handling.
version
Package version holds the Go backend's own release version, mirroring scripts/version.js's pkg.version (package.json is the Node build's source of truth; this package is the Go build's).
Package version holds the Go backend's own release version, mirroring scripts/version.js's pkg.version (package.json is the Node build's source of truth; this package is the Go build's).
webpush
Package webpush implements VAPID-based web push notification delivery.
Package webpush implements VAPID-based web push notification delivery.
wsproto
Package wsproto implements the custom WebSocket envelope protocol that replaces Socket.IO.
Package wsproto implements the custom WebSocket envelope protocol that replaces Socket.IO.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL