Skip to content

feat(dev-server): opt-in Tailscale preview via dev.json config - #233

Open
theshwal wants to merge 1 commit into
co-l:developfrom
theshwal:feature/tailscale-preview
Open

feat(dev-server): opt-in Tailscale preview via dev.json config#233
theshwal wants to merge 1 commit into
co-l:developfrom
theshwal:feature/tailscale-preview

Conversation

@theshwal

@theshwal theshwal commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds an optional Tailscale preview to the dev server, enabled per project through a new tailscaleExpose boolean in .openfox/dev.json (default false).

The preview starts automatically after a successful dev server start, remains non-blocking on Tailscale errors, and is cleaned up on stop, crash, or stopAll — without touching pre-existing Serve entries.

Feature

  • Opt-in via tailscaleExpose in .openfox/dev.json — defaults to false; existing projects are unaffected.
  • New "Expose via Tailscale" checkbox added to the Dev Server Config modal, under Hot Reload and Disable inspect feedback. The checkbox state is persisted with the rest of the dev-server config.
  • No-op on toggle while running — the preview lifecycle is bound to dev-server Start/Restart, not to live toggling.
  • Auto-launch on successful dev server start when tailscaleExpose: true.
  • Auto-cleanup on Stop / crash / stopAll (no orphan entries).
  • Read-only Tailnet URL in the footer when the preview is active (no separate Expose / Retry / Stop buttons in the UI).
  • Non-blocking on errors — Tailscale failures (binary missing, daemon stopped, operator permission denied) are reported as tailscalePreview.status: 'error' and do not affect dev server state.

Mechanism

  • foreground tailscale serve only — entry disappears naturally with the controlling process. No --bg fallback.
  • stdout URL is primary source for the Tailnet URL (matches Tailscale's actual exposed entry).
  • tailscale serve status --json is used only to:
    1. Pick a free HTTPS port (skipping root Web/TCP and foreground-owned Foreground[*].Web/TCP entries).
    2. Confirm stabilization after spawn.
    3. Verify disappearance after kill.
  • Targeted cleanup when an entry persists (tailscale serve --https=<port> off) — never reset. Pre-existing entries (e.g. 443 → 10369) are never touched.

Tests

  • Unit (src/server/dev-server/tailscale-preview.test.ts) — 25 tests covering foreground spawn, stdout URL parsing, Foreground[*] status JSON normalization (with a host-agnostic redacted fixture), targeted inverse, never-reset guarantee, stopAll.
  • Unit (src/server/dev-server/manager.test.ts)tailscaleExpose config round-trip, auto-launch fire-and-forget (never blocks on Tailscale error), no auto-launch when absent or false, idempotent re-call, stop-coupled cleanup, stopAll cleanup.
  • E2E (Playwright)e2e-playwright/tailscale-preview.spec.ts with a hard gate: tests skip cleanly when OPENFOX_TS_NODE_HOST / OPENFOX_TS_NODE_IP are unset or when the tailscale binary / backend is unavailable, so the standard CI suite is not impacted.
    • Test A — standalone HTTP fixture, accessed via real Tailscale MagicDNS HTTPS with the real cert accepted. URL generated, served, torn down. Pre-existing 443 entry untouched.
    • Test B — Vite backend, isolates Blocked request ... allowedHosts (HTTP 403) as a Vite backend compat limitation, not a feature bug.

Verification

  • Real E2E PASS with MagicDNS / HTTPS / Chromium headed under Xvfb + ffmpeg x11grab — captured window screenshot showing the Tailscale URL in the address bar and the dev-server content (TAILSCALE_PREVIEW_TEST_A) loaded in the same image. Pre-existing 443 entry intact before/after. Screenshots persisted at /home/hermesy/openfox-verify/feature-tailscale-preview-v1.1/ (not committed).
  • QA_PASS and INT_PASS on SHA f28999cf43234ec55b4d1796cbd8d1d334fabbfc.
  • All unit + E2E tests green at that SHA.

Known limitation (backend compat, not a feature bug)

  • Vite allowedHosts: when the dev server is Vite (default since v5), Vite returns HTTP 403 Blocked request. This host (...) is not allowed because the Host header it sees is the Tailscale MagicDNS hostname. The Tailscale mechanism itself works correctly; users need to add the tailnet host to server.allowedHosts in vite.config.{js,ts}. Example: see web/vite.config.local.ts.example which is already in the repo.

Out of scope

  • tailscale funnel (public HTTPS exposure)
  • Hot toggle of the checkbox while the dev server is running
  • Configurable remote port
  • Multi-preview-per-workdir
  • Auto-reconnect on tailscaled restart

AI-Enhanced Development

  • AI Models: MiniMax-M3 (developed by MiniMax) assisted the development workflow, code generation, test design, and verification harness. All architectural decisions, code review, and final validation were performed by a human contributor.

Cache Impact

  • No — no system prompts, tool definitions, skills, or other cached context were affected.

Add an optional Tailscale preview to dev servers, enabled per project
through `tailscaleExpose` in `.openfox/dev.json`.

The preview starts automatically after a successful dev server start,
remains non-blocking on Tailscale errors, and is cleaned up on stop,
crash, or stopAll without touching pre-existing Serve entries.

Add config UI, read-only Tailnet status, unit coverage, and gated
Playwright E2E verification for Tailscale-enabled environments.
@theshwal theshwal closed this Aug 9, 2026
@theshwal theshwal reopened this Aug 9, 2026
@theshwal

theshwal commented Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

CI status snapshot — f28999cf

Check Status
lint ✅ pass
typecheck ✅ pass
duplicate ✅ pass
test ✅ pass
ai-disclosure ✅ pass
windows ❌ fail

The only failing check is windows, on the pre-existing flaky test fires close event after killing process group — unrelated to this PR's diff. The test pass-rate on this runner is normally green; the failure is environmental (Windows process-group kill race).

QA_PASS and INT_PASS confirmed on f28999cf43234ec55b4d1796cbd8d1d334fabbfc.

A re-run of the windows job should target the same SHA (no new commit needed).

@theshwal theshwal closed this Aug 11, 2026
@theshwal theshwal reopened this Aug 11, 2026
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.

1 participant