# Understanding the Reasonix Extension Protocol v2 and Sidecar Event Interception

> Explore Reasonix Extension Protocol v2, a JSON-RPC 2.0 contract for connecting Reasonix hosts with extension sidecars. Learn how sidecars intercept runtime events via blocking requests or notifications.

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

---

**The Reasonix Extension Protocol v2 is a strict JSON-RPC 2.0 over NDJSON wire contract that connects the DeepSeek-Reasonix host with out-of-process extension sidecars, enabling them to intercept runtime events through blocking requests or observe them via fire-and-forget notifications.**

The DeepSeek-Reasonix repository implements a sophisticated extension system that isolates plugin logic into separate processes to ensure host stability. At the heart of this system lies the Reasonix Extension Protocol v2, which defines the deterministic communication schema that allows sidecars to safely intercept, modify, or block AI runtime events without compromising the main process.

## What Is the Reasonix Extension Protocol v2?

The **Reasonix Extension Protocol v2** (protocol ID `reasonix.extension.v2`) is the stable wire contract governing all communication between the Reasonix host and extension sidecars. According to the specification in [`docs/EXTENSION_PROTOCOL.md`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/docs/EXTENSION_PROTOCOL.md), it establishes a machine-verifiable JSON schema checked into CI to prevent drift, ensuring that backward-compatible changes only add optional fields, enums, or methods within the major version.

The protocol defines **17 frozen hook points** where sidecars can attach behavior, a fixed set of lifecycle messages, and deterministic error codes. The canonical schema lives at [`internal/extension/protocol/schema.generated.json`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/extension/protocol/schema.generated.json) and is referenced by its SHA-256 hash, while the generated method index at [`docs/EXTENSION_PROTOCOL.generated.md`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/docs/EXTENSION_PROTOCOL.generated.md) serves as the source of truth for SDK generation.

## Transport Layer and Message Framing

Communication occurs over **NDJSON** (Newline Delimited JSON) where each complete JSON object occupies exactly one line on the sidecar's *stdin* and *stdout*. The *stderr* stream is reserved exclusively for sidecar diagnostics.

Key constraints include:

- **Frame Size Limit**: Messages are capped at **8 MiB**; oversized frames trigger a `frame_too_large` error.
- **Externalization**: Payload fields exceeding **64 KiB** are off-loaded to the host's content store. The frame carries an `ExternalizedField` descriptor with a `null` placeholder, and the peer retrieves content via `host/content/read` in **256 KiB** chunks.
- **Schema Validation**: All messages validate against the checked-in JSON schema to enforce type safety.

## Sidecar Lifecycle and Initialization

The host manages sidecars through a strict lifecycle defined in the protocol:

1. **Spawn**: The host starts the sidecar process.
2. **Initialize**: The host sends `extension/initialize` with the plugin manifest.
3. **Declaration**: The sidecar replies with `InitializeResult`, enumerating which hook points it will intercept or observe. The host validates these capabilities against the plugin manifest.
4. **Ready**: The host sends `extension/initialized` to signal that normal operation can begin.
5. **Shutdown**: Normal termination uses `extension/shutdown` with a configurable timeout. Crashes cancel all pending RPCs and trigger reloads only during idle time.

## How Sidecars Intercept Runtime Events

Sidecars interact with runtime events through two distinct mechanisms: **blocking intercepts** for synchronous decision-making and **observations** for asynchronous monitoring.

### Blocking Intercepts via `extension/intercept`

