eleven / messenger Public
🤖 lchat try — the one-command trial tunnel
lchat try is the zero-setup way to experience a real, self-run Eleven space: download the binary, run one command, get a public https://try-<slug>.11ch.at address serving a full instance from your own machine — through NAT, with no inbound ports, no DNS record, no certificate, and no root. Stop the command and the address retires; the space's data stays in ./lchat-try.db.
curl -LO https://github.com/elevenmessenger/releases/releases/download/server/lchat-linux-amd64
chmod +x lchat-linux-amd64
./lchat-linux-amd64 tryIt prints two links: the space's address, and a one-time setup link that makes you its admin (the same conversational setup a hosted space gets).
How it works
Two halves, both in the single binary (internal/tunnel/):
- The client (
lchat try) starts a normal local instance (a subprocess running the ordinary single-instance path againstlchat-try.db), then dials out to the gateway over one control WebSocket (wss://tunnel.11ch.at/api/tunnel/register). The gateway assignstry-<slug>.11ch.atand sends{"type":"ready","host":…}. For every backend connection the public side needs, the gateway sends{"type":"dial","id":…}and the client dials back one data WebSocket (/api/tunnel/conn/<id>), which both ends treat as a plain byte pipe (websocket.NetConn) spliced to the local instance's TCP port. Ordinary HTTP/1.1 flows through it — including WebSocket upgrades, which is what makes the chat itself work through the tunnel unmodified. - The gateway (
lchat tunnel serve, unitlchat-tunnel.service) listens on/run/lchat/tunnel.sockbehind the ordinary router. Each live tunnel is published as a plain registry route (kind=service,host=try-…,socket=/run/lchat/tunnel.sock), so the router needs no tunnel awareness at all: TLS comes from the existing*.11ch.atwildcard cert, and routing is the same host→socket lookup every instance uses. The router notices new routes via its registry watcher (aPRAGMA data_versionpoll every 2s), so the unprivileged gateway never needs rights to signal anything.
The E2EE story is unchanged: what crosses the tunnel is the same already-ciphertext traffic the router fronts for every instance. The gateway can see exactly what the router can see — nothing readable.
Why single-label slugs
try-x7k2m.11ch.at, not x7k2m.try.11ch.at: TLS wildcards match one label, and the fleet's wildcard cert is *.11ch.at. A nested scheme would need its own wildcard cert and DNS; the prefix gets the same effect for free.
Operations
- Enable on a box:
systemctl enable --now lchat-tunnel, then route its control host once:lchat add-service tunnel.11ch.at --socket /run/lchat/tunnel.sock. (Bootstrap installs the unit everywhere; it's inert until enabled.) - Config (
/data/tunnel.env, optional):LCHAT_TUNNEL_HOST,LCHAT_TUNNEL_DOMAIN,LCHAT_TUNNEL_SOCKET, plus--max(default 50 concurrent tunnels). - Guardrails: 5 new tunnels per IP per hour, 50 concurrent tunnels, 32 concurrent connections per tunnel, 24-hour lifetime, dial-back timeout 15s. And the door check: before a hostname goes public — and every 10 minutes after — the gateway probes the tunnelled backend through the tunnel (
GET /api/version) and refuses anything that isn't an Eleven instance, so the trial domain never lends its name (or its TLS) to arbitrary content. This is a trial surface, not free hosting — the limits say so politely. - Kill switch:
lchat tunnel kill try-<slug>.11ch.atremoves the route (the router stops serving it within seconds);systemctl restart lchat-tunneladditionally drops every live tunnel connection. - Cleanup is automatic: a tunnel's route is removed when its client disconnects; on gateway restart, leftover
try-%rows pointing at its socket are swept.lchat instanceswill show live trials asservicerows — transient by design. - Self-hosters can run their own gateway anywhere the router fronts a wildcard domain;
lchat try --gateway https://tunnel.your.domainpoints the client at it.
Honest limits
- A trial's address dies with the process (or at 24h).
lchat tryagain mints a fresh address; the data inlchat-try.dbpersists, but members' saved links won't follow it. The printed copy says so — a keeper space should graduate to a real install (docs/self-hosting.md). - The gateway is a relay for one machine's trial, not a CDN: throughput is whatever the trial machine's uplink gives it.