From 0517b21f0733abc90a8ab765f28a7dd42b5727e7 Mon Sep 17 00:00:00 2001 From: Karn Date: Wed, 19 Aug 2026 03:24:52 +0530 Subject: [PATCH 1/2] docs+ux: say out loud that sessions outlive flue flue restart and flue update now report that sessions kept running (suppressed under the FLUE_NO_HOLDER escape hatch, whose sessions still ride snapshots), usage stops promising snapshots where none are needed, and the README gets the durability story in one paragraph. Co-Authored-By: Claude Fable 5 --- README.md | 9 +++++++++ cmd/flue/main.go | 10 +++++++++- cmd/flue/update.go | 6 ++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a15b157..f5ad50c 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,15 @@ not lost, but nothing runs until it wakes. The full version is at [flue.sh/docs/setup](https://flue.sh/docs/setup). +## Sessions outlive flue + +Every session runs in its own small holder process, not inside the daemon. +Updating flue, restarting it, even the daemon crashing outright: the shells +and agents keep running, and the next daemon picks them back up with their +scrollback where you left it. A machine reboot is the one thing that ends a +session, and even then flue brings it back with its history, a fresh shell, +and the command that resumes the agent conversation it was in. + ## The CLI ``` diff --git a/cmd/flue/main.go b/cmd/flue/main.go index 2441962..f1a9d85 100644 --- a/cmd/flue/main.go +++ b/cmd/flue/main.go @@ -114,7 +114,7 @@ const usageText = `flue — your terminal, as a browser tab flue enable install the login service, start the daemon, open the UI flue disable remove the login service - flue restart restart the daemon; live sessions ride across on snapshots + flue restart restart the daemon; sessions keep running through it flue status daemon, login service, and session diagnostics flue relay setup deploy a relay to your own Cloudflare account flue relay join URL --secret S --fleet K point this machine at an existing relay @@ -1611,6 +1611,14 @@ func runRestart(w io.Writer, wait time.Duration) error { return err } fmt.Fprintf(w, " ✓ daemon running on 127.0.0.1:%d\n", port) + // True by construction, not aspiration: sessions live in their own + // holder processes, and the daemon that just restarted reattached to + // them on its way up. The one exception is a daemon running with + // FLUE_NO_HOLDER, whose sessions still ride snapshots; that escape + // hatch's users chose the old behaviour and know what it costs. + if os.Getenv("FLUE_NO_HOLDER") == "" { + fmt.Fprintf(w, " ✓ sessions kept running; tabs reconnect on their own\n") + } return nil } diff --git a/cmd/flue/update.go b/cmd/flue/update.go index 50434c7..0d79d74 100644 --- a/cmd/flue/update.go +++ b/cmd/flue/update.go @@ -365,6 +365,12 @@ func restartForUpdate(w io.Writer, latest string) error { return err } fmt.Fprintf(w, " ✓ daemon restarted, running flue %s on 127.0.0.1:%d\n", got, port) + // Same honesty rule as runRestart: holder-backed sessions rode + // across the bounce; only the FLUE_NO_HOLDER escape hatch still + // pays for an update with its sessions. + if os.Getenv("FLUE_NO_HOLDER") == "" { + fmt.Fprintf(w, " ✓ sessions kept running; tabs reconnect on their own\n") + } return nil } } From 241f500919d87dadd866270c8ac8e212ebbf9df0 Mon Sep 17 00:00:00 2001 From: Karn Date: Wed, 19 Aug 2026 10:57:37 +0530 Subject: [PATCH 2/2] docs(site+web): the durability story where users actually read How-it-works reframes ownership honestly (sessions own their shells, and outlive the daemon too), the FAQ answers the update question directly, and the update dialog stops implying a restart costs anything. Co-Authored-By: Claude Fable 5 --- site/src/routes/docs.faq.tsx | 17 +++++++++++++++-- site/src/routes/docs.how-it-works.tsx | 13 +++++++++---- web/src/components/update-notice.tsx | 3 ++- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/site/src/routes/docs.faq.tsx b/site/src/routes/docs.faq.tsx index af41bab..fc74fbd 100644 --- a/site/src/routes/docs.faq.tsx +++ b/site/src/routes/docs.faq.tsx @@ -119,8 +119,21 @@ const QUESTIONS: { q: string; verdict: string; body?: ReactNode }[] = [ verdict: 'Nothing. Closing detaches the session. It does not kill it.', body: (

- The daemon owns the shell and its scrollback, so the build keeps running. Reattach from any - device and it replays what you missed. + The session owns its shell and its scrollback, so the build keeps running. Reattach from + any device and it replays what you missed. +

+ ), + }, + { + q: 'What happens to my sessions when flue updates or restarts?', + verdict: 'Nothing. Sessions outlive the daemon.', + body: ( +

+ Every session runs in its own small holder process, separate from the daemon. Update flue, + restart it, even have it crash: the shells and agents keep running, and the next daemon + picks them back up with their scrollback intact. A machine reboot is the one thing that + ends a session, and even then flue brings it back with its history, a fresh shell, and the + command that resumes the agent conversation it was in.

), }, diff --git a/site/src/routes/docs.how-it-works.tsx b/site/src/routes/docs.how-it-works.tsx index b25498e..3daea42 100644 --- a/site/src/routes/docs.how-it-works.tsx +++ b/site/src/routes/docs.how-it-works.tsx @@ -26,10 +26,11 @@ const COMMANDS: { cmd: string; what: string }[] = [ function HowItWorks() { return ( -
+
- A small Go daemon holds the terminals and their scrollback. Everything that draws them is - a client, including the tab you are reading this in. + Every session is a small process of its own that holds the shell and its scrollback. A Go + daemon coordinates them, and everything that draws them is a client, including the tab + you are reading this in.

The whole product rests on that one move. Normally a shell belongs to the window that @@ -38,9 +39,13 @@ function HowItWorks() {

Closing the tab does not kill the session. It only detaches it. The build keeps running, - the agent keeps working, the SSH session stays up. Reattach and the daemon replays the + the agent keeps working, the SSH session stays up. Reattach and the session replays the scrollback you missed.

+

+ It outlives the daemon too. Updating flue, restarting it, even a crash: sessions keep + running through all of it, and the next daemon picks them back up where they were. +

diff --git a/web/src/components/update-notice.tsx b/web/src/components/update-notice.tsx index 70a36c9..cf1fce3 100644 --- a/web/src/components/update-notice.tsx +++ b/web/src/components/update-notice.tsx @@ -250,7 +250,8 @@ function UpgradeDialog({ {latest ? `Update to flue ${latest}` : 'Update flue'} Replacing the binary is something only this machine can do, so it happens in a terminal - rather than here. Whichever way flue was installed, run its line and restart the daemon.{' '} + rather than here. Whichever way flue was installed, run its line and restart the + daemon. Your sessions keep running through the restart.{' '} {url !== undefined && ( <>