> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cybr.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> These docs cover learner integrations through Hosted Lab Pages, the SDK, and the REST API. Content management is outside this integration scope.
> Read the setup page for the chosen approach before implementing it. Keep organization API keys and hosted mint secrets on the server.
> Install the SDK with `npm install @cybr/labs-sdk`. It runs on the server; the `/terminal`, `/terminal/xterm`, `/video`, and `/hosted-browser` entry points run in the browser.
> Cybr provides completion tracking and CTF verification. The integrating platform decides whether to award points.

# Terminal WebSocket protocol

> Connect a direct WebSocket client with temporary terminal credentials.

The terminal uses a persistent WebSocket connection. It does not accept separate HTTP requests for individual shell commands.

The [browser SDK helper](/reference/terminal) handles this protocol. Direct clients can use the wire format on this page.

## Create credentials

Request a [terminal session](/api-reference/create-terminal-session) from the server. The response contains `wsUrl`, `expiresAt`, and an optional `protocols` array.

The URL and protocols can contain temporary credentials. They belong only to the deployment owner.

## Open the socket

Pass the protocols to the WebSocket constructor:

```ts theme={null}
const socket = new WebSocket(session.wsUrl, session.protocols ?? [])
socket.binaryType = 'arraybuffer'
```

The organization API key does not belong in this connection. A socket-open event does not mean that the shell is ready.

## Control messages

Control messages use text frames with JSON:

| Direction | Message | Meaning |
| - | - | - |
| Server to browser | `{"type":"ready"}` | The shell accepts input |
| Browser to server | `{"type":"resize","cols":80,"rows":24}` | Change the terminal dimensions |
| Server to browser | `{"type":"exit","code":0}` | The shell exited, with an optional code |
| Server to browser | `{"type":"error"}` | The terminal reported an error |

Before input, wait for `ready`. After readiness, send the latest terminal dimensions.

The terminal applies 1 to 500 columns and 1 to 300 rows.

## Input and output

Terminal data uses binary frames. Input is UTF-8 text:

```ts theme={null}
socket.send(new TextEncoder().encode('pwd\r'))
```

Binary output arrives as `ArrayBuffer` data. A terminal renderer can consume those bytes as a `Uint8Array`.

A text decoder must preserve partial UTF-8 characters between output frames. xterm accepts byte arrays directly.

## Reconnect and expiration

Every reconnect requires a new terminal-session request. The previous URL and protocols are not reusable credentials for a new connection.

The server replays recent output on every new connection, so clear the screen before writing it. A closed socket does not end the lab.

Each lab allows up to 4 terminal connections at once.

An expired or ended lab can reject session creation. The learner must request a separate lab launch to start another environment.

`expiresAt` is the deadline for connection authorization. It does not set the shell lifetime or deployment deadline.

An existing connection can continue after token expiration until the lab ends.

## Close codes

An `exit` message arrives both when the shell exits and when the lab ends. The close code that follows tells them apart.

| Code | Meaning | Reconnect with a new session |
| - | - | - |
| `4408` | The lab ended | No. Launch the lab again. |
| `4403` | The session belongs to a different lab | No |
| `4401` | The session expired or was rejected | Yes |
| `4409` | The shell is being set up again | Yes |
| `4500` | Terminal service failure | Yes, when the learner asks |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.