Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
216 changes: 216 additions & 0 deletions docs/docs/durable-sessions.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
---
sidebar_position: 3.5
id: "durable-sessions"
title: "Durable Sessions"
---

import { VersionBadge } from "@site/src/components/versionbadge";

# Durable Sessions <VersionBadge version="v0.14" />

Keep your remote SSH shell sessions alive through network changes, computer sleep, and Wave restarts.

## Overview

:::info Remote Connections Only
Durable sessions are designed for **remote SSH connections only**. Local terminals and WSL connections use standard sessions, as they're not affected by network interruptions and remain active as long as Wave is running.
:::

Durable sessions protect your terminal state when working with remote SSH connections, similar to tmux or screen but built directly into Wave. Unlike standard SSH sessions that terminate when the connection drops, durable sessions maintain your:

- **Shell state** - Current directory, environment variables, and shell history
- **Running programs** - Background jobs and long-running commands continue executing
- **Terminal history** - Full scrollback buffer preserved across reconnections

Durable sessions automatically reconnect when your connection is restored, picking up right where you left off.

## How It Works

When you start a durable session, Wave launches a persistent job manager on the remote server. This manager:

1. Keeps your shell process running independently of the Wave connection
2. Buffers terminal output while disconnected
3. Automatically reattaches when you reconnect
4. Survives Wave restarts and network interruptions

The session continues running on the remote server even if you close Wave, put your computer to sleep, or switch networks.

## Session Status Indicator

The shield icon in your terminal header shows the current session status:

| Icon | Status | Description |
|------|--------|-------------|
| <i className="fa-sharp fa-regular fa-shield text-muted"></i> | Standard Session | Connection drops will end the session |
| <i className="fa-sharp fa-solid fa-shield text-sky-500"></i> | Durable (Attached) | Session is protected and connected |
| <i className="fa-sharp fa-solid fa-shield text-sky-300"></i> | Durable (Detached) | Session running, currently disconnected |
| <i className="fa-sharp fa-solid fa-shield text-muted"></i> | Durable (Awaiting) | Configured but not yet started |

Comment on lines +42 to +48
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Check Font Awesome configuration
rg -l "font-awesome|fontawesome|@fortawesome" docs/ --include="*.json" --include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" --include="*.mdx"

Repository: wavetermdev/waveterm

Length of output: 505


🏁 Script executed:

# Check for Tailwind/CSS configuration files
fd -e "tailwind.config" -e "postcss.config" -e "docusaurus.config" docs/

Repository: wavetermdev/waveterm

Length of output: 389


🏁 Script executed:

# Search for color utility class usage
rg "text-sky-500|text-sky-300|text-muted" docs/ --include="*.mdx" --include="*.tsx" --include="*.css"

Repository: wavetermdev/waveterm

Length of output: 505


🏁 Script executed:

# Check for similar icon patterns in docs
rg "className=\"fa-" docs/ --include="*.mdx" | head -20

Repository: wavetermdev/waveterm

Length of output: 505


🏁 Script executed:

# Look at the root docusaurus config
cat docusaurus.config.js 2>/dev/null | head -50

Repository: wavetermdev/waveterm

Length of output: 46


🏁 Script executed:

# First, explore the docs directory structure
ls -la docs/ | head -20

Repository: wavetermdev/waveterm

Length of output: 1055


🏁 Script executed:

# Find configuration files in docs
fd . docs/ -name "*.config.*" -o -name "docusaurus*" -o -name "tailwind*" -o -name "postcss*"

Repository: wavetermdev/waveterm

Length of output: 294


🏁 Script executed:

# Search for Font Awesome configuration (corrected rg syntax)
rg "font-awesome|fontawesome|@fortawesome" docs/

Repository: wavetermdev/waveterm

Length of output: 50377


🏁 Script executed:

# Search for color utility usage in mdx files
rg "text-sky-500|text-sky-300|text-muted" docs/

Repository: wavetermdev/waveterm

Length of output: 679


🏁 Script executed:

# Check for Font Awesome icons in use
rg "fa-sharp|fa-solid|fa-regular" docs/ | head -20

Repository: wavetermdev/waveterm

Length of output: 5691


🏁 Script executed:

# Look for CSS or Tailwind imports in the main docusaurus config
cat docusaurus.config.js | grep -A 5 -B 5 "css\|theme\|tailwind" || echo "No matches found"

Repository: wavetermdev/waveterm

Length of output: 150


🏁 Script executed:

