Tech

LAN-Direct Routing: Sub-Millisecond Latency for Local Dashboards

Akshay Sarode
Direct answer

The dashboard probes 127.0.0.1:33120 on every API call. If the local daemon answers, the request stays on your loopback interface — round-trip 0.3–0.8ms. If the probe fails (you're off the network), the dashboard falls back to a Firebase-gated tunnel transparently. The user sees no difference except latency.

Most "control your machine from a browser" tools route every keystroke through a cloud relay. SSH-via-browser products. Cloud-IDE backends. The pattern: browser → CDN → cloud relay → tunnel back to your machine. Latency is 80–200ms even when you're sitting two feet from the machine.

For a terminal — where every keystroke is interactive — that latency is what you're going to remember about the product. Make it 1ms and the experience is invisible. Make it 100ms and the experience is "it works but it's a little laggy."

The probe

The dashboard's network layer wraps every fetch:

async function api(path, init) {
  // 1. Try local
  try {
    const r = await fetch(`http://127.0.0.1:33120${path}`, {
      ...init,
      signal: AbortSignal.timeout(150),
    });
    if (r.ok) return r;
  } catch { /* fall through */ }

  // 2. Fall back to tunnel
  return fetch(`https://${machineId}.tunnel.ujex.dev${path}`, init);
}

150ms timeout is the budget. If the local daemon is reachable, it answers in <5ms. If not, the connection refuses or times out fast on a healthy network. The tunnel takes over.

Why 127.0.0.1 from a browser at all?

The dashboard runs at celistra.dev (Firebase Hosting). When the dashboard JS calls fetch('http://127.0.0.1:33120/...'), the browser opens a connection from your laptop to your laptop's loopback. Mixed-content is the gotcha — HTTPS pages can't open HTTP connections in modern browsers without a CORS pre-flight that allows it.

The fix: the daemon serves CORS headers that explicitly allow https://celistra.dev as origin. The browser permits the cross-protocol fetch because the daemon is on the loopback (localhost is treated as secure context).

// daemon-side
w.Header().Set("Access-Control-Allow-Origin", "https://celistra.dev")
w.Header().Set("Access-Control-Allow-Credentials", "true")
w.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type")

Numbers from a real laptop

M3 MacBook, Wi-Fi 6, daemon and dashboard tab same machine:

PathMedianP95
Loopback (LAN-direct)0.34ms0.81ms
Same-LAN remote daemon (over Wi-Fi)1.8ms4.2ms
Tunnel via mail.ujex.dev (USA → DE relay)148ms270ms

The story tells itself. Loopback is two orders of magnitude faster than the tunnel. Same-LAN-remote (your laptop's dashboard talking to the daemon on a Pi across the room) is still an order faster than the tunnel.

Why this matters for terminals

A PTY pushes a byte for every keystroke. The browser pushes one back over WebSocket. Round-trip on every character. At 100ms it feels laggy; at 50ms it's fine; at 1ms it feels native.

Tools that proxy through the cloud can't get below 50ms unless they edge-relay near you. Even then, they're paying for the edge nodes. LAN-direct is free; it's the network you already have.

What about WebSocket?

Same probe pattern. The PTY connection opens to ws://127.0.0.1:33120/v1/agents/X/ws first. If it drops or refuses, the dashboard upgrades to wss://...tunnel.ujex.dev/.... The daemon authenticates both with the same Firebase ID token — no separate tunnel auth.

What about ports?

33120 is fixed. The daemon binds 127.0.0.1:33120 exclusively (not 0.0.0.0) — even if your firewall is misconfigured, the daemon is unreachable from another LAN box. The "remote daemon over LAN" case (laptop dashboard, Pi daemon) goes over the tunnel, not directly. That's a deliberate tradeoff: simpler security model, slightly worse same-LAN-remote latency (still under 5ms in practice).

The probe failure mode

What if you're on coffee-shop Wi-Fi and the dashboard probes 127.0.0.1 first? It's still your laptop. The daemon either answers or doesn't. If it does (you have the daemon running on your laptop), great. If not, the timeout fires and the tunnel takes over. No user-visible state.

Edge case: the user has a different process bound to 127.0.0.1:33120. The probe gets back a non-Celistra response (or a 401 with the wrong shape) and we fall back. We check for a magic header in the daemon's response to disambiguate.

FAQ

Does this work in Firefox / Safari?

Yes. CORS for localhost is consistent across modern browsers. Safari is sometimes pickier about mixed-content; the daemon's CORS headers handle it.

Why not bind to 0.0.0.0 and use TLS?

Because then the daemon is reachable from any machine on the LAN, and the security model has to assume that. Binding to loopback only means 'one user per machine' is the simple, correct posture.

What if I want a phone on the same LAN to skip the tunnel?

It can't — phones can't connect to your laptop's loopback. The mobile app uses the tunnel by design. LAN-direct is dashboard-on-the-same-machine only.

How does the probe avoid burning startup time?

It runs in parallel with the tunnel-resolve step the first time. If loopback wins, we cancel the tunnel resolution. Subsequent calls in the same session skip the probe and use the cached choice for 30 seconds.