A small WebSocket broadcast server in Go — one binary, one dependency, no database.
Clients connect to /ws, send JSON messages, and every other connected client receives them. That
is the whole feature set: no rooms, no history, no accounts. What it does bring is the operational
surface you would otherwise write yourself — origin validation against CSWSH, per-connection rate
limiting, message size caps, graceful shutdown, and a static cross-platform binary. Use it as a
real-time relay behind your own application, or as a starting point to build on.
Requires Go 1.26.5 or later (go version) and Git. GNU Make is optional.
git clone https://github.com/maltemindedal/blip.git
cd blip
make build # or: go build -o bin/blip ./cmd/server
./bin/blip # Windows: .\bin\blip.exetime=2026-07-23T15:51:57.081+02:00 level=INFO msg="starting Blip server" version=dev commit=unknown build_time=unknown
time=2026-07-23T15:51:57.081+02:00 level=INFO msg="hub started and ready to manage WebSocket connections"
time=2026-07-23T15:51:57.082+02:00 level=INFO msg="server listening" addr=:8080
Logs are structured key-value records via log/slog; set LOG_LEVEL=debug to trace individual
messages.
Open http://localhost:8080/test in two browser tabs, click Connect in each, and type. Messages go to the other tab — senders do not receive their own messages.
With Docker instead:
docker compose up -d --buildSend and receive JSON with a single content field. The Origin header must match the server's
allow-list — browsers set it automatically, other clients must not forget it.
const ws = new WebSocket("ws://localhost:8080/ws");
ws.onopen = () => ws.send(JSON.stringify({ content: "Hello" }));
ws.onmessage = (event) => console.log(JSON.parse(event.data).content);Configuration is entirely environment variables:
SERVER_PORT=:8080 \
ALLOWED_ORIGINS=http://localhost:8080,http://localhost:3000 \
MAX_MESSAGE_SIZE=512 \
RATE_LIMIT_BURST=5 \
LOG_LEVEL=info \
./bin/blipFull index: docs/README.md.
| Guide | Contents |
|---|---|
| Getting started | Build, run, and try it in ~10 minutes |
| Connecting a client | JavaScript, Go, Python, websocat examples |
| Configuration | Every environment variable and default |
| API reference | Endpoints, message protocol, failure modes |
| Deploying to production | TLS, reverse proxy, systemd, scaling limits |
| Security hardening | What is protected, and what is not |
| Architecture overview | Hub, client pumps, design trade-offs |
| Endpoint | Purpose |
|---|---|
/ws |
WebSocket endpoint. GET only; rejects disallowed origins with 403 |
/ |
Health check returning Blip server is running! (also the catch-all for unmatched paths) |
/test |
Built-in browser test page — development only |
cmd/server/ Entry point: startup, signal handling, graceful shutdown
internal/server/ Hub, client pumps, handlers, config, origin checks, rate limiter
test/ Unit and integration suites plus shared helpers
docs/ Documentation (see docs/README.md)
.github/workflows/ CI pipeline
Actively maintained, single-instance by design. The hub lives in process memory, so multiple
instances behind a load balancer form separate chat rooms — see
scaling. There is no built-in authentication;
enforce it in front of /ws if your data needs it.
Test coverage was 70.4% of statements as of 2026-07-24 (make test-coverage). Broadcast fan-out and
rate limiting are allocation-free per message, and the origin check is allocation-free per
handshake — see
performance for the numbers and make bench to
reproduce them.
Bug reports, features, and pull requests are welcome — see docs/contributing.md.
MIT — see LICENSE.