← the Atlas

Port preview — click the URL your agent just printed

Features·seedling·proposed·

Auto-detected dev-server ports on any bound host become clickable previews served through kolu's own origin — TCP bytes riding the existing padi wire, so the two-hop topology needs zero new listeners.

The feature, in plain words. Agents start dev servers constantly and print the URL (localhost:3000). Today that URL is dead text if the terminal lives on a remote host. After this: padi notices the port, a chip appears on the terminal, and clicking it opens the running app — served through the same kolu origin you’re already on, no matter which machine the server actually runs on. (Codespaces/VS Code prove the demand — they auto-detect and forward console URLs; the agent era makes it sharper, because agents print URLs far more often than humans type ssh -L. See the future-work survey.)

The two-hop question, answered

The hard-looking case: kolu-server runs on a headless box you reach remotely, and it binds a padi on a third machine over ssh. The dev server listens on localhost:3000 on that third machine — your browser is two networks away. Doesn’t forwarding take two hops? Yes — and both hops already exist as open pipes. That’s the whole design:

YOUR MACHINE (anywhere)HEADLESS BOX (runs kolu-server)REMOTE PADI HOST (over ssh)Browserkolu tab + the previewiframe — ONE originkolu-serverreverse-proxy route:/preview-port/<host>/<port>/*padi wire (exists)stdio-over-ssh surface link:cells · procedures · BYTESTREAMS (terminalAttach kin)padi (on the host)port sensor: sees the URL inPTY output · serves ports celldev serverlocalhost:3000 — started bythe agent in a kolu terminal(never exposed publicly)HOP A: normal kolu origin (HTTPS/WS) — already openridesHOP B: TCP bytes as a surfacebyte-stream member — the pipe already existspadi dials localhost:3000 (loopback, on-host)The trick: nothing new listens anywhere. The browser talks to the origin it already trusts; kolu-server pipes bytes down the padi wire it already holds; padi dials loopback on its own host. Two hops = two existing pipes.
Nothing new listens anywhere. HOP A is the kolu origin the browser already trusts; HOP B is the padi surface wire kolu-server already holds (the same link that carries terminal bytes today); padi dials loopback on its own host. ssh -L appears nowhere.

Why this shape beats the alternatives

The three pieces

  1. Detection (padi). A PTY-output sensor for URL/port patterns (padi already runs output sensors for agent state), publishing a per-terminal ports cell — { port, url, firstSeenAt, terminalId }, value-typed per the forwarding policy. Agents print URLs; humans get the same chip when they start servers by hand.
  2. Transport (padi surface + kolu-server). The dialPort byte-stream member (fail-through on link drop, per the byte-stream policy — a dropped preview reconnects end-to-end, never replays stale bytes), and kolu-server’s reverse-proxy route streaming request/response bodies over it. WebSocket upgrades tunnel the same way (the proxy speaks the upgrade; the bytes don’t care).
  3. UI (client). A port chip on the terminal tile (and the dock row), click = open /preview-port/<host>/<port>/ in a tab or the preview panel. The chip carries the host name — the same per-host honesty rules as everything post-switch.

Honest limits, named up front

Sequencing: single-host (local arm) works standing alone and is the natural first PR; the remote leg is the same members over the existing wire. Constraint ledger of the future-work note applies (notably: the wire stays per-feature-bounded — a preview stream is opened on click, never held warm per host).