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:
elide expose 3000Elide 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:
elide expose 127.0.0.1:8080The 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:
elide expose 3000 --method tailscaleThis 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:
elide expose 3000 --method acme --domain myapp.example.com --acme-email me@example.comElide 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:
elide expose 3000 --method self-signedThis 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:
elide expose 3000 --method local --port 8080Useful 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):
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
| Method | TLS | DNS | Requirements |
|---|---|---|---|
auto | Auto-detect | Varies | None |
tailscale | Tailscale-issued | MagicDNS (*.ts.net) | tailscaled running |
acme | Let's Encrypt | Your domain | —domain + DNS A record |
self-signed | Ephemeral self-signed | Local IP / hostname | None |
local | None (plain HTTP) | None | None |
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:
elide expose 3000 --persistThe command returns immediately after the sidecar confirms the exposure is live. The proxy keeps running independently of your terminal session.
Manage persistent exposures:
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:
elide share ./distThis serves the directory and exposes it to the internet in one step. Add --dev for live reload during development:
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
| Key | Action |
|---|---|
q | Quit |
Tab | Toggle focus between Requests and Logs |
j / k or Arrow keys | Scroll requests |
PgUp / PgDn | Page through requests |
Esc | Reset focus to Requests |
--no-tui for plain log output, or --json for structured newline-delimited 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]list, stop , tui [name]
| Flag | Default | Description |
|---|---|---|
—method | auto | TLS method: auto, tailscale, acme, self-signed, or local |
—domain | none | Custom domain for ACME certificate provisioning |
—acme-email | none | Contact email for ACME account registration |
—acme-staging | false | Use ACME staging environment (no rate limits) |
—port | 443 | Public-facing listen port |
—listen | 0.0.0.0 | Listen address for the public proxy |
—persist | false | Run via sidecar; returns immediately after setup |
—no-qr | false | Suppress QR code output |
—no-tui | false | Disable interactive TUI; use plain log output |
—json | false | Emit structured JSON lines to stdout |
—no-websocket | false | Disable WebSocket proxying |
—admin-port | none | Port for the admin API |
—reactors | 1 | Number of reactor (event loop) threads |
—workers | 0 (auto) | Worker threads across all reactors |
elide share
elide share [OPTIONS] <DIR>| Flag | Default | Description |
|---|---|---|
—method | auto | TLS method: auto, tailscale, acme, self-signed, or local |
—domain | none | Custom domain for ACME certificate provisioning |
—acme-email | none | Contact email for ACME account registration |
—port | 443 | Public-facing listen port |
—dev | false | Enable dev mode with live reload on file changes |
—no-qr | false | Suppress QR code output |
—no-tui | false | Disable interactive TUI; use plain log output |
—json | false | Emit 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