Somewhere in the last ten years, “I want to see the CPU graph on my other machine” quietly turned into a SaaS product with an agent, a relay server, an account, and a pricing page, and the whole arrangement is strange when you sit with it for a minute, because the two machines are yours, the data is a stream of numbers about hardware you own, and the only thing the cloud in the middle is actually contributing is a rendezvous point and a monthly invoice. LinSight’s answer to remote monitoring is that there is no middle: the daemon already serves everything over a Unix socket on the local machine, so the remote feature is a small binary called linsight-tunnel that pipes those socket bytes over a mutually authenticated TLS connection to another linsight-tunnel on your desktop, where it re-materializes as a local Unix socket that the GUI, the CLI, and the Prometheus scraper connect to as if the daemon were sitting right there.
The reason we went this way instead of building a relay is that a relay inherits every problem the user was trying to avoid, since it holds plaintext or near-plaintext metric streams from every customer, it needs accounts and tokens and a breach disclosure policy, and it goes down on exactly the Saturday night when your rack is misbehaving, whereas a direct mTLS link between two machines you control has a trust boundary you can hold in one hand: a CA you generated, a server cert, a client cert, and nothing else.
A transparent byte pipe, not a protocol gateway
The design decision that makes the whole thing maintainable is that the tunnel understands nothing about the traffic it carries, because it is not translating between a local protocol and a cloud protocol, it is copying bytes in both directions between a Unix socket and a TLS session, and every client that already works against linsightd keeps working without knowing the tunnel exists. The topology looks like this:
remote machine desktop
linsightd linsight (GUI/CLI)
│ unix sock │ unix sock
linsight-tunnel ── TCP + mTLS ──> linsight-tunnel
server :9443 client
On the remote host you point the server side at the daemon’s socket and hand it a cert chain, and on the desktop you point the client side at a fresh local socket and hand it the matching chain:
# on the remote machine running linsightd
linsight-tunnel server \
--bind 0.0.0.0:9443 \
--cert server.pem --key server.key --ca ca.pem \
--socket /run/user/1000/linsight.sock \
--allow-san desktop-1
# on the desktop
linsight-tunnel client \
--listen $XDG_RUNTIME_DIR/linsight-remote.sock \
--server remote.host.example:9443 \
--cert client.pem --key client.key --ca ca.pem
After that, linsight --socket $XDG_RUNTIME_DIR/linsight-remote.sock opens the live dashboard against the remote machine, and the postcard framing, the version handshake, and every sensor subscription flow through unchanged, which is the whole point: the wire format we already wrote about in the postcard piece never grew a network mode, because it never needed one.
The trust model, stated plainly
Mutual TLS means both sides present certificates, so the server authenticates itself to the client the way any TLS server does, and the client authenticates itself to the server with its own certificate, which is the property that lets you bind the tunnel to a real interface without turning your monitoring daemon into an unauthenticated sensor feed for the internet. The defaults lean cautious: the server binds to 127.0.0.1:9443 unless you explicitly pass 0.0.0.0:9443, so an install never silently bridges the daemon to every interface on the host, and --max-connections defaults to 64 with excess connections dropped before TLS authentication begins, so an unauthenticated connection flood gets turned away before the daemon pays any handshake or allocation costs per connection, which is the failure mode that actually hurts when the auth path allocates on every attempt.
The important sentence in the trust model is the one about the CA boundary: if you pass no --allow-cn or --allow-san filters on the server, then any client certificate chained to your CA is accepted, which means the CA trust boundary equals full daemon access, and that is a fine default for a two-machine setup where the CA exists only for this purpose. When you want tighter control, --allow-cn and --allow-san accept exact, case-insensitive matches on the client certificate’s CommonName or DNS SubjectAltName, a leading wildcard like *.example.com matches one DNS label and not the bare suffix, and when any allowlist is configured the client cert must match at least one entry after normal chain validation succeeds, so you can admit desktop-1 and laptop-1 while a third cert signed by the same CA gets turned away (note that --allow-san matches DNS SubjectAltName entries, so the client cert must actually carry one, a CN-only cert will be rejected without a hint).
For kicking the tires, a self-signed P-256 CA and two leaf certs with openssl takes about a minute, and the tunnel’s own docs walk through the exact invocations; for a real deployment, the docs say to use whatever PKI you already trust, whether that is step-ca, Vault, or your org’s internal CA, because the tunnel does not want to be your certificate authority, it wants to consume the one you already have.
The honest tradeoff: you probably want ssh -L
Here is the part a marketing page would bury: for most people, most of the time, the right answer is not linsight-tunnel at all, it is ssh -L $XDG_RUNTIME_DIR/linsight.sock:/run/user/1000/linsight.sock host, because SSH gives you the same security guarantees with host keys and shell auth you have already managed, and it adds zero certificate machinery to your life (Unix-socket forwarding needs OpenSSH 6.7 or newer on both ends, which shipped in 2014, so anything you are actually running qualifies). We say this in the tunnel’s own README, in the first paragraph, because a tool that exists to solve a narrow topology problem should not pretend to be a lifestyle. The GUI leans the same direction; the Settings page stores saved hosts as ssh://[user@]host[:port] entries in ~/.config/linsight/hosts.json, and remote switching in the sidebar is just a key-based SSH connection with the same trust model as the manual forward.
The tunnel earns its keep in the topologies where SSH is the wrong tool: kiosks and monitoring nodes that have no SSH account to log into, environments where SSH egress is restricted but a pinned TLS port is allowed, and setups where the person watching the dashboard is not the person who owns the shell on the monitored box. In those cases, mTLS is the natural fit precisely because both ends authenticate with certificates rather than interactive credentials, and the connection carries no shell, no forwarding negotiation, and no attack surface beyond the byte pipe.
Small operational details that matter at 3 a.m.
The unglamorous edges are where a tunnel like this lives or dies, so a few of them are worth naming. Ctrl+C or SIGTERM triggers a graceful drain with a ten-second budget for in-flight TLS sessions to send close_notify, after which outstanding tasks are aborted so the process actually exits instead of hanging a systemd unit, and on the client side a Drop guard removes the local listener socket on exit so a crashed or Ctrl+C’d client leaves nothing behind. On startup the client probes a pre-existing socket file by connecting to it first, refuses to clobber the path if a live listener answers, and removes it only when nothing does, which replaces the old exists-then-delete race with a check that also protects a healthy peer’s socket from being unlinked out from under it. The test suite generates a full self-signed CA plus server and client chain with rcgen and completes a mutual handshake with byte round-trips in both directions, and there is a dedicated test that presents a certificate signed by an out-of-bundle CA and asserts rejection, because the “verifier accidentally accepts everything” failure mode is the one that turns an mTLS feature into a decorative one.
The historical parallel I keep coming back to is stunnel, which solved “wrap a plaintext service in TLS” in the late nineties with exactly this shape of tool, a small binary that shuttled bytes between a local socket and an encrypted tunnel, and which a generation of admins reached for because it did one thing and the config fit on an index card. The modern equivalent adds mutual authentication and an allowlist and drops the OpenSSL config file for a couple of flags, but the philosophy is identical: your encryption boundary should be a thing you can audit in an afternoon, not a platform you have to trust. And that is the whole answer to the question in the title, because remote monitoring without a cloud is not a product category, it is two processes and three certificates, and it has been that simple for a while.
