A WebSocket Relay for Two
There’s a WebSocket relay server on this VPS whose entire user base is two VS Code instances — one on the server, one on a laptop. Its purpose is real-time messaging between development environments.
This might be the most over-engineered two-user chat system in existence. It’s also genuinely useful.
The problem
When you’re developing across two machines — editing code on a laptop, deploying on a server — there’s a coordination gap. You make a change locally, push to git, SSH into the server, pull, restart containers. Or you edit directly on the server via Remote SSH and lose the local development experience.
The relay bridges that gap. Either environment can send messages to the other. “Build complete.” “Tests passed.” “Deploy triggered.” It’s a coordination channel that lives outside of git.
The implementation
A Node.js server running the ws library, about 200 lines of code:
- Authentication — each client connects with an API key. The server validates against a configured list of known instances. No user accounts, no passwords, no OAuth. Two keys, hardcoded in environment variables.
- Message relay — when one client sends a message, the server broadcasts it to all other connected clients. With two clients, “broadcast” means “send to the other one.”
- Message history — messages are stored in SQLite with timestamps. A client that reconnects can request history since its last seen message. This handles the case where one side was offline when a message was sent.
- Channels — messages can be tagged with a channel name for filtering. In practice, there’s one channel.
Why not just use SSH?
Because SSH is synchronous. You run a command, you see its output. The relay is asynchronous — a message sent from the laptop arrives on the server whenever the server is listening, even if nobody is watching. It’s the difference between a phone call and a text message.
The relay also enables automation. A VS Code extension can send a message when a build completes. A server-side script can notify the laptop when a deployment finishes. No human needs to be in the loop.
The Docker setup
One container, Node 20 Alpine, port 3000, behind Traefik with TLS. The SQLite database is mounted as a volume for persistence. The connection URL is wss:// (WebSocket Secure), so all communication is encrypted.
Total resource usage: about 20MB of RAM. For a service that provides real-time cross-machine coordination, that’s essentially free.
Was it worth building?
For a general audience, no. Use Slack, or Discord, or literally any messaging platform. But for two development environments that need a private, low-latency, programmable communication channel with no third-party dependencies? Yes. Every tool you build for yourself is exactly the tool you need.