How it works#
Tollan has two planes:
- The data plane carries your traffic: the relay and the agents.
- The control plane manages configuration: the API and the console.
Keeping them separate is the whole trick. The relay only pumps bytes; it never holds your CA key or your database. The control plane holds the state; it never sees your traffic.
The core idea: dial out, never listen#
A traditional "expose a service" setup opens an inbound port and hopes your firewall, NAT, and ISP cooperate. Tollan inverts it.
The agent on your device makes an outbound connection to the relay and keeps it open:
Internet ──▶ Tollan relay ──(mTLS, tollan.v1)──▶ Agent ──▶ your service
(public address) outbound tunnel 127.0.0.1:8080
(the device dials out) or a LAN hostBecause the tunnel is outbound, it works from behind NAT, CGNAT, and restrictive firewalls with no port-forwarding. Your device is never a listening target on the public internet — the only connection is the one it opened itself.
The players#
Agent#
A small client that runs on (or next to) your device. It:
- Establishes a mutual-TLS WebSocket to the relay — the device proves its identity with a client certificate, and pins the relay's certificate against a CA it trusts.
- Holds that connection open, answering keepalives and reconnecting with backoff if the network drops.
- When the relay opens a channel, connects to the local target (e.g.
127.0.0.1:8080or a LAN host) and pipes bytes both ways.
There are two agent implementations that speak the identical wire protocol:
- The Go agent for Linux, macOS, and Windows — installed from a device bundle.
- The TollanAgent library for ESP32 / Arduino, for devices too small to run the Go binary.
Relay#
The public-facing byte pump. It:
- Terminates the agents' mutual-TLS tunnels on its control port.
- Accepts inbound public connections and figures out which device each one belongs to — by TLS SNI hostname on the shared
:443ingress, or by a dedicated port. - Multiplexes each inbound connection onto a channel over the owning device's tunnel.
- Loads its routing table from the control plane and hot-reloads it when routes change — no restart, no dropped tunnels.
The relay is deliberately dumb about your data. For passthrough routes it never decrypts your traffic (see zero-knowledge passthrough); it just moves bytes.
Control plane#
The API and console — the brain. It:
- Stores devices, routes, certificates, organizations, and billing state.
- Runs the certificate authority (CA) that issues device and relay certificates.
- Builds device bundles (agent binary + certificates + config).
- Tells the relay what routes exist and pushes updates over an internal event channel.
- Enforces plan limits (devices, routes, egress, seats) and billing status.
You interact with the control plane through the console at app.tollan.ie or its REST API at api.tollan.ie.
What happens on a request#
Follow a single public request from the internet to your service and back:
- A user opens
https://my-nas.tollan.app/(or a port likerelay.tollan.ie:19000). - The relay reads the SNI hostname (
my-nas.tollan.app) from the TLS handshake, or matches the port, and finds the owning device in its route table. - The relay sends an
OPENframe down that device's tunnel, naming a new channel (and, for a forwarding agent, the internalhost:porttarget). - The agent dials the internal target and streams the connection back as
DATAframes; the relay forwards those bytes to the public visitor. - When either side hangs up, a
CLOSEframe tears the channel down.PING/PONGframes keep the idle tunnel alive in between.
That five-frame vocabulary — OPEN, DATA, CLOSE, PING, PONG — is the whole wire protocol.
Two ways an agent answers a channel#
- Handler mode — the device is the service. The agent hands channel data to your code (this is how the ESP32 handler mode serves a response directly).
- Forwarding mode — the device is a proxy for other hosts on its LAN. On
OPENit dials the relay-namedhost:portand pipes bytes, so one agent can front a whole network of printers, cameras, and appliances.
In forwarding mode the agent only ever dials targets on an allowlist you control. A compromised relay can name a target, but it can't make the device reach a host you never authorized. See routes and security.
Split deployment#
In production the relay and the control plane run on separate hosts:
- The control plane host holds the database, the console, and the sealed CA key — but no relay and no agent binaries.
- The relay host pumps traffic on
:443and the agent control port — but has no CA key, no database, and no operator UI.
They talk over an authenticated, private channel that stays on trusted infrastructure. You can restart either one independently: restart the control plane and tunnels stay up; restart the relay and agents reconnect on their backoff. See self-hosting.
Next: run through it yourself in the Quickstart.