shellphone 0.1.0

Pipe CLI commands to a secure mobile web terminal
---
id: data-flow
title: End-to-end data flow
altitude: 2
topics:
- server
- pty
- frontend
relations:
- type: refines
  target: architecture
- type: references
  target: pty-bridge
- type: references
  target: http-server
- type: references
  target: frontend-terminal
summary: 'Detailed walkthrough of bytes flowing from the PTY through channels, WebSocket, and into xterm.js.'
---

# End-to-end data flow

## Output path (command → phone screen)

```
child process stdout/stderr
  → PTY master reader (blocking, in spawn_blocking thread)
  → broadcast::Sender<PtyEvent::Output> [4096-byte chunks]
  → relay_events task (server-owned broadcast::Sender)
  → per-connection broadcast::Receiver in send_task
  → WebSocket Binary frame
  → browser: new Uint8Array(data) → term.write()
```

## Input path (phone keyboard → command)

```
browser: term.onData(keystroke)
  → ws.send(JSON: {type: "input", data: "..."})
  → WebSocket recv_task: parse ClientMessage::Input
  → cmd_tx.send(PtyCommand::Input(bytes))
  → PTY master writer → child process stdin
```

## Resize path

```
browser: fitAddon.fit() → ResizeObserver/window resize
  → ws.send(JSON: {type: "resize", cols: N, rows: N})
  → recv_task: parse ClientMessage::Resize
  → cmd_tx.send(PtyCommand::Resize { cols, rows })
  → master.resize(PtySize { ... })
```

## Control messages (server → client)

Sent as JSON text frames, distinct from binary PTY output:
- `{"type": "refresh_token", "token": "..."}` — on first auth
- `{"type": "exit", "code": N}` — when child process exits

## Channel sizing

Both channels use a buffer of 256 messages. For output, this means up to ~1MB of buffered PTY data. If the browser can't keep up, the broadcast receiver lags and `RecvError::Lagged` is handled by continuing (skipping missed chunks).