Skip to content

feat: reverse proxy as a first-class front door (0.12.0)#14

Merged
AltanS merged 4 commits into
mainfrom
feat/reverse-proxy-ingress
Jul 17, 2026
Merged

feat: reverse proxy as a first-class front door (0.12.0)#14
AltanS merged 4 commits into
mainfrom
feat/reverse-proxy-ingress

Conversation

@AltanS

@AltanS AltanS commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Summary

Makes a reverse proxy (Caddy/Nginx) a fully supported alternative to tailscale serve as Collie's single hardened front door — documented as README Variant C.

Builds on and supersedes #13 by @diogenesc: their commit is included here with authorship preserved (ad5833a, cherry-picked onto 0.11.1), plus the hardening the posture change needs.

Changes

From #13 (@diogenesc):

  • COLLIE_SKIP_SERVE=1collie-ctl.sh never touches tailscale serve (guards inside cmd_serve/status, so start/uninstall are covered too)

Hardening on top:

  • cmd_unserve always attempts teardown — skipping it under the flag would strand a stale serve mapping still publishing the app (teardown was already idempotent)
  • Bridge startup warning when COLLIE_TRUSTED_USER is set under COLLIE_SKIP_SERVE=1: without tailscale serve, Tailscale-User-Login is never injected and checkAccess only enforces it when present — the identity gate is silently inert. The warning points at COLLIE_DEVICE_HEADER (per-device auth, designed for proxy-injected headers) as the replacement. The Variant-A empty-trustedUser warning no longer fires in skip-serve mode.
  • Startup warnings extracted to a pure startupWarnings(cfg) — unit-tested under bun test (6 new cases)
  • COLLIE_PUBLIC_URL — status banner shows the operator's real proxy URL

Docs:

  • New Variant C — reverse proxy as the only front door (no Tailscale): Caddy example, required env (COLLIE_PUBLIC_HOSTS, COLLIE_ALLOWED_ORIGINS, COLLIE_DEVICE_HEADER+allowlist), links Variant B's four proxy requirements verbatim
  • Posture reframed everywhere: "tailscale serve is the sole ingress" → "exactly one hardened front door" (README security section + Requirements + Web Push + architecture diagrams, ARCHITECTURE §2/§6, CLAUDE.md). "Never funnel, never a bare port" stays absolute.

Default-path behavior (flag unset) is unchanged; the one legacy-string edit is the publicHosts warning no longer naming COLLIE_SERVE_MODE (which the bridge never read).

Test plan

  • bun run typecheck both sides (root build)
  • root bun run test: 191 pass (was 184; +COLLIE_SKIP_SERVE parsing, +startupWarnings matrix)
  • cd web && bun run test: 719 pass (pre-push hook)
  • bash -n scripts/collie-ctl.sh; banner renders proxy <url> with COLLIE_PUBLIC_URL, placeholder without
  • scripts/check-version.sh ✓ 0.12.0

Closes #13 — superseded with the original commit included.

🤖 Generated with Claude Code

diogenesc and others added 4 commits July 17, 2026 15:20
Adds COLLIE_SKIP_SERVE=1 support to collie-ctl.sh so the bridge can
run behind a reverse proxy (Caddy, Nginx, etc.) without triggering
tailscale serve. The bridge stays on 127.0.0.1 only — your proxy
handles TLS, auth, and public access.

- cmd_serve/cmd_unserve: early return when COLLIE_SKIP_SERVE=1
- print_status_banner: shows 'proxy' instead of 'tailnet' when skipped
- cmd_status: skips tailscale serve status check when skipped
- .env.example: documents the new variable
- Version bumped to 0.12.0 (minor — new feature)

(cherry picked from commit 00307cd)
…warning, COLLIE_PUBLIC_URL

- cmd_unserve always attempts teardown (stale serve mapping is a security
  hazard, and teardown was already idempotent)
- bridge warns at startup when COLLIE_TRUSTED_USER is set under
  COLLIE_SKIP_SERVE=1 (Tailscale-User-Login is never injected, so the
  identity gate is inert — point at COLLIE_DEVICE_HEADER instead); the
  Variant-A empty-trustedUser warning no longer fires in that mode
- startup warnings extracted to pure startupWarnings(cfg) for bun test
- COLLIE_PUBLIC_URL shows the operator's real proxy URL in the status banner

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
README gains Variant C (reverse proxy as the only front door, Caddy
example, env block); 'sole ingress' becomes 'exactly one hardened front
door' across README, ARCHITECTURE §2/§6, and the CLAUDE.md posture line.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@AltanS
AltanS merged commit 140a243 into main Jul 17, 2026
1 check passed
@AltanS
AltanS deleted the feat/reverse-proxy-ingress branch July 17, 2026 13:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants