API Reference

Base URL

  • Development: http://localhost:4020
  • Production: Configurable via settings

Authentication

Token Generation

POST /api/auth/token
Content-Type: application/json

{
  "password": "optional-password"
}
Response
{
  "token": "jwt-token-string",
  "expiresIn": 86400
}

Session Management

Create Session

POST /api/sessions
Authorization: Bearer <token>
Content-Type: application/json

{
  "command": "zsh",
  "args": [],
  "cwd": "/Users/username",
  "env": {},
  "name": "Session Name",
  "cols": 80,
  "rows": 24
}
Response
{
  "id": "session-uuid",
  "name": "Session Name",
  "created": "2024-01-01T00:00:00Z",
  "status": "running",
  "pid": 12345
}

List Sessions

GET /api/sessions
Authorization: Bearer <token>
Response
[
  {
    "id": "session-uuid",
    "name": "Session 1",
    "created": "2024-01-01T00:00:00Z",
    "status": "running",
    "pid": 12345
  }
]

Get Session Details

GET /api/sessions/:id
Authorization: Bearer <token>

Delete Session

DELETE /api/sessions/:id
Authorization: Bearer <token>

Resize Terminal

POST /api/sessions/:id/resize
Authorization: Bearer <token>
Content-Type: application/json

{
  "cols": 120,
  "rows": 40
}

WebSocket Connection

Connect to Session

const ws = new WebSocket('ws://localhost:4020/api/sessions/:id/ws');
ws.binaryType = 'arraybuffer';

Message Types

Terminal Output (Server → Client)

Binary format with magic byte:
[0xBF][4-byte length][UTF-8 data]

User Input (Client → Server)

{
  "type": "input",
  "data": "ls -la\n"
}

Terminal Resize (Client → Server)

{
  "type": "resize",
  "cols": 120,
  "rows": 40
}

Keep-Alive Ping

{
  "type": "ping"
}

Health Check

Server Status

GET /api/health
Response
{
  "status": "healthy",
  "uptime": 3600,
  "version": "1.0.0",
  "sessions": 5
}

Error Responses

StatusErrorDescription
400Bad RequestInvalid parameters
401UnauthorizedMissing/invalid token
404Not FoundSession not found
409ConflictSession already exists
500Server ErrorInternal error
Error Format
{
  "error": "Error message",
  "code": "ERROR_CODE",
  "details": {}
}

Rate Limiting

  • Session Creation: 10 per minute
  • API Calls: 100 per minute
  • WebSocket Messages: Unlimited

Binary Buffer Protocol

Packet Structure

┌──────────┬──────────────┬──────────────┐
│ Magic    │ Length       │ Data         │
│ (1 byte) │ (4 bytes)    │ (n bytes)    │
│ 0xBF     │ Big-endian   │ UTF-8        │
└──────────┴──────────────┴──────────────┘

Implementation

// Encoding
function encodeBuffer(data: string): ArrayBuffer {
  const encoded = new TextEncoder().encode(data);
  const buffer = new ArrayBuffer(5 + encoded.length);
  const view = new DataView(buffer);
  view.setUint8(0, 0xBF);
  view.setUint32(1, encoded.length, false);
  new Uint8Array(buffer, 5).set(encoded);
  return buffer;
}

// Decoding
function decodeBuffer(buffer: ArrayBuffer): string {
  const view = new DataView(buffer);
  if (view.getUint8(0) !== 0xBF) throw new Error('Invalid magic byte');
  const length = view.getUint32(1, false);
  return new TextDecoder().decode(new Uint8Array(buffer, 5, length));
}

Session Recording

Sessions are recorded in asciinema v2 format:
{
  "version": 2,
  "width": 80,
  "height": 24,
  "timestamp": 1234567890,
  "env": {
    "SHELL": "/bin/zsh",
    "TERM": "xterm-256color"
  }
}
Event format:
[timestamp, "o", "output data"]

See Also