## Symptom
A tunnel process that dies unexpectedly (crash, OOM, network hiccup, the
24h `maxLifetimeMs` supervision cutoff) leaves `TunnelProcessManager` in
a terminal `failed` state. `handleUnexpectedExit` records the error and
stops — the operator's remote-access tunnel silently goes dark until a
human notices and restarts it.
## Fix
The manager now remembers the desired tunnel (`provider` + `config`)
across the start/switch lifecycle and respawns it after an unexpected
exit with exponential backoff — 1s base doubling to a 30s cap, both
configurable via new `restartBaseDelayMs` / `restartMaxDelayMs` options,
and `autoRestart: false` to opt out. The attempt counter resets once the
tunnel reports ready.
Intentional shutdowns stay shut down: `stop()` and `switchProvider()`
clear the desired tunnel and cancel any pending restart timer.
Hardening that falls out of restart support:
- Exit/spawn-error/readiness-timeout handlers are guarded by
process-handle identity, so a stale `close` event from a superseded
child cannot clobber the state of the restarted tunnel.
- The readiness-timeout path now SIGTERMs the stalled child instead of
leaking it while marking the tunnel failed.
- A synchronous `superviseSpawn` throw now records a redacted
`start_failed` status instead of escaping unhandled.
## Verification
- New tests (fake timers, no real waits): restart after unexpected exit
for both cloudflare and tailscale providers; backoff progression and
delay cap across repeated pre-readiness failures; explicit `stop()`
cancels a pending restart.
- `pnpm --filter @fusion/engine exec vitest run
src/__tests__/tunnel-process-manager.test.ts` — 17/17 pass.
- `pnpm verify:fast` — 14 steps green (scoped typecheck/build + CLI
build + boot smoke).
- Changeset included (`patch`, category `fix`).
## Provenance
This is the tunnel half of a fork-side fix (FN-915) that has been
running in production since July; the merge-blocker-preservation half of
that same fix already landed upstream (present since v0.76.0-beta.0).
Ported onto current `main` — the original patch applied cleanly.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Tunnels now automatically restart after unexpected crashes.
* Restart attempts use increasing delays up to a configurable maximum.
* Automatic recovery is enabled by default and can be configured.
* **Bug Fixes**
* Prevented stale process events from triggering unwanted restarts.
* Explicit stops and provider changes now cancel pending restart
attempts.
* Failed or unready tunnel processes are terminated cleanly.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: v <v@v.speedport.ip>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>