- 01.Starting Point
- 02.The Exploration: What Exists?
- 03.The Pivot: Can pocket-server do both?
- 04.The Architecture Delta
- 05.Driver/Viewer Model
- 06.Resolved Design Decisions
- 07.Effort Estimate
- 08.Where We Landed
- 09.Takeaways
Shaping Multi-Session (tmux replacement inside pocket-server)
Pocket-server exists as an ET replacement — ~30% built (crypto complete + tested, session/PTY/protocol scaffolded). Architecture: 1 ET client ↔ 1 session, single connection, XSalsa20-Poly1305 encrypti
Starting Point
Pocket-server exists as an ET replacement — ~30% built (crypto complete + tested, session/PTY/protocol scaffolded). Architecture: 1 ET client ↔ 1 session, single connection, XSalsa20-Poly1305 encryption, sequence-based reconnection with BackedWriter catchup buffers.
The question came from frustration with tmux: "I love resumable terminal sessions with a name, I hate everything else — specifically the scroll." This led to exploring alternatives (Zellij, dtach, abduco, Eternal Terminal) and then the realization: pocket-server is already building session persistence + network resilience. Adding named multi-client sessions is a small delta on top.
The Exploration: What Exists?
Mapped the "resumable terminal session" landscape:
- Zellij — Modern Rust tmux replacement. Normal scroll, better defaults. But still a multiplexer (panes, layouts).
- dtach/abduco — Session-only tools. Detach/reattach, nothing else. Pair with terminal's native scroll.
- Eternal Terminal — Network resilience (survives wifi drops), but doesn't do session persistence in the tmux sense.
- screen — Same scroll problems as tmux, worse UX.
Key insight: ET solves "connection survives network drops" but NOT "session persists when you close the terminal." tmux solves persistence but NOT network resilience. These are two halves of the same problem.
The Pivot: Can pocket-server do both?
The user's workflow: jump between iPhone (Pocket app) and MacBook throughout the day. Same sessions, seamless switching. This requires:
- Named sessions that persist (tmux's job)
- Network-resilient connections (ET's job)
- Multi-device attach (both)
pocket-server already has:
- PTY management (
internal/pty/terminal.goviacreack/pty) - Session struct with reconnection support
- BackedWriter with sequence-based replay (1MB ring buffer)
- Crypto (XSalsa20-Poly1305, complete + tested)
The gap: current model is map[clientID]*Session (1:1). Need map[name]*Session with multiple clients per session.
The Architecture Delta
The crypto wrinkle: ET's BackedWriter stores encrypted messages (preserves crypto state on reconnect). Multi-client means each client has different keys/nonces. Can't share one encrypted buffer.
Solution: two transport types with different trust models:
pocket-server
├── TCP :2022 (ET protocol, encrypted — for Pocket iOS)
└── Unix socket (plaintext, fast — for local terminal)
This means:
- Remote clients (Pocket) get full ET crypto + reconnection. Unchanged protocol.
- Local clients (Mac terminal) skip crypto entirely. Unix socket permissions = auth.
- Session stores a plaintext output ring buffer for broadcast.
- Each RemoteClient has its own BackedWriter for ET-compatible reconnection.
Client interface:
Client (interface)
├── ID() string
├── SendOutput([]byte) error
├── IsLocal() bool
└── Close()
RemoteClient implements Client // ET protocol
LocalClient implements Client // Unix socket, simple framingDriver/Viewer Model
Instead of multi-writer (complex, conflict-prone): one driver at a time.
- Driver: can send input to PTY
- Viewers: see output, can't type
/take— steal driver seat (always works, single-user system)/release— give up driver seat- On attach: if no current driver, you become it automatically
No negotiation, no permissions. Tailscale handles network auth. Unix socket perms handle local auth. This is one person's Mac.
Resolved Design Decisions
Five open questions, all resolved:
-
Late-join scrollback: 64KB ring buffer. ~500-1000 lines. Enough to orient on attach. Client handles deeper scroll.
-
PTY size matches the driver. Viewers deal with wrapping in their own terminal.
/taketriggers resize to new driver's dimensions. No "smallest client wins." -
Session naming: client ID = session name. Pocket sends "work" as client ID → gets session "work". Configurable in Pocket settings. Zero protocol changes.
-
Detach = disconnect, session persists. Close terminal → client gone → session lives. Shell exit → session dies. No explicit detach command.
-
Shell: respect
$SHELL. No per-session override.
Effort Estimate
Core ET implementation (existing scope): ~1,500-2,000 lines
Multi-session addition: ~300-400 lines
────────────────────────────────────────────────────────────
Total: ~1,800-2,400 lines
Breakdown of the delta:
- Refactor Session for multi-client: ~150 lines
- Unix socket listener + LocalClient: ~100 lines
- Driver model + /take /release: ~50 lines
- Output broadcast: ~50 lines
- Session naming +
pocketCLI: ~50-100 lines
Where We Landed
Shape document written: pocket-server/SHAPE-MULTI-SESSION.md
The implementation order is sequential after core ET lands:
- Refactor Session for multi-client (extract Client interface)
- Add session naming (map by name, implicit create)
- Unix socket listener
- LocalClient implementation
pocketCLI binary (attach, sessions, take, release)- Driver model + input routing
- 64KB output buffer for late-join
- Integration test: phone + Mac on same session
Takeaways
- The "two halves of one problem" framing unlocked this. ET = connection resilience. tmux = session persistence. Solving both in one server eliminates the need for tmux entirely.
- Driver/viewer is dramatically simpler than multi-writer. For a single-user system,
/takewith no permissions is the right UX. - Two transports (ET over TCP, plaintext over Unix socket) keeps Pocket iOS unchanged while giving local clients zero-overhead attach. The session doesn't care what kind of client is talking to it.
- 64KB plaintext ring buffer for late-join catchup is a clean middle ground — not the full "server-side scrollback" feature, but enough to make device switching seamless.