WebSocket & SSE
Streaming endpoints — PTY, LSP, logs, events.
PandaStack uses WebSockets for bidirectional streams (PTY, LSP) and Server-Sent Events for one-way streams (logs, events, exec output).
Auth on streams
Browsers can't set custom headers on new WebSocket() or new EventSource(). So all
streaming endpoints accept the JWT or API key via query parameter:
wss://api-dev.pandastack.ai/v1/sandboxes/{id}/exec/pty?access_token=<jwt>&rows=30&cols=100The API strips access_token from the URL before forwarding to the agent.
PTY
WS /v1/sandboxes/{id}/exec/pty?rows=30&cols=100&access_token=<jwt>Client → server: raw bytes (keystrokes). Server → client: raw bytes (terminal output, ANSI escapes included).
Control frames: JSON messages prefixed with the magic byte 0x01:
{"type": "resize", "rows": 40, "cols": 120}Exec stream (SSE)
GET /v1/sandboxes/{id}/exec/stream
Content-Type: text/event-streamEvents:
event: stdout
data: hello
event: stderr
data: warning
event: done
data: {"exit_code": 0}Request body (POST, then upgrade-style):
{"cmd": "python -c 'print(1)'", "env": {"FOO": "bar"}, "timeout_sec": 30}Logs
GET /v1/sandboxes/{id}/logs?follow=1Streams the serial console of the microVM (kernel boot, init, agent stdout/stderr). SSE format, one event per line.
Events
GET /v1/sandboxes/{id}/events?tail=200&follow=1Streams lifecycle events:
event: state
data: {"state": "running", "ts": "2025-11-21T10:00:00Z"}
event: exec
data: {"cmd": "ls", "exit_code": 0, "duration_ms": 42}
event: snapshot
data: {"id": "snap_xxx"}LSP
WS /v1/sandboxes/{id}/lsp/{lang}?access_token=<jwt>{lang} ∈ python | typescript | go | rust.
The agent spawns the language server inside the sandbox (pyright, typescript-language-server,
gopls, rust-analyzer) and proxies LSP JSON-RPC frames bidirectionally.
Workspace root is /workspace inside the sandbox.
Reconnection
All streaming endpoints support reconnection. SSE clients should use the standard
Last-Event-ID header. WS clients should reconnect with exponential backoff.