feat(FN-2519): add remote tunnel manager for settings sync

- Add remote-access contracts, provider adapters, and a tunnel process manager with lifecycle handling
- Wire tunnel manager into ProjectEngine startup/shutdown flow and export new remote-access modules
- Update settings modal UX for remote auth URLs, including wrapping and related UI test coverage
- Document tunnel manager behavior and remote settings sync details in architecture, CLI, and settings docs
This commit is contained in:
Fusion
2026-04-26 02:20:28 -07:00
committed by gsxdsm
parent 7527a7ddaf
commit cb1da0c659
16 changed files with 1234 additions and 28 deletions

View File

@@ -351,13 +351,17 @@ Implemented in `agent-heartbeat.ts`:
- `PeerExchangeService` (`peer-exchange-service.ts`) — peer sync orchestration
### Remote access runtime
- `remote-access/remote-access-manager.ts` supervises tunnel lifecycle with non-blocking child processes
- Provider adapters:
- `remote-access/providers/tailscale-adapter.ts` (`tailscale serve`, status probes)
- `remote-access/providers/cloudflare-adapter.ts` (`cloudflared tunnel run`, startup URL detection)
- Crash-safe lifecycle: graceful stop (`SIGTERM`), bounded wait, forced kill fallback (`SIGKILL`)
- Restore-on-start guardrails: only when remember-last-running is enabled and provider config/binaries are valid
- Short-lived token registry is in-memory and intentionally ephemeral (clears on process restart)
- `remote-access/tunnel-process-manager.ts` owns tunnel lifecycle orchestration with `spawn`-based, non-blocking process supervision.
- `remote-access/types.ts` defines the runtime contract used by downstream API/TUI/headless layers:
- Providers: `"tailscale" | "cloudflare"`
- Lifecycle states: `"stopped" | "starting" | "running" | "stopping" | "failed"`
- Error codes: `invalid_config`, `start_failed`, `stop_failed`, `switch_failed`, `readiness_timeout`, `process_exit`, etc.
- `remote-access/provider-adapters.ts` provides provider-specific command composition + readiness parsing while enforcing config validation.
- Credential inputs are reference-based (`tokenEnvVar`, `credentialsPath`) and validated without logging raw secret values.
- Redaction is applied to command previews and emitted log lines before publishing status/log events.
- Deterministic stop semantics: graceful shutdown (`SIGTERM`) first, bounded wait, then force-kill fallback (`SIGKILL`).
- Safe provider switching is stop-first: active provider fully stops before target start is attempted; failed starts emit `switch_failed` terminal status.
- `ProjectEngine.start()` instantiates a per-project tunnel manager and exposes it through `getRemoteTunnelManager()` for API/UI consumers.
### Multi-runtime support + IPC
- Runtime contracts: `project-runtime.ts`

View File

@@ -97,6 +97,13 @@ Remote action keys in Settings detail pane:
- `K` request QR payload hand-off
- `R` refresh remote status/snapshot
Engine/runtime remote tunnel semantics used by dashboard + serve + TUI:
- Lifecycle states: `stopped → starting → running → stopping` (or terminal `failed`)
- Start/stop is process-supervised (`spawn`, `SIGTERM`, 5s default timeout, then `SIGKILL`)
- Provider switch is stop-first: the current provider is fully stopped before target startup is attempted
- Failed switch/start emits explicit failure status (`switch_failed` / `invalid_config` / `start_failed`) and never runs both providers concurrently
- Status/log subscribers receive redacted events (token-bearing args/env/log text masked)
QR hand-off behavior in TUI:
- `format="text"`: renders the text payload directly
- `format="image/svg"`: does not render raw SVG in terminal; shows the authenticated URL, expiry metadata, and a fallback instruction to open the URL on phone/browser
@@ -224,6 +231,11 @@ When remote access is enabled/configured, the headless server exposes `/api/remo
control/status endpoints and applies the same hybrid token validation rules for
remote routes (persistent token + optional short-lived token registry).
For programmatic consumers, these endpoints map to the engine tunnel manager contract:
- `getStatus()` for current snapshot
- `start(provider, config)` / `stop()` / `switchProvider(...)`
- subscription hooks for live status and log updates (used by stream/poll clients)
---
## `fn daemon`

View File

@@ -200,6 +200,14 @@ Remote access settings are project-only (stored in `.fusion/config.json`), not g
| `remoteWasRunningOnShutdown` | `boolean` | `false` | Internal state flag persisted on shutdown. |
| `remoteLastStartedProvider` | `"tailscale" \| "cloudflare" \| null` | `null` | Internal last-known running provider for restore decisions. |
Runtime provider config/credential contract (engine remote-access manager):
- The tunnel manager consumes **resolved provider configs** (`TunnelProviderConfig`) from callers; it does not read dashboard form state directly.
- Provider config must include executable + args and may include credential references:
- `tokenEnvVar` (env var name, value sourced from process/config env)
- `credentialsPath` (Cloudflare credentials file path)
- Missing/invalid credential references fail fast with `invalid_config` status/error behavior.
- Secret-bearing values are redacted in command previews and emitted tunnel logs before they are published to subscribers.
Short-lived token bounds are enforced server-side:
- Minimum TTL: `60_000` ms (60s)
- Maximum TTL: `86_400_000` ms (24h)