How it works
The architecture that keeps terminal data off the server.
Porthole is built around one idea: the server should never see your terminal. Everything below follows from that.
The pieces
- Agent — the
portholebinary on your machine. It opens a PTY (a real shell) and speaks WebRTC to the viewer. - Signaling server — a small, stateless service that introduces two peers to each other. It holds sessions in memory only; there is no database.
- Viewer — either a browser, or another
portholebinary runningporthole join. Both speak the same protocol; the server cannot tell them apart and does not need to.
The handshake
WebRTC peers cannot find each other on their own — they need a broker to swap connection details. That is the signaling server's entire job:
p3rx-9kma).From step 4 on, the server is out of the loop. Terminal output and the viewer's keystrokes travel directly between the two peers.
The signaling server only ever relays the metadata needed to connect. It never sees a byte of terminal output or input — and neither does it see session context, which is deliberately sent over the peer-to-peer channel after connecting rather than during the handshake.
Why it is private
- Direct path — once connected, there is no server in the middle to log, store, or leak terminal data.
- Encrypted in transit — WebRTC DataChannels are encrypted with DTLS by default.
- Enforced at the source — read-only, passwords, invites, and masking are all applied by the agent on your machine, so a tampered viewer cannot bypass them.
That last point is the one that matters most. A permission check in the browser is a suggestion; the browser is code the other party controls. Porthole's permission check runs in the writer goroutine on the host, immediately before bytes reach the shell.
Ordered and reliable
The DataChannel is configured as ordered and reliable (TCP-like). Keystrokes and output arrive in order with nothing dropped — exactly what a terminal needs.
Frames are capped at 256 KiB. That is not an arbitrary number: the SCTP layer reassembles a whole message in memory against a 1 MiB ceiling, and a message above that ceiling can never complete — it would pin the receive window at zero and wedge the connection permanently. Anything larger chunks instead.
When the connection drops
The PTY outlives the peer, by design. Killing the shell on every network blip would destroy your working directory, your scrollback, and anything running.
While no viewer is attached, output accumulates in a bounded ring buffer and is replayed when one reconnects, so they see what they missed. A grant of write access survives reconnection too — but only for the peer it was issued to, never for whoever connects next.
TURN relay fallback
If a direct connection cannot be established — some strict NATs and corporate firewalls — WebRTC falls back to a TURN relay to keep the session working.
The relay forwards DTLS-encrypted packets and cannot read your terminal, but it is worth knowing that in this case the traffic is passing through a third party's infrastructure rather than travelling directly.