Extension Protocol v1 in DeepSeek-Reasonix: How Sidecars Intercept Runtime Events

Extension Protocol v1 is a stable, language-agnostic JSON-RPC 2.0 contract that enables out-of-process sidecars to intercept, modify, and respond to runtime events in the DeepSeek-Reasonix engine without linking against the host binary.

DeepSeek-Reasonix separates its core engine logic from extensible user code through a strict process boundary. This architecture leverages Extension Protocol v1, a wire contract that defines how sidecars communicate with the host over NDJSON streams. By implementing this protocol, developers can hook into critical runtime events—such as input processing and permission decisions—while maintaining the engine's deterministic execution and security guarantees.

What Is Extension Protocol v1?

Extension Protocol v1 is the frozen, major-version-locked interface between the Reasonix host and out-of-process extensions. Defined in docs/EXTENSION_PROTOCOL.md and enforced by the machine-readable schema in internal/extension/protocol/schema.generated.json, the protocol specifies how sidecars subscribe to events, return decisions, and publish UI elements.

The contract uses JSON-RPC 2.0 over NDJSON (Newline-Delimited JSON) via stdin/stdout, with each line representing a complete JSON object. Frames are strictly capped at 8 MiB; exceeding this limit triggers a frame_too_large error and connection termination. All capabilities must be declared in the sidecar's manifest during the initialization handshake—any undeclared capability results in a capability_not_declared failure.

Architecture of the Host-Sidecar Model

The system comprises three primary components that communicate through the protocol's strict lifecycle:

  • Host (Reasonix Engine): The main process managing sessions, caching, and permissions. It spawns sidecars as plain subprocesses (no shell) and supplies a manifest declaring which events the sidecar may subscribe to, replace, or provide UI for.
  • Sidecar: A standalone binary compiled by the plugin author that implements the Extension Protocol. It communicates via stdin/stdout using NDJSON framing.
  • Transport: NDJSON over stdio with an 8 MiB frame limit, ensuring bounded memory usage during communication.

The lifecycle follows a deterministic sequence:

  1. Host launches sidecar and sends extension/initialize.
  2. Sidecar replies with its declaration (subscriptions, capabilities).
  3. Host validates against the manifest and sends extension/initialized.
  4. Runtime proceeds with event interception.
  5. On shutdown, host sends extension/shutdown; if the process does not exit, it is killed.

The host enforces resource constraints: only one sidecar per runtime generation may be started, with up to four sidecars bootable in parallel under a shared 30-second startup budget.

How Sidecars Intercept Runtime Events

Sidecars intercept events by registering for hook points (e.g., input.receive, permission.decision) during initialization. The host executes interceptors sequentially in deterministic priority order: manifest-defined priority → plugin ID → registration order.

The Interception Lifecycle

When a subscribed event occurs, the host emits extension/intercept to the sidecar. This method is blocking—the host awaits the sidecar's decision before continuing. In contrast, extension/event is fire-and-forget for telemetry or logging.

The interception flow operates as follows:

  1. Declare Subscription: In the Initialize call, the sidecar returns a list of hook points (e.g., ["input.receive"]).
  2. Host Sends Event: When processing user input, the host emits extension/intercept with the method name and payload.
  3. Sidecar Responds: The interceptor returns a decision type (continue, block, replace, allow, or deny).
  4. Host Continues: The host proceeds with the original or modified payload.

If the sidecar crashes or times out, the pending RPC is cancelled, the operation fails explicitly, and the sidecar is only restarted during an idle-time reload.

Decision Types and Payload Replacement

Sidecars influence runtime behavior through specific decision values:

  • continue: Pass the event through unchanged.
  • block: Halt the operation.
  • replace: Supply a new payload that the host re-validates against the DTO schema before proceeding.
  • allow / deny: Specific to permission decisions. Returning allow from a full-trust sidecar can override a host deny and is audited.

Implementing a Sidecar

Developers implement sidecars using the Go SDK located in sdk/go/, though any language capable of JSON-RPC over stdio can conform to the protocol.

Minimal Starter Sidecar

The repository provides a minimal example in sdk/go/examples/starterextension/main.go that intercepts input.receive to rewrite user input:

package main

import (
	"context"
	"encoding/json"
	"os"
	"strings"

	extension "github.com/esengine/DeepSeek-Reasonix/sdk/go"
)

const inputPrefix = "starter: "

type starter struct{}

func (starter) Initialize(context.Context, extension.InitializeParams) (*extension.InitializeResult, error) {
	return &extension.InitializeResult{
		Subscriptions: []string{"input.receive"},
	}, nil
}

func interceptInput(_ context.Context, _ string, payload json.RawMessage) (*extension.InterceptResult, error) {
	var in struct{ Text string `json:"text"` }
	if err := json.Unmarshal(payload, &in); err != nil || !strings.HasPrefix(in.Text, inputPrefix) {
		return extension.Continue(), nil
	}
	return extension.Replace(map[string]string{
		"text": strings.TrimPrefix(in.Text, inputPrefix) + " [rewritten by starter-extension]",
	})
}

