Show what you are building, without deploying it
Run one command and someone else can use the app on your laptop, in their browser, from anywhere. No public URL, no inbound port, no VPN — and nothing left running afterwards, because there was never an address to leave running.
A description of a request, carried as a message
Porthole does not forward packets. It takes the HTTP request the guest's browser wanted to make, seals it, and sends it as an ordinary message on a channel. The agent on your machine unseals it, makes that request against localhost, and sends the response back the same way.
-
You run
porthole 3000. It connects outbound to a channel and waits. - You send a link and, separately, the session password. The password is never in the link — a link alone reaches nothing.
- Their browser loads a session page holding the key. A service worker intercepts every fetch the app makes and tunnels it, so the app runs unmodified — it does not know it is not local.
- The relay sees sealed blobs. It cannot read the traffic, and there is no TLS termination where it could.
Not another tunnel
ngrok, Cloudflare Tunnel and friends work by creating a public address for your local app. Porthole creates no address at all, and that is the entire point.
| A tunnel with a public URL | Porthole | |
|---|---|---|
| Public URL | yes — scannable within minutes | none exists |
| Inbound ports | none | none |
| Who can reach it | anyone holding the URL | channel and password holders |
| What the relay sees | your traffic — TLS terminates there | ciphertext |
| Left behind | live until you stop it | nothing to leave behind |
Pick who gets in, deliberately
Porthole and Backstage are one app now: the same agent and the same viewer, and the
difference is a mode you choose when you start it. backstage still works —
it is this agent with the door shut by default.
Instant --mode instant
Whoever holds the link and the password reaches the app. Fast, and right when
you are showing a colleague something for ten minutes. The default for porthole.
Approval --mode approval
Nobody gets in until you press a key, every knock and refusal is written to an audit
file, and a named session (--session) gives back the same link tomorrow.
Right when the person asking is a client, or the answer has to survive a security
review. --mode review lets anyone with the link look, and nobody change anything.
What it does not do
It is not hosting. Your machine has to be awake and the agent running — close the laptop and the session ends, which is the honest consequence of there being no copy of your app anywhere else.
And in instant mode a link plus a password reaches everything the app serves, on every path —
limit it with --allow and --deny, or start it in
approval mode and let each viewer in yourself.
What it costs
Free for an open link — approval and review, with an audit file, are licensed
An open link is free: your machine serves the app and the channel carries sealed blobs, so there is nothing to meter. The door and the paperwork — approval, read-only review and the audit file, formerly Backstage — are what a licence covers. Without one they still run, and the log records licensed: false.
The part that matters, in the open
A service worker in the guest's browser answers the app's own requests, so a private site loads with no public URL.
await navigator.serviceWorker.register('sw.js', { scope: './' });
await navigator.serviceWorker.ready;
navigator.serviceWorker.addEventListener('message', onWorkerMessage);
Taken from this app's source, not written for the page. Built on the Messaging Platform SDK.