Last week I shared a new project with our UX designer. Sent her an ngrok link, asked her to try the onboarding flow. She clicked around for a few minutes, then messaged me: "I can't get past the login screen."
I checked my code. Everything looked fine. I tried the flow locally — worked perfectly. I asked her to try again. Same result.
It took me twenty minutes to figure out what happened. The frontend was loading through the tunnel, but the login form was still posting to localhost:3001. The API call was silently failing on her machine because localhost doesn't exist there. On mine it worked because the API was running locally.
Twenty minutes of debugging for something I would have seen in five seconds if I could have just watched her session.
The paradox
At work, we have error tracking, analytics funnels, session replay, performance monitoring, log aggregation. Dashboards for dashboards. All for apps that already work, where the decisions are incremental.
But when we're building something new — a side project, an MVP, a freelance prototype — and we share it for the first time, we send a localhost link with ngrok and ask "what do you think?"
Full observability for the app that already has product-market fit. Zero visibility for the one where every decision still matters. Aren't we being ridiculous?
The stage where you learn the fastest is the stage where you see the least
Your first testers are the most valuable and the least observed. They don't file bug reports. They don't describe what they did accurately. They say "looks good" or "I can't get past login" and you're left reverse-engineering what happened from a Slack message.
This is the moment where you're supposed to discover that your navigation is confusing, that nobody notices the CTA, that the onboarding flow has a dead end. Instead you're debugging tunnel misconfigurations, creating a single use reverse proxy or chasing phantom bugs that only exist because your sharing setup is broken.
And the tunnel itself is causing half these problems
Here's the thing nobody warns you about: most apps aren't a single service on a single port anymore.
You have a React frontend on 5173, an API on 3001, maybe an auth service on 3002. You run ngrok http 5173 and your frontend loads — but API calls still point to localhost. Cookies are scoped to the wrong domain. CORS headers don't match. Auth redirects break because the callback URL points to a port on your machine.
So you open a second tunnel. Now you have two URLs, neither of which knows about the other. You start hacking CORS configs and rewriting cookie domains and suddenly you're spending your afternoon on infrastructure for a demo that was supposed to take five minutes.
That's exactly what happened with my login screen. The frontend loaded fine through the tunnel. The API didn't, because it was never tunneled. From my machine, everything worked. From hers, nothing did.
Single-port tunneling works great — until your app has a frontend and a backend, which is most apps built in the last five years.
The two problems nobody solves together
Access — making your localhost reachable. ngrok and Cloudflare Tunnel handle this well, as long as your app fits behind one port.
Visibility — knowing what actually happened when someone used it. Analytics and replay tools handle this, as long as your app is deployed and instrumented.
The gap is the intersection. Local, multi-service, uninstrumented, too early for any of that. That's where you spend the most critical weeks of a new project, and it's where tooling just doesn't show up.
What I built to close it
I kept landing in this gap, so I built DemoTape.
One command. Your app stays on localhost. All your local services — frontend, API, auth, whatever — route through a single shared URL. Cookies, CORS, Websockets and redirects just work because there's one domain, not one tunnel per port.
My login screen problem wouldn't have existed — the API would have been routed through the same URL as the frontend, no separate tunnel needed.
And every session is recorded. Not events you anticipated. Not funnels you defined. The full session — clicks, scrolls, pauses, console errors, failed network requests. If my designer's login had still failed for some other reason, I'd have seen the failed POST in the replay instead of spending twenty minutes guessing.
No SDK. No deployment. No setup beyond the command you already ran.
npx @demotape.dev/cli demo
That downloads a sample app, starts it locally, and opens a shared session with replay. Takes about 60 seconds.
Convinced? Install with npm and run:
demotape
As simple as that.
tl;dr
The stage where you learn the fastest is the stage where you see the least. We solved observability for production years ago. Nobody solved it for the moment you first show someone what you're building.
Top comments (0)