# Extension Protocol RPC Wire Format and Bootstrapping in DeepSeek-Reasonix

> Explore the Extension Protocol RPC wire format and bootstrapping process in DeepSeek-Reasonix. Learn how NDJSON framing ensures reliable communication between host and extension processes.

- Repository: [YHH/DeepSeek-Reasonix](https://github.com/esengine/DeepSeek-Reasonix)
- Tags: internals
- Published: 2026-08-07

---

**The Extension Protocol is a strict JSON-RPC 2.0 dialect using NDJSON framing that enables reliable communication between the DeepSeek-Reasonix desktop host and extension processes, initialized through a mandatory bootstrap handshake that exchanges protocol versions and UI capabilities.**

The DeepSeek-Reasonix project implements a robust extension system defined by a precise Extension Protocol RPC wire format that governs all cross-process communication. This protocol, implemented primarily in [`sdk/go/wire.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/sdk/go/wire.go), utilizes newline-delimited JSON (NDJSON) for message framing and enforces strict validation rules to ensure type safety and protocol compliance. Before any extension-specific operations occur, the system executes a structured bootstrapping process that establishes the communication channel and synchronizes initial state between the host and the extension process.

## Extension Protocol RPC Wire Format Specification

The wire format revolves around strict JSON-RPC 2.0 semantics with additional constraints to prevent protocol drift and ensure predictable behavior across language boundaries.

### NDJSON Framing and Message Structure

All messages transit as **NDJSON frames**—individual JSON objects terminated by newline characters. The reader implementation in [`sdk/go/wire.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/sdk/go/wire.go) uses `readLine` to extract frames, enforcing a maximum size limit defined by `FrameBytes`. If a frame exceeds this budget, the connection terminates immediately with a `FrameTooLargeError`.

Each message conforms to the `outbound` or `inbound` struct definitions containing standard JSON-RPC fields:

- **`jsonrpc`**: Must always be the string `"2.0"` (validated by `validateStrictFrame`)
- **`id`**: Restricted to **integers only** or `null`; the `validRPCID` helper rejects string IDs or other types
- **`method`**: String identifier for the RPC endpoint
- **`params`**: Must be a JSON object; empty objects are permitted but bare primitives cause validation failures
- **`result`** and **`error`**: Populated in responses according to JSON-RPC 2.0 specification

### Request-Response and Notification Flows

The protocol distinguishes between synchronous requests and fire-and-forget notifications, handling each with distinct concurrency models.

**Request-Response Flow:**
The `call` method marshals requests into NDJSON frames and registers a response channel in the `pending` map using the integer ID as the key. The caller blocks until `resolve` matches an inbound response to the pending call, or until context cancellation or connection closure occurs. This design ensures strict request-response correlation without ambiguity.

**Notification Flow:**
For one-way communication, the `notify` method marshals notifications and enqueues them on the `notifyQueue` channel. A dedicated writer goroutine, `serveOutboundNotifications`, serializes these messages to guarantee transmission order. The queue enforces a maximum depth of `maxQueuedNotifications` (256 messages) to prevent memory exhaustion under backpressure.

### Concurrency Guards and Transport Limits

The implementation maintains thread safety through mutex-protected mutable state. Key limits include:

- **Maximum concurrent handlers**: 32 (`maxConcurrentHandlers`)
- **Maximum queued outbound notifications**: 256 (`maxQueuedNotifications`)
- **Frame size limit**: Enforced by `FrameBytes` via `readLine`

Panic-safe wrappers surround all handler invocations, translating panics into logged diagnostics without crashing the critical read loop.

### Error Handling Protocol

Errors follow standard JSON-RPC 2.0 codes ranging from `-32700` (Parse error) to `-32603` (Internal error). The protocol additionally defines a custom transport-local code `-32099` (`CodeServerBusy`) for overload scenarios. All errors wrap the `rpcErrorObject` struct and propagate through `respondError`, ensuring consistent error serialization across the wire.

## DeepSeek-Reasonix Bootstrapping Process

Before extension-specific APIs become available, the host and extension perform a capability handshake that establishes the runtime contract.

### Connection Establishment

The host initializes communication by invoking `newConn` to create a bidirectional pipe wrapping the child process's `stdin` and `stdout` streams. This connection object integrates with the host's logging system and immediately launches background goroutines via `Serve` to manage the read loop and notification writer.

### The Bootstrap Handshake

Immediately after connection creation, the host issues a JSON-RPC request with the method `"bootstrap"` (defined in the TypeScript bridge at [`desktop/frontend/src/lib/bridge.ts`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/desktop/frontend/src/lib/bridge.ts)). The request parameters include the host's protocol version:

```go
type BootstrapParams struct {
    ProtocolVersion string `json:"protocolVersion"`
}

```

The extension must respond with a `BootstrapResult` containing:

- **Protocol version**: Must match the host's declared version (currently `"2.0"`)
- **Actions**: A slice of `ExtensionActionView` objects describing UI palette entries
- **Generations**: A map of initial generation numbers (`extensionGenerations`) for each UI surface

```go
type BootstrapResult struct {
    ProtocolVersion string               `json:"protocolVersion"`
    Actions         []ExtensionActionView `json:"actions"`
    Generations     map[string]int64      `json:"generations"`
}

```

### State Initialization and Runtime Epoch Fencing

Upon receiving the bootstrap response, the host stores the declared actions in `paletteExtensionActions` and records the generation numbers. These generation numbers establish a **per-tab runtime epoch fence** utilized by `applyExtensionSurfaceEvent` and `withAcceptedExtensionGeneration` to discard out-of-order UI updates. This mechanism ensures that asynchronous extension UI events apply deterministically regardless of network latency or goroutine scheduling.

### Post-Bootstrap API Surface

After successful handshake completion, the host exposes three public extension APIs through the bridge:

- **`ExtensionActions(tabID)`**: Enumerates the actions declared during bootstrap
- **`InvokeExtensionAction(tabID, name, args)`**: Triggers a specific extension action with provided arguments
- **`SubmitExtensionForm(tabID, pluginID, surfaceID, values)`**: Submits form data to a specific UI surface

## Implementation Examples

Establishing the low-level connection and executing the bootstrap sequence:

```go
// Create connection wrapping child process I/O
conn := extension.NewConn(childStdout, childStdin, logger)
go conn.Serve(context.Background()) // Launches read loop and notification writer

// Execute bootstrap handshake
var result BootstrapResult
raw, err := conn.Call(ctx, "bootstrap", BootstrapParams{ProtocolVersion: "2.0"})
if err != nil {
    log.Fatalf("bootstrap failed: %v", err)
}
if err := json.Unmarshal(raw, &result); err != nil {
    log.Fatalf("invalid bootstrap response: %v", err)
}

// Initialize host state with extension capabilities
paletteExtensionActions = result.Actions
extensionGenerations = result.Generations

```

Invoking an extension action after successful bootstrapping:

```go
type InvokeParams struct {
    Name string            `json:"name"`
    Args map[string]string `json:"args"`
}

raw, err := conn.Call(ctx, "invokeExtensionAction", InvokeParams{
    Name: "generateDocumentation",
    Args: map[string]string{"language": "go", "style": "godoc"},
})
if err != nil {
    // Handle JSON-RPC error (possibly CodeServerBusy if overloaded)
    return err
}
// Process result returned by extension

```

Submitting form data to an extension surface:

```go
type FormParams struct {
    PluginID  string                 `json:"pluginId"`
    SurfaceID string                 `json:"surfaceId"`
    Values    map[string]interface{} `json:"values"`
}

_, err := conn.Call(ctx, "submitExtensionForm", FormParams{
    PluginID:  "reasonix-helper",
    SurfaceID: "config-panel",
    Values:    map[string]interface{}{"theme": "dark", "autoSave": true},
})
// Errors return via JSON-RPC error objects; success returns empty result

```

## Summary

- The **Extension Protocol RPC wire format** uses strict NDJSON framing with validated JSON-RPC 2.0 envelopes, enforcing integer-only IDs and object-typed parameters.
- Transport limits include **32 concurrent handlers** and **256 queued notifications**, with `FrameBytes` limiting individual message sizes.
- The **bootstrap handshake** exchanges protocol versions, UI actions, and generation numbers before normal RPC operations commence.
- Generation numbers establish **epoch fencing** that prevents out-of-order UI updates across asynchronous extension events.
- Key implementation files include [`sdk/go/wire.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/sdk/go/wire.go) for the core protocol and [`desktop/frontend/src/lib/bridge.ts`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/desktop/frontend/src/lib/bridge.ts) for the host-side TypeScript integration.

## Frequently Asked Questions

### What is the Extension Protocol RPC wire format in DeepSeek-Reasonix?

The Extension Protocol RPC wire format is a strict JSON-RPC 2.0 dialect transmitted over NDJSON (newline-delimited JSON) streams. It enforces specific constraints including integer-only request IDs, object-typed parameters, and a maximum frame size (`FrameBytes`) to ensure reliable communication between the desktop host and extension processes.

### How does the bootstrapping process establish communication between host and extension?

The bootstrapping process begins with the host creating a bidirectional pipe via `newConn`, then immediately sending a `"bootstrap"` method request containing the host's protocol version. The extension responds with its version, a list of `ExtensionActionView` objects, and initial generation numbers, which the host stores to establish the runtime state before allowing calls to `ExtensionActions` or `InvokeExtensionAction`.

### What are the transport limits and concurrency constraints in the Extension Protocol?

The protocol enforces a maximum of **32 concurrent handlers** (`maxConcurrentHandlers`) for processing requests and **256 queued outbound notifications** (`maxQueuedNotifications`). Individual frames are limited by `FrameBytes`, and connections terminate with `FrameTooLargeError` if messages exceed this threshold.

### How does the protocol handle errors and panic recovery?

Errors use standard JSON-RPC 2.0 codes (-32700 to -32603) plus the custom `-32099` (`CodeServerBusy`) for overload conditions, wrapped in `rpcErrorObject` structures. The read loop includes panic-safe wrappers that convert panics into logged diagnostics without terminating the connection, ensuring robust error isolation between extensions and the host.