# Read the Docusaurus config to see Font Awesome and CSS setup
head -100 docs/docusaurus.config.ts | cat -n

Repository: wavetermdev/waveterm

Length of output: 4140


🏁 Script executed:

# Search for Tailwind configuration
rg "tailwind" docs/

Repository: wavetermdev/waveterm

Length of output: 163


🏁 Script executed:

# Look for global CSS files
find docs -name "*.css" -type f | head -20

Repository: wavetermdev/waveterm

Length of output: 343


🏁 Script executed:

# Check if there's a global styles directory
ls -la docs/src/css/ 2>/dev/null || echo "No src/css directory"

Repository: wavetermdev/waveterm

Length of output: 235


🏁 Script executed:

# Search for where text-sky or text-muted might be defined
rg "text-muted|text-sky" docs/

Repository: wavetermdev/waveterm

Length of output: 679


🏁 Script executed:

# Read the custom.scss file
cat docs/src/css/custom.scss

Repository: wavetermdev/waveterm

Length of output: 2862


🏁 Script executed:

# Check if Tailwind is in package.json dependencies
grep -A 5 -B 5 "tailwind" docs/package.json

Repository: wavetermdev/waveterm

Length of output: 46


🏁 Script executed:

# Look at what's actually loaded in docusaurus config - the stylesheets section
sed -n '100,200p' docs/docusaurus.config.ts | cat -n

Repository: wavetermdev/waveterm

Length of output: 4354


The Tailwind color utility classes are not configured in the docs site.

Font Awesome icons are properly configured and will render. However, the color classes (text-muted, text-sky-500, text-sky-300) do not exist in the docs CSS. Tailwind is not set up in the Docusaurus configuration, so these utilities won't apply any styling. The icons will appear but without the intended colors.

Use standard CSS color classes available in Docusaurus (like those defined in custom.scss) or define the color utilities explicitly in src/css/custom.scss.

🤖 Prompt for AI Agents
In `@docs/docs/durable-sessions.mdx` around lines 42 - 48, The MDX table uses
Tailwind class names (text-muted, text-sky-500, text-sky-300) that don't exist
in the docs CSS; add explicit CSS definitions for those utilities in your docs
stylesheet (e.g., create rules for .text-muted, .text-sky-500, .text-sky-300 in
src/css/custom.scss with the intended color values) so the icons in
durable-sessions.mdx render with the expected colors, or alternatively replace
those className values in durable-sessions.mdx with whatever existing
Docusaurus/custom classes you already have.

Hover over the shield icon to see detailed status information and available actions.

## Configuration

Durable sessions can be configured at three levels, with more specific settings overriding general ones:

### Global Settings (Lowest Priority)

Set the default for all SSH connections in your `settings.json`:

```json
{
"term:durable": true
}
```

### Connection Settings (Medium Priority)

Configure durability per connection in your `connections.json`:

```json
{
"connections": {
"user@host": {
"term:durable": true
}
}
}
```

### Block Settings (Highest Priority)

Override for individual terminal blocks through:

- **Context Menu**: Right-click terminal → Advanced → Session Durability
- **Flyover Actions**: Click shield icon → "Restart as Durable" or "Restart as Standard"
- **Command Line**: Use `wsh setmeta term:durable=true` or `wsh setmeta term:durable=false`

Configuration hierarchy (highest to lowest priority):
1. Block-level setting
2. Connection-level setting
3. Global setting

### Default Behavior

- **SSH connections**: Durable sessions disabled by default (opt-in via configuration)
- **Local terminals**: Always use standard sessions (durability not applicable)
- **WSL connections**: Always use standard sessions (durability not applicable)

## Switching Between Modes

### Standard to Durable

1. Hover over the regular shield icon
2. Click **"Restart as Durable"** in the flyover
3. Your session will restart with durability enabled

Or use the context menu:
- Right-click terminal → Advanced → Session Durability → Restart Session in Durable Mode

### Durable to Standard

1. Access the terminal context menu (right-click)
2. Navigate to Advanced → Session Durability
3. Select **"Restart Session in Standard Mode"**

:::warning Switching Modes Restarts the Session
Converting between standard and durable modes requires restarting the shell. Any running processes in the current session will be terminated.
:::

## Session States

### Attached
Your terminal is connected to the remote session. You can interact with the shell and see real-time output.

### Detached
Connection lost, but the session continues running on the remote server. Wave will automatically reconnect when possible. Any commands you ran continue executing.

### Awaiting Start
Session configured for durability but not yet started. Click "Start Session" or run a command to begin.

