Kubernetes exec terminal gateway over WebSocket.
Browser (xterm.js) <-> this server (WS) <-> Kubernetes API Server (exec) <-> Pod PTY
- Server:
sealos-tty-agent— a small HTTP + WebSocket gateway that turns Kubernetespods/execinto a browser-friendly terminal stream. - Client:
@labring/sealos-tty-client— a Web Streams API client (inpackages/protocol-client) that handles ticket issuance + WebSocket wiring for you.
pnpm install
cp config.example.json config.json
pnpm run devDefault: http://localhost:3000.
# Using Docker Compose
docker compose up -d
# or use Docker CLI
# Build the image
docker build -t sealos-tty-agent:latest .
# Spin up the container
docker run -d -p 3000:3000 -v $(pwd)/config.json:/app/config.json:ro sealos-tty-agent:latestThe recommended client is @labring/sealos-tty-client (Web Streams API), designed for browser terminals like xterm.js.
pnpm add @labring/sealos-tty-clientMinimal browser example (xterm-style wiring):
import { connectTerminalStreams } from '@labring/sealos-tty-client'
const { stdout, stdin, resize } = await connectTerminalStreams({
client: { baseUrl: 'http://localhost:3000' },
ticketRequest: { kubeconfig, namespace: 'default', pod: 'mypod', container: 'c1' },
connect: { initialSize: { cols: term.cols, rows: term.rows } },
})
const enc = new TextEncoder()
const writer = stdin.getWriter()
term.onData(d => void writer.write(enc.encode(d)))
term.onResize(({ cols, rows }) => resize(cols, rows))
void stdout
.pipeThrough(new TextDecoderStream())
.pipeTo(new WritableStream({ write: s => term.write(s) }))Notes:
- The server starts Kubernetes
execonly after receiving the firstresize. - If you need lower-level access, call the HTTP API to get a ticket, then connect to
GET /execvia WebSocket.
pnpm install
pnpm run devIssues a short-lived, one-time ticket for browser clients.
Request body:
{
"kubeconfig": "...",
"namespace": "default",
"pod": "mypod",
"container": "c1",
"command": ["bash", "-il"]
}Response:
{ "ok": true, "ticket": "...", "expiresAt": 0 }- If you cannot put the ticket in the URL, the first non-ping JSON message must be:
{ "type": "auth", "ticket": "..." }
- After auth, the client must send the first resize:
{ "type": "resize", "cols": 120, "rows": 30 }- The server starts Kubernetes exec only after receiving the first resize.
Binary frames:
- Client -> Server: stdin bytes
- Server -> Client: stdout/stderr bytes (TTY usually merged)
kubeconfig is sensitive. Use HTTPS/WSS and restrict RBAC for pods/exec.