# How OpenClaude Handles Different Transport Kinds: WebSocket, SSE, Hybrid, and Local

> Explore how OpenClaude manages diverse transport kinds like WebSocket, SSE, hybrid, and local. Discover the unified Transport interface simplifying model-server communication.

- Repository: [Gitlawb/openclaude](https://github.com/Gitlawb/openclaude)
- Tags: how-to-guide
- Published: 2026-09-02

---

**OpenClaude uses a unified `Transport` interface to abstract all model-server communication, with `getTransportForUrl()` in [`src/cli/transports/transportUtils.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/cli/transports/transportUtils.ts) automatically selecting the appropriate implementation based on URL scheme, CLI flags, and provider configuration.**

The transport layer is the critical bridge between OpenClaude's CLI and any language model backend—whether that's a local binary, an OpenAI-compatible API, or an Anthropic endpoint. Understanding how these different transport kinds work helps you debug connection issues, optimize for restrictive network environments, and extend the system with new providers.

## The Transport Interface: One Contract, Many Backends

At the heart of OpenClaude's transport system is the **`Transport`** interface defined in [`src/cli/transports/Transport.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/cli/transports/Transport.ts). Every concrete transport implements the same methods:

- `connect()` — establish the connection
- `write(data)` — send a message to the model
- `close()` — tear down gracefully
- `setOnData(callback)`, `setOnConnect(callback)`, `setOnError(callback)` — event hooks

This abstraction means higher-level code in `remoteIO` and `structuredIO` never needs to know whether it's talking over WebSocket, HTTP POST, or a local process pipe.

## The Five Transport Kinds in OpenClaude

### WebSocket Transport (Default)

**[`WebSocketTransport.ts`](https://github.com/Gitlawb/openclaude/blob/main/WebSocketTransport.ts)** provides full-duplex communication over `ws://` or `wss://` URLs. It's the default when you point OpenClaude at a WebSocket-capable server.

```ts
import { getTransportForUrl } from 'src/cli/transports/transportUtils.js';

const url = 'wss://api.example.com/v1/stream';
const transport = await getTransportForUrl(url);
await transport.connect();

```

The implementation handles reconnection with exponential back-off and keeps the connection alive for streaming responses.

### Hybrid Transport: WebSocket Reads + HTTP POST Writes

**[`HybridTransport.ts`](https://github.com/Gitlawb/openclaude/blob/main/HybridTransport.ts)** solves a common firewall problem: outbound WebSocket traffic blocked, but inbound streaming still possible. It uses WebSocket for reading server-sent events and plain HTTP POST for writing messages.

OpenClaude automatically falls back to Hybrid when it detects an `http://` or `https://` URL without explicit WebSocket markers. You can also force it with `--transport=hybrid`.

### SSE Transport: Server-Sent Events for OpenAI-Compatible APIs

**[`SSETransport.ts`](https://github.com/Gitlawb/openclaude/blob/main/SSETransport.ts)** implements the CCR v2 "openai-compatible" protocol. It reads from an `/sse` endpoint and writes via HTTP POST. This is what powers connections to OpenAI's API, Groq, and other compatible providers.

```ts
const url = 'https://api.openai.com/v1/chat/completions';
const transport = await getTransportForUrl(url);
// Returns SSETransport instance

```

The transport parses SSE-formatted chunks and converts them to OpenClaude's internal `StdoutMessage` format.

### Local Transport: In-Process Model Execution

When you run `--local` or configure a provider with `"kind": "local"`, OpenClaude bypasses the network entirely. **[`WorkerStateUploader.ts`](https://github.com/Gitlawb/openclaude/blob/main/WorkerStateUploader.ts)** spawns a child process from your specified binary and communicates over stdio pipes.

```ts
import { getLocalTransport } from 'src/cli/transports/transportUtils.js';

const transport = await getLocalTransport({
  exec: './my-llama-binary',
  args: ['--ngl', '35'],
});

```

No `ws://` or HTTP stack is involved. Messages flow directly to the spawned process, making this ideal for:
- Running quantized models via llama.cpp
- Dockerized local deployments
- Custom compiled inference engines

### CCR Client: Provider-Specific Wrappers

**[`ccrClient.ts`](https://github.com/Gitlawb/openclaude/blob/main/ccrClient.ts)** sits above the raw transports, providing the "openai-compatible" and "anthropic-compatible" transport kinds. It selects SSE or Hybrid based on provider configuration and CLI flags like `--provider=openai` or `--provider=anthropic`.

## How Transport Selection Works

The factory function **`getTransportForUrl()`** in [`src/cli/transports/transportUtils.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/cli/transports/transportUtils.ts) implements this decision logic:

1. **Parse URL scheme**
   - `ws://` / `wss://` → `WebSocketTransport`
   - `http://` / `https://` with `/sse` path → `SSETransport`
   - `http://` / `https://` otherwise → `HybridTransport`

2. **Detect local execution**
   - `--local` flag or `"kind": "local"` in config → `WorkerStateUploader` via `getLocalTransport()`

3. **Return initialized transport**
   - All paths yield an object conforming to the `Transport` interface

Here's a runtime fallback example when WebSocket fails:

```ts
let transport = await getTransportForUrl(wsUrl);

try {
  await transport.connect();
} catch (err) {
  console.warn('WebSocket failed, falling back to HTTP Hybrid');
  transport = await getTransportForUrl(httpUrl);
  await transport.connect();
}

```

## Why This Design Matters

- **Pluggable backends** — New providers need only a URL scheme handler and optionally a CCR client wrapper
- **Automatic resilience** — Network restrictions trigger graceful degradation without user intervention
- **Consistent developer experience** — All transports expose identical methods; higher-level code stays portable

The CLI's diagnostic commands like `--verbose` and `--transport-dump` leverage the optional `isConnectedStatus` and transport metadata to show exactly which kind is active.

## Key Source Files

| File | Purpose |
|------|---------|
| [`src/cli/transports/Transport.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/cli/transports/Transport.ts) | Core interface all transports implement |
| [`src/cli/transports/WebSocketTransport.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/cli/transports/WebSocketTransport.ts) | Default WebSocket transport |
| [`src/cli/transports/HybridTransport.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/cli/transports/HybridTransport.ts) | WebSocket reads + HTTP POST writes |
| [`src/cli/transports/SSETransport.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/cli/transports/SSETransport.ts) | SSE reads for OpenAI-compatible endpoints |
| [`src/cli/transports/WorkerStateUploader.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/cli/transports/WorkerStateUploader.ts) | Local process communication |
| [`src/cli/transports/ccrClient.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/cli/transports/ccrClient.ts) | Provider-specific CCR wrappers |
| [`src/cli/transports/transportUtils.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/cli/transports/transportUtils.ts) | Factory for transport selection |

## Summary

- OpenClaude's transport system centers on a single **`Transport`** interface in [`Transport.ts`](https://github.com/Gitlawb/openclaude/blob/main/Transport.ts)
- **`getTransportForUrl()`** automates selection: WebSocket for `wss://`, SSE for `/sse` paths, Hybrid otherwise
- **Local execution** uses `WorkerStateUploader` with stdio pipes—no network stack
- **Provider compatibility** (OpenAI, Anthropic) is handled by [`ccrClient.ts`](https://github.com/Gitlawb/openclaude/blob/main/ccrClient.ts) selecting appropriate underlying transports
- Automatic fallback between transport kinds enables operation across diverse network environments

## Frequently Asked Questions

### How do I force a specific transport kind?

Use the `--transport` CLI flag with values `websocket`, `hybrid`, `sse`, or `local`. For example: `openclaude --transport=hybrid --url https://api.example.com`. When omitted, OpenClaude infers from the URL scheme and provider configuration.

### Why would Hybrid transport be chosen automatically?

Hybrid is selected when you specify an `http://` or `https://` URL without WebSocket indicators. This commonly occurs in corporate environments where firewalls block outbound WebSocket connections but still permit HTTP POST requests and server-sent event streams.

### What's the difference between "local" transport and running a local HTTP server?

Local transport (`--local` or `"kind": "local"`) runs the model as a child process and communicates via stdio pipes—no TCP stack, no port binding. Running a local HTTP server involves starting a separate service (like Ollama) and connecting to it via WebSocket or HTTP, which uses the standard network transports.

### Can I implement a custom transport for my internal API?

Yes. Implement the `Transport` interface from [`src/cli/transports/Transport.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/cli/transports/Transport.ts) and register it in [`transportUtils.ts`](https://github.com/Gitlawb/openclaude/blob/main/transportUtils.ts) by extending `getTransportForUrl()` with your URL pattern detection. Your transport will work with all existing CLI features without modifying higher-level code.