### Starting
Job manager is initializing on the remote server. The session will become attached shortly.

### Ended
Session has terminated. Common reasons:
- **Exited**: Shell was closed normally (e.g., typed `exit`)
- **Lost**: Session not found on server (may have been terminated or system rebooted)
- **Failed to Start**: Job manager encountered an error during initialization

Click "Restart Session" to start a new durable session, or "Restart as Standard" to switch modes.

## Use Cases

### Long-Running Commands
Start a build, deployment, or data processing job and close your laptop. The command continues executing, and you can check on it later.

```bash
# Start a long build
./build.sh

# Close your laptop, get coffee
# Later: reconnect and see the completed output
```

### Unstable Networks
Work from a café, train, or cellular connection. Brief disconnections won't terminate your session or lose your work.

### Multiple Locations
Start work on your desktop, continue on your laptop. Your session and its state are preserved on the remote server.

### System Maintenance
Wave updates, restarts, or crashes won't interrupt your remote work. Reconnect and resume immediately.

## Session Lifecycle

Durable sessions are tied to the terminal block in Wave. The session will be terminated when you:

- **Close the block**: Closes the terminal and terminates the remote session
- **Switch connections**: Changing the connection on a block terminates the old session
- **Delete the workspace/tab**: Removes the block and terminates associated sessions

### Cleanup Behavior

If you close a block while **disconnected**, the remote session continues running until the next reconnection. When Wave reconnects to that server, it will automatically clean up any orphaned sessions from closed blocks.

This ensures that remote sessions don't accumulate on your servers when you close terminals while offline.

## Limitations

- **Local terminals**: Not applicable (already persistent with your local machine)
- **WSL connections**: Not applicable (WSL sessions managed by Windows)
- **Network latency**: Detached sessions buffer output; reconnecting may take a moment to sync
- **Server resources**: Each durable session maintains a lightweight Go process on the remote server for session management

## Troubleshooting

### Session Shows as "Lost"
The session was terminated on the remote server, possibly due to:
- Server reboot
- Manual termination of the job manager process
- Remote system running out of resources

**Solution**: Click "Restart Session" to start a new durable session.

### Session Won't Reconnect
Verify that:
- Your SSH connection to the server is working (check the connection status)
- The job manager process is still running on the remote server

**Try**: Right-click terminal → Advanced → Force Restart Controller

### "Failed to Start" Error
The job manager couldn't initialize on the remote server. Check the error message for specific details.

**Try**: Restart the session. If the issue persists, file a bug report with the error details.
Comment on lines +201 to +204
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Minor: "specific details" is redundant.

"Details" alone suffices here.

📝 Suggested wording
-The job manager couldn't initialize on the remote server. Check the error message for specific details.
+The job manager couldn't initialize on the remote server. Check the error message for details.
🧰 Tools
🪛 LanguageTool

[style] ~202-~202: This phrase is redundant. Consider writing “details”.
Context: ...ote server. Check the error message for specific details. Try: Restart the session. If the ...

(SPECIFIC_DETAILS)

🤖 Prompt for AI Agents
In `@docs/docs/durable-sessions.mdx` around lines 201 - 204, Update the sentence
under the "Failed to Start" Error section to remove the redundant word
"specific" so it reads "...Check the error message for details." Locate the
heading "Failed to Start" and the sentence "Check the error message for specific
details." and replace it with the shorter wording; ensure surrounding context
(the "Try" suggestion and restart/bug report guidance) remains unchanged.


:::info Technical Details
Durable sessions use Unix domain sockets on the remote server to maintain persistent connections between the shell and Wave's job manager. The job manager process runs independently and survives SSH disconnections.
:::

## Privacy & Security

- Durable sessions run entirely on your remote servers
- All data is transmitted over SSH between your local Wave instance and the remote machine
- No open ports on the remote machine - communication happens through your existing SSH connection
- When disconnected, output is buffered locally on the remote machine until you reconnect
- Sessions are isolated per user and use your remote user's permissions
2 changes: 1 addition & 1 deletion frontend/app/block/durable-session-flyover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ function isTermViewModel(viewModel: ViewModel): viewModel is TermViewModel {
}

function handleLearnMore() {
getApi().openExternal("https://docs.waveterm.dev/features/durable-sessions");
getApi().openExternal("https://docs.waveterm.dev/durable-sessions");
}

