COORD: 44.21.90
OFFSET: +12.5°
SYS.READY
BUFFER: 99%
FOCAL_PT
BACK TO DEVLOG
POCKET-SERVER

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

2026-01-22 // RAW LEARNING CAPTURE
PROJECTPOCKET-SERVER

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:

  1. Named sessions that persist (tmux's job)
  2. Network-resilient connections (ET's job)
  3. Multi-device attach (both)

pocket-server already has:

  • PTY management (internal/pty/terminal.go via creack/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 framing

Driver/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:

  1. Late-join scrollback: 64KB ring buffer. ~500-1000 lines. Enough to orient on attach. Client handles deeper scroll.

  2. PTY size matches the driver. Viewers deal with wrapping in their own terminal. /take triggers resize to new driver's dimensions. No "smallest client wins."

  3. Session naming: client ID = session name. Pocket sends "work" as client ID → gets session "work". Configurable in Pocket settings. Zero protocol changes.

  4. Detach = disconnect, session persists. Close terminal → client gone → session lives. Shell exit → session dies. No explicit detach command.

  5. 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 + pocket CLI: ~50-100 lines

Where We Landed

Shape document written: pocket-server/SHAPE-MULTI-SESSION.md

The implementation order is sequential after core ET lands:

  1. Refactor Session for multi-client (extract Client interface)
  2. Add session naming (map by name, implicit create)
  3. Unix socket listener
  4. LocalClient implementation
  5. pocket CLI binary (attach, sessions, take, release)
  6. Driver model + input routing
  7. 64KB output buffer for late-join
  8. 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, /take with 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.
LOG.ENTRY_END
ref:pocket-server
RAW