← back to Tailscale Funnel Research

REPORT.md

107 lines

# Tailscale Serve / Funnel — Mac2 pm2 Fleet Feasibility Report

**Date:** 2026-05-05
**Mac Studio 2:** tailnet IP 100.65.187.120, Tailscale Standalone variant (`io.tailscale.ipn.macsys`)

## TL;DR

- **Tailscale Serve is viable and recommended** — replaces Basic Auth on the 18 tailnet-gated services. Each service: `tailscale serve --bg --https=<port> http://localhost:<port>`; flip 0.0.0.0 → 127.0.0.1.
- **Basic Auth middleware can be stripped entirely.** Serve gates every request behind tailnet membership and injects `Tailscale-User-Login` headers automatically.
- **One hard one-time blocker:** Enable HTTPS Certificates at https://login.tailscale.com/admin/dns first.

## Serve vs. Funnel — do not confuse

| | Tailscale Serve | Tailscale Funnel |
|---|---|---|
| Who can access? | Tailnet members only | Public internet |
| HTTPS auto-cert | Yes | Yes |
| Identity headers | Yes (`Tailscale-User-Login`) | No |
| Port restrictions | Arbitrary | 443/8443/10000 only |
| What we want? | YES | NO |

## Architecture: Before vs. After

### Current (0.0.0.0 + Basic Auth)
- 18 services bound 0.0.0.0, gated by `admin:DWSecure2024!` (in 41+ files)
- Reachable from LAN (192.168.1.x); only Basic Auth blocks
- Rotation = edit 41 files + fleet restart

### Proposed (127.0.0.1 + Tailscale Serve)
- 18 services bound 127.0.0.1 (LAN-dark)
- HTTPS via auto LE cert: `https://stevestudio2s-mac-studio.<tailnet>.ts.net:<port>`
- Tailnet membership IS the auth — no shared password
- Identity headers injected by Serve

## Migration commands

```bash
# ── ONE-TIME PREREQ ──
# 1. Enable HTTPS at https://login.tailscale.com/admin/dns
# 2. Provision cert:
TAILNET=$(tailscale status --json | python3 -c "import sys,json; print(json.load(sys.stdin)['MagicDNSSuffix'])")
tailscale cert "stevestudio2s-mac-studio.${TAILNET}"

# ── REGISTER SERVICES (services stay live during this step) ──
for port in 9762 9763 9706 9707 9871 9873 9875 9890 9891 9892; do
  tailscale serve --bg --https=$port http://localhost:$port
done
for port in 9720 9721 9722 9723 9648 9881 9882 9725; do
  tailscale serve --bg --https=$port http://localhost:$port
done
tailscale serve status   # confirm

# ── PER-SERVICE FLIP (do one at a time) ──
# 1. Edit server.js: '0.0.0.0' → '127.0.0.1', remove Basic Auth middleware
# 2. pm2 restart <svc> --update-env
# 3. curl https://stevestudio2s-mac-studio.${TAILNET}:<port>/healthz
# 4. pm2 save

# ── ROLLBACK (any service, any time) ──
tailscale serve --https=<port> off
# revert code, pm2 restart, done
```

## Excluded (do not migrate)

| Service | Port | Reason |
|---|---|---|
| pd-preview | 9875 | Public Cloudflare tunnel target |
| lawyer-directory-builder | 9701 | Own session/passport auth |
| pd-api | 9874 | loopbackSyntheticAdmin design |
| Claude IDE bridges | 18789/91/92 | Hard rule: never touch |

## Side-by-side

| Dimension | Current | Proposed |
|---|---|---|
| Auth | DWSecure2024! shared password | Tailnet membership |
| Credential count | 1 password in 41+ files | Zero |
| LAN exposure | Yes | No (127.0.0.1) |
| HTTPS | No | Auto LE cert |
| Migration effort | N/A | ~2h for 18 services |
| Reversibility | N/A | 30 sec per-service rollback |

## Risks

| Risk | Severity | Mitigation |
|---|---|---|
| Tailscale daemon stops → all services unreachable from MBP | Medium | Existing pm2-hawk + launchd auto-restart |
| HTTPS prereq missing → mass outage | Medium | Do prereq first, verify cert |
| pd-preview public Cloudflare tunnel | High | Excluded |
| Wave-1 dead-code DWSecure2024! sprawl | Medium | Post-migration cleanup sprint |
| pm2 restart 1-3s 502 from Serve | Low | Acceptable for internal tools |

## Final Recommendation

**Switch.** Migration eliminates DWSecure2024! sprawl at the root. ~2 hours fleet-wide; ~30 sec rollback per service. The 41 hardcoded credential instances become dead code to scrub at leisure.

**Hard prerequisite:** enable HTTPS Certificates in Tailscale admin DNS page before any `tailscale serve --https` command — otherwise zero progress.

## Sources

- https://tailscale.com/docs/features/tailscale-serve
- https://tailscale.com/docs/reference/tailscale-cli/serve
- https://tailscale.com/docs/features/tailscale-funnel
- https://tailscale.com/docs/how-to/set-up-https-certificates
- https://tailscale.com/blog/reintroducing-serve-funnel