function LearnMoreButton() {
Expand Down
125 changes: 125 additions & 0 deletions frontend/app/onboarding/onboarding-durable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Copyright 2026, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0

import Logo from "@/app/asset/logo.svg";
import { EmojiButton } from "@/app/element/emojibutton";
import { RpcApi } from "@/app/store/wshclientapi";
import { TabRpcClient } from "@/app/store/wshrpcutil";
import { useState } from "react";
import { CurrentOnboardingVersion } from "./onboarding-common";
import { OnboardingFooter } from "./onboarding-features-footer";

export const DurableSessionPage = ({
onNext,
onSkip,
onPrev,
}: {
onNext: () => void;
onSkip: () => void;
onPrev?: () => void;
}) => {
const [fireClicked, setFireClicked] = useState(false);

const handleFireClick = () => {
setFireClicked(!fireClicked);
if (!fireClicked) {
RpcApi.RecordTEventCommand(TabRpcClient, {
event: "onboarding:fire",
props: {
"onboarding:feature": "durable",
"onboarding:version": CurrentOnboardingVersion,
},
});
}
};

return (
<div className="flex flex-col h-full">
<header className="flex items-center gap-4 mb-6 w-full unselectable flex-shrink-0">
<div>
<Logo />
</div>
<div className="text-[25px] font-normal text-foreground">Durable SSH Sessions</div>
</header>
<div className="flex-1 flex flex-row gap-0 min-h-0">
<div className="flex-1 flex flex-col items-center justify-center gap-8 pr-6 unselectable">
<div className="flex flex-col items-start gap-6 max-w-md">
<div className="flex h-[52px] ml-[-4px] pl-3 pr-4 items-center rounded-lg bg-hover text-[18px]">
<i className="fa-sharp fa-solid fa-shield text-sky-500" />
<span className="font-bold ml-2 text-primary">Your SSH Sessions, Protected</span>
</div>

<div className="flex flex-col items-start gap-4 text-secondary">
<p>Close your laptop, switch networks, restart Wave — your remote sessions keep running.</p>

<div className="flex items-start gap-3 w-full">
<i className="fa-sharp fa-solid fa-link text-accent text-lg mt-1 flex-shrink-0" />
<p>Shell state, running programs, and terminal history are all preserved</p>
</div>

<div className="flex items-start gap-3 w-full">
<i className="fa-sharp fa-solid fa-rotate text-accent text-lg mt-1 flex-shrink-0" />
<p>Sessions automatically reconnect when your connection is restored</p>
</div>

<div className="flex items-start gap-3 w-full">
<i className="fa-sharp fa-solid fa-box text-accent text-lg mt-1 flex-shrink-0" />
<p>Buffered output streams back in — you never miss a line</p>
</div>

<p className="italic">
All the persistence of tmux, built into your terminal. Look for the shield icon to
enable durability on any SSH session.
</p>

<EmojiButton emoji="🔥" isClicked={fireClicked} onClick={handleFireClick} />
</div>
</div>
</div>
<div className="w-[2px] bg-border flex-shrink-0"></div>
<div className="flex items-center justify-center pl-6 flex-shrink-0 w-[400px]">
<div className="flex flex-col gap-6 text-secondary">
<div className="text-lg font-semibold text-foreground">Session States</div>

<div className="flex items-start gap-3">
<i className="fa-sharp fa-solid fa-shield text-sky-500 text-xl mt-0.5" />
<div>
<div className="font-semibold text-foreground">Attached</div>
<div className="text-sm">Session is protected and connected</div>
</div>
</div>

<div className="flex items-start gap-3">
<i className="fa-sharp fa-solid fa-shield text-sky-300 text-xl mt-0.5" />
<div>
<div className="font-semibold text-foreground">Detached</div>
<div className="text-sm">Session running, currently disconnected</div>
</div>
</div>

<div className="flex items-start gap-3">
<i className="fa-sharp fa-regular fa-shield text-muted text-xl mt-0.5" />
<div>
<div className="font-semibold text-foreground">Standard</div>
<div className="text-sm">Connection drops will end the session</div>
</div>
</div>

<div className="mt-4 p-4 bg-hover rounded-lg border border-border/50">
<div className="text-sm">
<div className="font-semibold text-foreground mb-2">Common use cases:</div>
<ul className="space-y-1.5 ml-2">
<li>• Alternative to tmux or screen</li>
<li>• Long-running builds and deployments</li>
<li>• Working from unstable networks</li>
<li>• Surviving Wave restarts</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<OnboardingFooter currentStep={2} totalSteps={4} onNext={onNext} onPrev={onPrev} onSkip={onSkip} />
</div>
);
};
Loading
Loading