func main() {
	err := extension.Serve(context.Background(), starter{}, extension.Options{
		Name:    "starter-extension",
		Version: "0.1.0",
		Interceptors: map[string]extension.InterceptorFunc{
			"input.receive": interceptInput,
		},
	})
	if err != nil {
		os.Exit(1)
	}
}

To test this sidecar:

go build -o bin/starter-extension.exe .
reasonix plugin install "$(pwd -P)" --link --replace --yes

When a user types starter: explain protocols, the model receives the rewritten text with the suffix appended.

Intercepting Permission Decisions

Sidecars can override host security decisions by subscribing to permission.decision. The following skeleton demonstrates forcing an allow decision, which overrides host denials:

package main

import (
	"context"
	"encoding/json"
	"os"

	extension "github.com/esengine/DeepSeek-Reasonix/sdk/go"
)

type allowAll struct{}

func (allowAll) Initialize(context.Context, extension.InitializeParams) (*extension.InitializeResult, error) {
	return &extension.InitializeResult{
		Subscriptions: []string{"permission.decision"},
	}, nil
}

func interceptPermission(_ context.Context, _ string, payload json.RawMessage) (*extension.InterceptResult, error) {
	var req struct {
		Decision string `json:"decision"`
	}
	if json.Unmarshal(payload, &req) != nil {
		return extension.Continue(), nil
	}
	return extension.Allow(), nil
}

func main() {
	err := extension.Serve(context.Background(), allowAll{}, extension.Options{
		Name:    "allow-all",
		Version: "0.1.0",
		Interceptors: map[string]extension.InterceptorFunc{
			"permission.decision": interceptPermission,
		},
	})
	if err != nil {
		os.Exit(1)
	}
}

Because sidecars are full-trust, this capability bypasses sandbox restrictions and is strictly audited in host logs.

Publishing Structured UI

Sidecars communicate with front-ends (desktop, web, or TUI) via structured UI methods, not HTML or JavaScript. To publish a status card:

func publishStatus(ctx context.Context) error {
	card := map[string]interface{}{
		"title":   "Hello from Sidecar",
		"content": "✅ Extension is running",
	}
	_, err := extension.Call(ctx, "host/ui/publish", map[string]interface{}{
		"kind":   "status",
		"payload": card,
	})
	return err
}

UI updates include plugin and runtime generation IDs to ensure stale updates are discarded by the front-end.

Security and Trust Model

Sidecars operate as full-trust processes outside the Reasonix sandbox. They inherit the host environment and can read/write files unless blocked by host-level sandboxing.

Authorization occurs solely at installation time: only plugins installed via the official flow (reasonix plugin install …) can start a sidecar. The host validates every capability declaration against the plugin manifest; undeclared capabilities cause immediate handshake failure. This design ensures that while sidecars possess broad system access, their capabilities are explicitly scoped and user-consented during the installation phase.

Summary

  • Extension Protocol v1 provides a stable, version-locked JSON-RPC 2.0 interface for out-of-process extensions in DeepSeek-Reasonix.
  • Sidecars communicate via NDJSON over stdio with an 8 MiB frame limit and must declare all capabilities in their manifest.
  • Event interception uses blocking extension/intercept calls that support continue, block, replace, and permission-specific allow/deny decisions.
  • The host enforces deterministic ordering of interceptors and validates all payload replacements against DTO schemas.
  • Sidecars run as full-trust processes with file system access, requiring installation through the official plugin flow for security.
  • Developers can implement sidecars in any language, with reference implementations available in sdk/go/ and protocol specifications in docs/EXTENSION_PROTOCOL.md.

Frequently Asked Questions

What transport protocol does Extension Protocol v1 use?

Extension Protocol v1 uses JSON-RPC 2.0 over NDJSON (Newline-Delimited JSON) transported via stdin/stdout. Each line represents a single JSON-RPC message, and frames are strictly limited to 8 MiB to prevent memory exhaustion. This design ensures language-agnostic compatibility while maintaining strict message boundaries.

How does a sidecar modify or replace a runtime payload?

When intercepting an event like input.receive, the sidecar returns a replace decision containing the new payload. According to the protocol implementation in internal/extension/sidecar/doc.go, the host validates this replacement against the DTO schema before substituting it into the processing pipeline. If validation fails, the host rejects the replacement and aborts the operation.

What security privileges do sidecars have in DeepSeek-Reasonix?

Sidecars operate as full-trust processes that run outside the Reasonix sandbox. They inherit the host's environment variables and file system access unless restricted by external sandboxing. Because they can override permission decisions and access sensitive resources, sidecars can only be installed through the official reasonix plugin install flow, which serves as the sole authorization checkpoint.

How many sidecars can run simultaneously?

The host supports up to four sidecars booting in parallel under a shared 30-second startup budget. However, only one sidecar per runtime generation may be active at a time. If a sidecar crashes or times out during a blocking interception, the operation fails explicitly, and the sidecar is only restarted during an idle-time reload to prevent disruption of active sessions.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →