osmRouter docs
Concepts

Architecture

Pinned-TLS + role-inverted HTTP/2: the protocol that lets your laptop be a server.

osmRouter v2 ("Option D" internally) routes every public request from a visitor to your Mac through a path that has no third-party software inside the data plane. You operate the cloud edge; you operate the device. Nobody else can intercept, replay, or rate-limit your traffic.

The data path

visitor (TLS 1.3)


Caddy edge  ── terminates TLS using a Let's-Encrypt cert minted
   │            on-demand for your domain


osm-proxy   ── HTTP reverse proxy with FlushInterval=-1 (true streaming)

   ▼            pinned-TLS over TCP — proxy is the *client*, Mac is the *server*
osm-agent   ── runs on your Mac. Listens on a unix socket, terminates TLS
   │            using an operator-issued leaf cert. NOT publicly reachable.

   ▼            HTTP/2 reverse flow inside the tunnel
your local process at 127.0.0.1:<port>

The two unusual properties of this wire protocol are:

  1. Pinned TLS. The trust anchor for the proxy↔Mac tunnel is an operator CA embedded into the Mac sidecar binary at build time (//go:embed). The Mac doesn't trust the public web PKI for this hop; it only trusts the proxy if its leaf chains up to a CA we baked in.
  2. Role inversion. The Mac is the server of the inner HTTP/2 connection, even though it's the client of the outer TCP socket. The proxy dials the Mac (firewall-friendly), but once the tunnel is up, the proxy forwards visitor requests into the tunnel and the Mac responds. This is the trick that lets your laptop "be" the public origin without ever opening a port.

Read the full formal spec in the Yellow Paper. It defines every frame, every header, and every state transition.

What's running where

ComponentWhereWhat it does
osm-agent (sidecar)Your MacMaintains the pinned-TLS tunnel to the proxy. Forwards requests it receives from the proxy to a local port.
osmRouter Mac appYour MacSwiftUI UI for binding domains and watching tunnel traffic. Manages the sidecar process.
osm-proxyCloud (our Hetzner box, or your own)TLS server for the operator-CA pinned tunnels. Reverse proxies visitor requests into the right tunnel.
osm-serverCloudControl plane: auth, domains, devices, bindings. Speaks HTTPS and WebSockets to the dashboard and the Mac app.
CaddyCloudPublic HTTPS terminator. Mints Let's Encrypt certs on-demand for customer domains. Reverse proxies to osm-proxy for tunneled paths and to osm-server for control-plane paths.
Postgres + RedisCloudState for the control plane.

Why role inversion matters

The "obvious" way to build a reverse-tunnel is the ngrok pattern: the Mac dials a public TCP endpoint and upgrades it to a bidirectional channel where the cloud forwards requests in.

This works, but it has two costs:

  1. The cloud is a TLS endpoint. The cloud sees visitor request bodies in plaintext. Even if it doesn't log them, it could.
  2. The wire is bespoke. It's a custom framing on top of WebSockets or a raw socket — every reverse-tunnel vendor invents their own.

osmRouter inverts the roles instead: the Mac is the TLS server of an HTTP/2 connection that flows over a TCP connection it dialed out. The cloud is the TLS client. This means:

  1. The Mac sees plaintext. The cloud sees TLS handshake + encrypted application bytes. The Mac decrypts on arrival.
  2. The wire is plain HTTP/2. No bespoke framing. Anything that speaks HTTP/2 can be a sidecar.

This is the Option D design (so named because it was the fourth design we considered — A and B were ngrok-style; C was a QUIC variant; D won).

What the trust boundary buys you

  • The operator (us, today; you, if you self-host) cannot read request bodies. The bytes the proxy forwards are encrypted to the Mac sidecar's TLS endpoint.
  • The operator cannot replay. Each request is signed by the visitor's TLS session at the edge.
  • The operator cannot impersonate your device. The pinned operator CA only signs leaves; the leaves' private keys live on the proxy box. To impersonate your Mac, the operator would need a device leaf — which doesn't exist. (Per-node device leaves are on the v2.1 roadmap as a defense in depth.)

See Sovereignty & Trust for the full threat model.

Where to go next

On this page