Exposing Local Services

You're building something locally and you need someone else to see it. A client reviewing a prototype. A webhook that needs to reach your machine. A mobile device on a different network. You could deploy to staging, wait for CI, and send a link -- or you could run one command and have a public HTTPS URL in seconds.

elide expose creates a reverse proxy that forwards internet traffic to a local port. It handles TLS automatically, prints a QR code you can scan from your phone, and opens a live dashboard showing every request as it flows through. When you're done, Ctrl-C and it's gone.

Quick start

Say you have a dev server running on port 3000:

bash
 elide expose 3000

Elide detects the best available TLS method (Tailscale Funnel if you're on a tailnet, otherwise a self-signed certificate), starts an HTTPS reverse proxy on port 443, and prints output like:

Exposure ready

  URL:     https://your-node.tail1234.ts.net
  Method:  Tailscale Funnel
  Target:  127.0.0.1:3000

  [QR code]

The interactive TUI then takes over, showing live requests, latency percentiles, and a rolling RPS sparkline. Every request that hits the public URL gets forwarded to your local server and back.

If your service listens on a specific address:

bash
 elide expose 127.0.0.1:8080

The target accepts a bare port (3000), a host:port pair (127.0.0.1:8080), or a named host (localhost:3000).

---

TLS methods

Elide supports four ways to terminate TLS. By default (--method auto), it tries them in order and picks the first that works.

Tailscale Funnel -- zero-config public access

If tailscaled is running on your machine, Elide uses Tailscale Funnel to get a public DNS name (e.g. https://your-node.tail1234.ts.net) with a valid TLS certificate, no configuration required:

bash
 elide expose 3000 --method tailscale

This is the best option when you have Tailscale installed. The URL is stable across restarts, the certificate is trusted by all browsers, and there's nothing to configure.

ACME -- your own domain

If you have a domain with DNS pointing at this machine, Elide provisions a Let's Encrypt certificate automatically:

bash
 elide expose 3000 --method acme --domain myapp.example.com --acme-email me@example.com

Elide handles the ACME challenge and certificate lifecycle. Use --acme-staging to test against the Let's Encrypt staging environment without hitting rate limits.

Self-signed -- instant fallback

When neither Tailscale nor ACME is available, Elide generates an ephemeral self-signed certificate. Browsers will show a security warning, but the connection is still encrypted:

bash
 elide expose 3000 --method self-signed

This is the automatic fallback when --method auto can't find a better option. Good enough for quick demos and local testing.

Local -- LAN only, no TLS

Skip TLS entirely and expose on the local network over plain HTTP:

bash
 elide expose 3000 --method local --port 8080

Useful when you just need another device on the same network to reach your server and don't need encryption.

Auto-detection order

When --method auto (the default):

1. Tailscale Funnel -- if tailscaled is running and Funnel is available 2. ACME -- if --domain is specified and Tailscale is unavailable 3. Self-signed -- fallback when nothing else works
MethodTLSDNSRequirements
autoAuto-detectVariesNone
tailscaleTailscale-issuedMagicDNS (*.ts.net)tailscaled running
acmeLet's EncryptYour domain—domain + DNS A record
self-signedEphemeral self-signedLocal IP / hostnameNone
localNone (plain HTTP)NoneNone
---

Persistent mode

By default, elide expose runs in the foreground and stops when you close the terminal. For long-running exposures, delegate to the Elide sidecar:

bash
 elide expose 3000 --persist

The command returns immediately after the sidecar confirms the exposure is live. The proxy keeps running independently of your terminal session.

Manage persistent exposures:

bash
 elide expose list                # List all active exposures
 elide expose tui my-exposure     # Attach a live TUI to monitor it
 elide expose stop my-exposure    # Tear it down

---

Sharing directories

elide share combines a static file server with automatic TLS exposure in a single command. Instead of running elide serve ./dist and elide expose 3000 separately:
bash
 elide share ./dist

This serves the directory and exposes it to the internet in one step. Add --dev for live reload during development:

bash
 elide share ./dist --dev

---

Interactive TUI

The TUI shows everything happening in real time:

  • Status bar -- public URL, exposure method, state (Starting/Running/Stopping), uptime
  • Requests table -- scrollable list of proxied requests with method, path, status code, and latency
  • Metrics panel -- live RPS, active connections, total requests, p50/p99 latency
  • RPS sparkline -- 60-second rolling throughput graph
  • Logs panel -- structured log output with severity filtering
KeyAction
qQuit
TabToggle focus between Requests and Logs
j / k or Arrow keysScroll requests
PgUp / PgDnPage through requests
EscReset focus to Requests
For scripting and CI, use --no-tui for plain log output, or --json for structured newline-delimited JSON:
json
{"event":"started","url":"https://your-node.tail1234.ts.net","method":"Tailscale Funnel"}
{"method":"GET","url":"/api/health","status": 200,"latency_us": 1234}
{"method":"POST","url":"/api/data","status": 201,"latency_us": 5678}

Pipe to jq, feed to a log aggregator, or consume from a CI script.

---

CLI reference

elide expose

elide expose [OPTIONS] [PORT_OR_ADDR] [COMMAND]
Subcommands: list, stop , tui [name]
FlagDefaultDescription
—methodautoTLS method: auto, tailscale, acme, self-signed, or local
—domainnoneCustom domain for ACME certificate provisioning
—acme-emailnoneContact email for ACME account registration
—acme-stagingfalseUse ACME staging environment (no rate limits)
—port443Public-facing listen port
—listen0.0.0.0Listen address for the public proxy
—persistfalseRun via sidecar; returns immediately after setup
—no-qrfalseSuppress QR code output
—no-tuifalseDisable interactive TUI; use plain log output
—jsonfalseEmit structured JSON lines to stdout
—no-websocketfalseDisable WebSocket proxying
—admin-portnonePort for the admin API
—reactors1Number of reactor (event loop) threads
—workers0 (auto)Worker threads across all reactors

elide share

elide share [OPTIONS] <DIR>
FlagDefaultDescription
—methodautoTLS method: auto, tailscale, acme, self-signed, or local
—domainnoneCustom domain for ACME certificate provisioning
—acme-emailnoneContact email for ACME account registration
—port443Public-facing listen port
—devfalseEnable dev mode with live reload on file changes
—no-qrfalseSuppress QR code output
—no-tuifalseDisable interactive TUI; use plain log output
—jsonfalseEmit structured JSON lines to stdout
---

What's next

  • elide serve -- Full HTTP server with static files, script handlers, and Pkl config
  • elide orb -- Network orchestrator with reverse proxy, load balancing, and Tailscale integration
  • elide fwd -- Forward proxy with MITM TLS interception
  • elide tun -- WireGuard tunnels, Tailscale connectivity, and DERP relay