expose-servicelisted
Install: claude install-skill mickzijdel/dev-hooks
# Exposing an internal service
Four decisions, in order: **who may reach it**, **how you avoid locking yourself out**, **what
proves it works**, and **what happens on reboot**. Skipping the second and fourth is how a
five-minute change turns into a trip to the server room.
## 1. Pick the exposure
Pick the narrowest row that satisfies the audience. Widening later is cheap; narrowing after
people have bookmarked a URL is not.
| Audience | Mechanism | Gives you |
|---|---|---|
| Just you, right now, once | `ssh -L` local forward, or `ssh -D` + `curl -x socks5h://` | Nothing persistent to clean up or forget |
| You + tailnet members | **Tailscale Serve** on a proxy node | Real LE cert, no port opened, origin never exposed |
| Same, but several services want clean names | **Tailscale VIP Services** (`svc:<name>`) | `https://<name>.<tailnet>.ts.net` per service, one proxy node |
| Anyone with an identity you can gate | **Cloudflare Tunnel + Access** | Public URL, no inbound port, SSO in front |
| Genuinely public | Reverse proxy + real cert at a public edge | Full control, and full responsibility for hardening |
Two rules that override the table:
- **A proxy node does not need a subnet route.** If the proxy dials the origin from its own
network stack, routing the whole subnet buys nothing and widens blast radius. Don't advertise
a subnet just to reach one web UI.
- **An identity gate at the edge is not a gate on the origin.** Cloudflare Access is enforced
by Cloudflare.