Persistent by default: how JetStream KV remembers your workspace
Close the laptop. Kill the terminal. Come back tomorrow — your tabs, panes, agent output, and history are still there. Here's the machinery.
Your terminal has amnesia.
Close the window: gone. Reboot: gone. Even tmux — the gold standard of terminal persistence — only survives as long as its server process does. The moment the machine restarts, your carefully arranged panes, your scrollback, your context: gone.
That was an acceptable trade when panes held shells. It stops being acceptable when panes hold agents. An agent pane isn't just a viewport — it's a conversation with history, a task in progress, accumulated context. Losing it isn't an inconvenience; it's losing work.
So rysh is persistent by default. Not as a plugin, not as a "save session" command you have to remember. It's built into the data layer.
The machinery: an embedded NATS server with JetStream KV
Every rysh session runs its own embedded NATS server, in-process, with JetStream enabled. All coordination between the TUI, workspace, tabs, and panes already flows through NATS as messages — so persistence isn't a separate subsystem bolted on. It's the same bus, with a key-value store attached.
Two JetStream KV buckets do the remembering:
rysh-workspace— one key,state: the tab structure, pane references, lane layouts, and active indices. The shape of your workspace.rysh-panes— one key per pane UUID: that pane's output buffer, status, input mode (shell vs prompt), and last command. The contents of your workspace.
The data lives on disk per session at ~/.local/state/rysh/nats/{session}/. Your machine, your filesystem. Rysh is self-hostable end to end — there's no cloud dependency in the persistence path.
Write strategy: eager where it's cheap, gated where it's hot
Persistence systems die one of two deaths: they write too rarely (and lose data) or too often (and burn the disk). Rysh splits the difference by workload:
Workspace state persists on every change. Created a tab? Persisted. Moved focus? Persisted. Structural changes are low-frequency and small, so eager writes are free — and it means the shape of your workspace is never more than one action stale.
Pane state is time-gated: at most one KV write per pane every 2 seconds. Pane output is the hot path — a chatty build or a streaming agent response can produce hundreds of updates a second. Writing each one to KV would be a write storm for zero benefit. The 2-second gate turns a firehose into a heartbeat, and the worst case on a hard crash is losing the last two seconds of buffer.
On shutdown, everything flushes. A clean exit — or a detach via Ctrl+O d — writes the full state out, no gate.
What restore feels like
On startup, the WorkspaceActor tries to restore from KV before it does anything else. Found state? Your tabs come back, your panes come back, their output buffers and modes come back. No state (or a failed restore)? It bootstraps fresh tabs and panes and moves on — restore failure never bricks a session.
Pair that with the session registry and you get the full lifecycle:
rysh mysession # start (recorded as running)
# ... work, spawn agents, arrange panes ...
Ctrl+O d # detach (recorded as detached, state flushed)
rysh attach mysession # everything is where you left it
And because sessions are addressable even when you're not attached, you can poke a detached session from any shell:
rysh send mysession "run the test suite" --mode prompt
The agent works. The output lands in the pane's buffer. The buffer persists to KV. Whenever you attach — tonight, tomorrow — the transcript is waiting.
Why KV and not a state file?
Fair question. tmux-resurrect proves you can serialize a terminal session to a file. Why run a KV store?
Because rysh's state isn't one blob — it's an actor hierarchy. The workspace, each tab, each pane group, each pane is an independent actor with its own state and its own lifecycle. A per-pane KV bucket means each pane persists itself, on its own schedule, without a central serializer that has to know about everything. Panes come and go; keys come and go. The workspace bucket only tracks the wiring.
It also means persistence composes with everything else NATS already does for rysh: the same JetStream infrastructure that stores your pane state backs agent todo lists and the cross-agent context store. One data layer, several jobs.
The part that matters
Here's the reframe: in an agentic terminal, state is the product. The conversation history is what makes the next prompt cheap. The output buffer is what the agent re-reads before acting. The workspace layout is a map of your working memory.
A terminal that forgets all of that on restart is asking you to rebuild your own context every morning — and to rebuild your agents' context too.
Rysh treats your workspace like a database treats your data: it's not allowed to lose it just because the process ended.
Close the laptop. It'll all be there.
We're recruiting design partners — teams who want persistent, observable agent workspaces and a direct line to the founders while we build. Apply at rysh.ai/design-partner.
More in this series: Sessions that survive: attach, detach, and never lose an agent mid-task · Why rysh is built on proto.actor and NATS