# How to Extend and Customize Runtime Types in Cloudflare Computer

> Extend and customize runtime types in Cloudflare Computer by implementing WorkspaceModuleBackend. Learn how to register your custom backend with the Workspace constructor for enhanced functionality.

- Repository: [Cloudflare/computer](https://github.com/cloudflare/computer)
- Tags: how-to-guide
- Published: 2026-08-15

---

**Yes—you can extend and customize runtime types in Cloudflare Computer by implementing the `WorkspaceModuleBackend` interface and registering your backend with the `Workspace` constructor.**

Cloudflare Computer exposes a **pluggable runtime architecture** that lets you swap or augment execution backends without modifying core code. The `workspace.runtime` surface acts as a thin router that delegates calls to registered backends, making it possible to add custom execution environments, new protocols, or extended type systems.

---

## Understanding the Runtime Extension Architecture

The runtime layer in Cloudflare Computer separates routing logic from execution logic. In [`packages/computer/src/workspace.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/workspace.ts), the `Workspace` class accepts a `backends` array that defines which execution engines are available. The router resolves backend IDs at runtime and forwards `runtime.exec`, `runtime.getExec`, and related calls to the appropriate implementation.

This design means you can:

- **Add entirely new execution backends** (custom containers, WASM modules, remote sandboxes)
- **Replace built-in backends** with specialized implementations
- **Extend the type system** for return values and event payloads

---

## The Backend Registration Mechanism

When constructing a `Workspace`, you provide custom backends via the `backends` option:

```typescript
import { Workspace } from "@cloudflare/computer";

const ws = new Workspace({
  storage: makeStorage(),
  backends: [myCustomBackend],     // Register your implementation
  defaultBackendId: "my-backend",  // Optional: make it the default
});

```

The `backends` parameter accepts `WorkspaceRegisteredBackend` objects as defined in [`packages/computer/src/workspace.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/workspace.ts). The router uses these registrations to:

1. Match the `backend` option passed to `runtime.exec()` calls
2. Fall back to `defaultBackendId` or the first registered backend when unspecified

---

## Implementing the WorkspaceModuleBackend Interface

Every custom backend must conform to the `WorkspaceModuleBackend` contract in [`packages/computer/src/runtime/types.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/types.ts). The required shape includes:

| Property | Type | Description |
|----------|------|-------------|
| `protocol` | `"module"` | Fixed discriminator for module-type backends |
| `id` | `string` | Unique identifier used for routing |
| `type` | `string` | Human-readable type name |
| `connect(host)` | `Promise<WorkspaceModuleBackendHandle>` | Factory that returns an execution handle |

The `connect` method receives a host context and must return a `WorkspaceModuleBackendHandle` implementing the execution primitives.

---

## Building a Custom Backend Handle

The handle returned by `connect` must implement four core methods plus an optional `close` method, as specified in [`packages/computer/src/runtime/types.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/types.ts):

- **`exec(input)`** — Start a new execution, return a `ModuleExecutionEnvelope` with an event stream
- **`getExec(id)`** — Reconnect to an existing execution (optional for stateless backends)
- **`killExec(id)`** — Terminate a running execution
- **`disposeExec(id)`** — Clean up resources for a completed execution
- **`close()`** — Release global backend resources (optional)

The event stream must follow the `WorkspaceRuntimeExecHandle` semantics: ordered events with `id`, `seq`, `name`, and `value` fields, terminating with an `exit` event.

### Complete Custom Backend Example

```typescript
// echo-backend.ts
import type {
  WorkspaceModuleBackend,
  WorkspaceModuleBackendHandle,
  ModuleExecutionInput,
  ModuleExecutionEnvelope,
} from "@cloudflare/computer";

export const echoBackend: WorkspaceModuleBackend = {
  protocol: "module",
  id: "echo",
  type: "demo-echo",
  
  async connect(host) {
    const handle: WorkspaceModuleBackendHandle = {
      async exec(input: ModuleExecutionInput): Promise<ModuleExecutionEnvelope> {
        const resultId = input.id ?? crypto.randomUUID();
        
        return {
          id: resultId,
          runtimeId: undefined,
          events: new ReadableStream({
            start(controller) {
              // Echo the input as stdout
              const payload = typeof input.source === "string" ? input.source : "";
              
              controller.enqueue({
                id: resultId,
                seq: 0,
                name: "stdout",
                value: payload,
              });
              
              controller.enqueue({
                id: resultId,
                seq: 1,
                name: "exit",
                code: 0,
              });
              
              controller.close();
            },
          }),
        };
      },

      async getExec() {
        throw new Error("Echo backend does not support getExec");
      },
      
      async killExec() {
        // No-op: executions complete immediately
      },
      
      async disposeExec() {
        // No-op: nothing to dispose
      },
      
      async close() {
        // Clean up any global resources
      },
    };
    
    return handle;
  },
};

```

---

## Registering and Using Your Custom Runtime

With the backend defined, wire it into your workspace and invoke it through the standard `runtime.exec` API:

```typescript
import { Workspace } from "@cloudflare/computer";
import { echoBackend } from "./echo-backend";

const ws = new Workspace({
  storage: makeStorage(),
  backends: [echoBackend],
});

// Execute using the custom backend
const handle = await ws.runtime.exec("Hello, Cloudflare Computer!", {
  backend: "echo",      // Select by backend ID
  encoding: "utf8",
});

// Consume the event stream
for await (const event of handle) {
  if (event.name === "stdout") {
    console.log("Received:", event.value);
  }
}

const result = await handle.result();
console.log("Exit code:", result.exitCode);  // 0

```

The `backend` option in `runtime.exec` selects your registered implementation. Omit it to use the `defaultBackendId` or first available backend.

---

## Extending Runtime Value Types

To customize the type system for values flowing through your backend, extend the `WorkspaceRuntimeValue` union in [`packages/computer/src/runtime/types.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/types.ts) and re-export from your package:

```typescript
// custom-types.ts
import type { WorkspaceRuntimeValue } from "@cloudflare/computer";

export type ExtendedRuntimeValue = 
  | WorkspaceRuntimeValue 
  | { __tag: "custom-struct"; payload: Record<string, unknown> };

```

Consumers importing your extended types gain type safety for backend-specific return values while maintaining compatibility with the base runtime API.

---

## Key Implementation Files

| File | Purpose |
|------|---------|
| [`packages/computer/src/runtime/types.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/types.ts) | Defines `WorkspaceModuleBackend`, `WorkspaceModuleBackendHandle`, and `WorkspaceRuntimeValue`—the core contracts for customization |
| [`packages/computer/src/workspace.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/workspace.ts) | Implements backend registration and routing logic in the `Workspace` class |
| [`packages/computer/src/runtime/wire.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/wire.ts) | Low-level event stream protocol used by all backends |
| [`docs/05_runtime_interface.md`](https://github.com/cloudflare/computer/blob/main/docs/05_runtime_interface.md) | Public API documentation for `workspace.runtime` |
| [`docs/16_code_execution.md`](https://github.com/cloudflare/computer/blob/main/docs/16_code_execution.md) | Usage patterns and backend selection semantics |

---

## Summary

- **Backend registration** happens at `Workspace` construction via the `backends` array in [`packages/computer/src/workspace.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/workspace.ts)
- **The `WorkspaceModuleBackend` interface** in [`packages/computer/src/runtime/types.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/types.ts) defines the contract for custom runtimes
- **Four core methods** (`exec`, `getExec`, `killExec`, `disposeExec`) plus optional `close` must be implemented
- **Event streams** must conform to `WorkspaceRuntimeExecHandle` semantics with ordered, named events terminating in `exit`
- **Type extensions** are achieved by augmenting `WorkspaceRuntimeValue` and re-exporting from your package
- **Runtime selection** uses the `backend` option in `runtime.exec` calls, routed through the internal backend registry

---

## Frequently Asked Questions

### How do I make my custom backend the default runtime?

Pass `defaultBackendId` matching your backend's `id` in the `Workspace` constructor. When `runtime.exec` is called without a `backend` option, the router selects this default.

### Can I override the built-in container or bash backends?

Yes—register a backend with the same `id` as a built-in (e.g., `"container"`, `"bash"`) and it will take precedence. Alternatively, omit built-in backends from the `backends` array entirely and supply only your implementations.

### What happens if my backend doesn't support `getExec`?

Throw an error from `getExec` as shown in the echo backend example. This is valid for stateless or ephemeral execution models where reconnection isn't meaningful. The `Workspace` runtime will propagate this error to callers attempting to reconnect.

### How do I debug event stream issues in my custom backend?

Refer to [`packages/computer/src/runtime/wire.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/wire.ts) for the exact wire protocol. Ensure your stream emits events with monotonic `seq` numbers, includes all required fields (`id`, `name`, `value`), and always terminates with an `exit` event—otherwise consumers will hang awaiting completion.