For events requiring immediate policy enforcement or transformation, the host issues blocking `extension/intercept` calls. In [`internal/extension/sidecar/client.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/extension/sidecar/client.go), the `Intercept` method (around line 13) constructs an `InterceptParams` struct containing the event enum, sequence number, payload, and timeout, then sends it via JSON-RPC.

Blocking intercepts execute sequentially in a deterministic priority order:

- Manifest `priority` value
- Plugin ID (lexicographical)
- Registration order

The sidecar returns an `InterceptResult` containing a **decision** (`continue`, `block`, `replace`, or `allow`/`deny` for permission events) and optional replacement data. Replacement slots (such as `system_prompt` or `provider_request`) enforce exactly one owner across all plugins to prevent conflicts.

### Externalization During Interception

When intercepting large payloads, the host's `externalizeInterceptParams` function automatically moves data exceeding **64 KiB** to the content store. The sidecar receives an `ExternalizedField` reference and retrieves the actual content via `host/content/read`. Similarly, replacement data from the sidecar undergoes re-externalization before the host applies the decision.

### Non-Blocking Observations via `extension/event`

For telemetry or logging that must not impact latency, the protocol supports **observations** through `extension/event` notifications. These are fire-and-forget messages enqueued on a bounded non-blocking writer queue. If the queue saturates, the host drops the event with a warning rather than back-pressuring the main request path, ensuring that observation failures never abort user operations.

## Decision Types and Payload Replacement

The protocol defines four primary decision types returned by interceptors:

- **`continue`**: Resume normal processing with the original payload.
- **`block`**: Halt the operation and return an error to the caller.
- **`replace`**: Substitute the payload with new data provided in `result.Replacement`.
- **`allow` / `deny`**: Specific to `permission.decision` hooks for access control.

When a sidecar specifies `DecisionReplace`, the host validates the new payload against the DTO schema before applying it to the running operation.

## Error Handling and Security Model

Errors travel as JSON-RPC error code **-32000** with structured data containing `reason`, `retryable`, and `action` fields. The full enumeration of error reasons appears in the generated protocol index.

Sidecars operate under a **full trust** security model: they receive full environment access, can read session data, and may bypass permission checks. Authorization is granted solely by installing a plugin that declares a `runtime` block in its manifest. The host redacts all credentials before sidecar output reaches UI surfaces or logs, preventing accidental credential leakage.

## Implementation Example

Below are minimal Go implementations demonstrating the handshake and interception flow using the Go SDK (`sdk/go`).

**Host side initialization and intercept:**

```go
ctx := context.Background()
rt := runtimeConfig // contains TimeoutMillis, Manifest, etc.
client, err := extension.NewSidecarClient(pluginID, rt)
if err != nil { log.Fatalf("spawn sidecar: %v", err) }

// 1️⃣ Initialize handshake
initResp, err := client.Initialize(ctx, extension.InitializeParams{
    Manifest: rt.Manifest,
})
if err != nil { log.Fatalf("init failed: %v", err) }

// 2️⃣ Intercept a runtime event (e.g., input.receive)
payload := json.RawMessage(`{"text":"hello world"}`)
result, err := client.Intercept(ctx,
    extension.InterceptEventInputReceive,
    payload,
    0, // let client pick timeout from manifest defaults
)
if err != nil { log.Fatalf("intercept error: %v", err) }

switch result.Decision {
case extension.DecisionContinue:
    // normal processing continues
case extension.DecisionBlock:
    log.Printf("blocked: %s", result.Reason)
case extension.DecisionReplace:
    // use result.Replacement as the new payload
}

```

**Sidecar intercept handler:**

```go
func main() {
    // The SDK sets up the JSON‑RPC server on stdin/stdout automatically.
    // Register the intercept handler:
    extension.RegisterInterceptor(func(ev extension.InterceptEvent, payload json.RawMessage) (extension.InterceptResult, error) {
        if ev == extension.InterceptEventInputReceive {
            // Example policy: block any input containing the word "secret"
            if bytes.Contains(payload, []byte("secret")) {
                return extension.InterceptResult{
                    Decision: extension.DecisionBlock,
                    Reason:   "prohibited term",
                }, nil
            }
        }
        return extension.InterceptResult{Decision: extension.DecisionContinue}, nil
    })
    // Block forever – the SDK runs the event loop.
    select {}
}

```

These examples rely on the Go SDK documented in [`sdk/go/README.md`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/sdk/go/README.md), which implements the full protocol contract including automatic externalization and schema validation.

## Summary

- The **Reasonix Extension Protocol v2** uses JSON-RPC 2.0 over NDJSON with an 8 MiB frame limit and SHA-256 verified schemas.
- Sidecars declare capabilities during the `extension/initialize` handshake validated against [`internal/extension/protocol/schema.generated.json`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/extension/protocol/schema.generated.json).
- **Blocking intercepts** via `extension/intercept` (implemented in [`internal/extension/sidecar/client.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/extension/sidecar/client.go)) execute sequentially by priority and support `continue`, `block`, or `replace` decisions.
- **Observations** via `extension/event` use a bounded non-blocking queue that drops messages under saturation to prevent latency spikes.
- Payloads exceeding **64 KiB** are automatically externalized to the host content store and retrieved in **256 KiB** chunks via `host/content/read`.
- Sidecars run with full trust, and all errors use code **-32000** with structured metadata indicating retryability.

## Frequently Asked Questions

### What is the difference between intercept and observe in the Reasonix Extension Protocol?

**Intercept** (`extension/intercept`) is a blocking RPC call where the sidecar must return a decision (`continue`, `block`, or `replace`) before the host resumes processing. **Observe** (`extension/event`) is a fire-and-forget notification that enters a bounded queue; if the queue is full, the event is dropped without blocking the main request path.

### What happens if a sidecar crashes while processing an intercept request?

If the sidecar crashes or exceeds the timeout during an `extension/intercept` call, the host returns an `intercept_timeout` or `provider_interrupted` error and aborts the current operation. The host will attempt to reload the sidecar only during idle time to avoid disrupting active user sessions.

### How does the protocol handle large payloads like images or long text?

Any payload field larger than **64 KiB** is automatically externalized to the host's content store, as defined in [`internal/extension/sidecar/client.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/extension/sidecar/client.go). The JSON-RPC frame contains an `ExternalizedField` descriptor instead of the raw data. The sidecar retrieves the content asynchronously via `host/content/read` in chunks of **256 KiB**.

### What permissions do sidecars have within the Reasonix host?

Sidecars operate with **full trust**: they receive the complete environment, can read session data, and may bypass permission checks. Authorization is granted only by installing a plugin that explicitly declares a `runtime` block. The host automatically redacts sensitive credentials from all sidecar output before it reaches logs or UI surfaces.