# How to Contribute to Cloudflare Computer Runtime Types: A Developer's Guide

> Learn how to contribute to Cloudflare Computer runtime types. Modify TypeScript interfaces, update router logic, and add tests to enhance the platform.

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

---

**To contribute to Cloudflare Computer's runtime types, modify the TypeScript interfaces in [`packages/computer/src/runtime/types.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/types.ts), update the router logic in [`runtime.ts`](https://github.com/cloudflare/computer/blob/main/runtime.ts), and add tests covering serialization and edge cases.**

The Cloudflare Computer repository provides a programmable execution environment through its **Workspace Runtime** API. The runtime types define the contract between callers (such as Workers) and the backends that execute commands or modules. This guide walks through the architecture, key files, and step-by-step process for contributing to these runtime types based on the actual source code in `cloudflare/computer`.

## Understanding the Runtime Type Architecture

The Workspace Runtime is built around a layered type system that separates the public API from backend implementations. Before making changes, you need to understand how these interfaces interact.

### Core Type Definitions

All public type definitions live in **[`packages/computer/src/runtime/types.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/types.ts)**. The primary interfaces include:

| Interface | Purpose | Location |
|-----------|---------|----------|
| `WorkspaceRuntime` | Entry point that routes `exec`, `getExec`, `killExec`, and `disposeExec` calls | [`runtime.ts`](https://github.com/cloudflare/computer/blob/main/runtime.ts) (implementation), [`types.ts`](https://github.com/cloudflare/computer/blob/main/types.ts) (interface) |
| `WorkspaceRuntimeExecHandle` | A `ReadableStream` of events with `result()`, `kill()`, and `[Symbol.dispose]` methods | [`types.ts`](https://github.com/cloudflare/computer/blob/main/types.ts) |
| `WorkspaceRuntimeEvent` | Low-level event shape for `stdout`, `stderr`, or `exit` with generic encoding support | [`types.ts`](https://github.com/cloudflare/computer/blob/main/types.ts) |
| `WorkspaceRuntimeResult` | Final aggregate containing status, exit code, captured output, and sync statistics | [`types.ts`](https://github.com/cloudflare/computer/blob/main/types.ts) |
| `WorkspaceModuleBackend` | Backend protocol interface that backends implement for module execution | [`types.ts`](https://github.com/cloudflare/computer/blob/main/types.ts) |

The generic `E` parameter on `WorkspaceRuntimeEvent` allows the same interface to represent either **UTF-8 strings** or raw **`Uint8Array`** buffers depending on the `encoding` option passed to `exec()`.

### Runtime Implementation Layer

The **[`packages/computer/src/runtime/runtime.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/runtime.ts)** file contains the concrete `WorkspaceRuntime` class. This class performs three critical functions:

1. **Validates options** passed to each operation
2. **Selects the correct backend** based on the `backend` option (e.g., `'container'`, `'worker-javascript'`)
3. **Wraps returned handles** in a streaming-or-result API that guarantees exclusive use

Two helper functions in this file handle the complex stream transformations:

- **`transformModuleEvents`** — converts raw byte streams into UTF-8 strings when `encoding: 'utf8'` is specified
- **`drainModuleResult`** — aggregates streams into a complete `WorkspaceRuntimeResult`

## Step-by-Step: Contributing New Runtime Types

Follow this five-step process when adding new capabilities or fixing bugs in Cloudflare Computer's runtime types.

### Step 1: Add or Modify Types in types.ts

Begin by editing **[`packages/computer/src/runtime/types.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/types.ts)**. Follow the established naming convention: prefix all public types with `WorkspaceRuntime`.

```typescript
// Example: Adding a new exec option for signal handling
export interface WorkspaceRuntimeExecOptions {
  backend: string;
  encoding?: 'utf8' | 'bytes';
  cwd?: string;
  env?: Record<string, string>;
  stdin?: ReadableStream<Uint8Array> | string;
  timeout?: number;
  signal?: AbortSignal; // New field
}

```

Maintain backward compatibility where possible. New fields should be optional with sensible defaults.

### Step 2: Update the Runtime Router

If your change introduces a new operation or requires different validation, modify the **`WorkspaceRuntime`** class in [`runtime.ts`](https://github.com/cloudflare/computer/blob/main/runtime.ts):

```typescript
// In packages/computer/src/runtime/runtime.ts
async exec(
  command: string,
  options: WorkspaceRuntimeExecOptions
): Promise<WorkspaceRuntimeExecHandle> {
  // Validate new signal option
  if (options.signal?.aborted) {
    throw new Error('Exec aborted before start');
  }
  
  const backend = this.selectBackend(options.backend);
  const handle = await backend.exec(command, options);
  
  return new WorkspaceRuntimeExecHandleImpl(handle, options.encoding);
}

```

### Step 3: Adjust Streaming Logic

New event kinds require updates to **`transformModuleEvents`** or **`drainModuleResult`**. For example, adding a `resize` event for terminal emulation:

```typescript
// In runtime.ts - extending event transformation
function transformModuleEvents(
  stream: ReadableStream<WorkspaceModuleEvent>,
  encoding: 'utf8' | 'bytes'
): ReadableStream<WorkspaceRuntimeEvent> {
  return new TransformStream({
    transform(event, controller) {
      switch (event.name) {
        case 'stdout':
        case 'stderr':
        case 'exit':
          // Existing handling
          break;
        case 'resize': // New event type
          controller.enqueue({ name: 'resize', value: event.value });
          break;
      }
    }
  });
}

```

### Step 4: Write Comprehensive Tests

Add tests in **[`packages/computer/src/runtime/runtime.test.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/runtime.test.ts)** or create new test files in the same directory. Cover:

- **Serialization** — round-trip of options through backend boundaries
- **Error handling** — invalid IDs, timeout scenarios, backend failures
- **Edge cases** — empty IDs, oversized IDs, malformed UTF-8 sequences

```typescript
// Example test structure
describe('WorkspaceRuntimeExecOptions', () => {
  it('validates signal option', async () => {
    const controller = new AbortController();
    controller.abort();
    
    await expect(ws.runtime.exec('echo', {
      backend: 'container',
      signal: controller.signal
    })).rejects.toThrow('Exec aborted before start');
  });
  
  it('handles empty exec ID', async () => {
    await expect(ws.runtime.getExec('')).rejects.toThrow(/invalid.*id/i);
  });
});

```

### Step 5: Document the Public API

Update two documentation locations:

- **[`packages/computer/README.md`](https://github.com/cloudflare/computer/blob/main/packages/computer/README.md)** — package-level usage examples for consumers
- **[`docs/README.md`](https://github.com/cloudflare/computer/blob/main/docs/README.md)** — design specification explaining architectural decisions and intended evolution

Include method signatures, parameter descriptions, and version information when relevant.

## Working with Backend Protocols

The runtime abstracts over multiple backend implementations through the **`WorkspaceModuleBackend`** interface. When contributing types that affect backend communication, coordinate changes across:

| Component | File | Responsibility |
|-----------|------|----------------|
| Backend handle factory | [`types.ts`](https://github.com/cloudflare/computer/blob/main/types.ts) | `backendHandle` type definition |
| Concrete backend | Backend-specific files | Implements `WorkspaceModuleBackend` and returns `WorkspaceModuleBackendHandle` |
| Runtime router | [`runtime.ts`](https://github.com/cloudflare/computer/blob/main/runtime.ts) | Calls `exec`, `getExec`, `killExec`, `disposeExec` on the backend handle |

## Practical Code Examples

### Running a Command with UTF-8 Output

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

const ws = new Workspace(/* configuration */);
const handle = await ws.runtime.exec('echo "hello world"', {
  backend: 'container',
  encoding: 'utf8',
});
const result = await handle.result();
console.log(result.stdout); // → hello world

```

### Streaming Real-Time Output

```typescript
const handle = await ws.runtime.exec('yes', { 
  backend: 'container',
  encoding: 'bytes' // Receive raw Uint8Array
});

for await (const event of handle) {
  if (event.name === 'stdout') {
    console.log(new TextDecoder().decode(event.value));
  }
  if (event.name === 'exit') break;
}
// Clean up resources
handle[Symbol.dispose]();

```

### Resuming a Previous Execution

```typescript
const execId = 'my-long-running-task';
const handle = await ws.runtime.getExec(execId, {
  backend: 'worker-javascript',
  resume: 'tail' // Start from most recent output
});

```

### Cleaning Up Remote Resources

```typescript
await ws.runtime.disposeExec(execId, { 
  backend: 'container' 
});

```

## Key Files Reference

| File | Purpose | Lines to Watch |
|------|---------|--------------|
| [`packages/computer/src/runtime/types.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/types.ts) | All public type definitions | Interface declarations, generics with `E` parameter |
| [`packages/computer/src/runtime/runtime.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/runtime.ts) | Runtime router and stream wrappers | `WorkspaceRuntime` class, `transformModuleEvents`, `drainModuleResult` |
| [`packages/computer/src/runtime/runtime.test.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/runtime.test.ts) | Test suite for runtime API | Serialization, error, and edge case coverage |
| [`packages/computer/README.md`](https://github.com/cloudflare/computer/blob/main/packages/computer/README.md) | Package documentation | Usage examples, API overview |
| [`docs/README.md`](https://github.com/cloudflare/computer/blob/main/docs/README.md) | Design specification | Architectural rationale, evolution roadmap |

## Summary

- **Cloudflare Computer runtime types** are defined in [`packages/computer/src/runtime/types.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/types.ts) with the `WorkspaceRuntime*` naming convention
- **The runtime router** in [`runtime.ts`](https://github.com/cloudflare/computer/blob/main/runtime.ts) validates options, selects backends, and wraps handles with streaming-or-result semantics
- **Stream transformation helpers** (`transformModuleEvents`, `drainModuleResult`) handle encoding conversions and result aggregation
- **Contributions require** type changes, router updates, streaming logic adjustments, comprehensive tests, and documentation updates
- **Backend protocols** implement `WorkspaceModuleBackend` and are selected by the `backend` option string

## Frequently Asked Questions

### What encoding options does WorkspaceRuntimeExecHandle support?

The `encoding` option accepts either `'utf8'` for string output or `'bytes'` (default) for raw `Uint8Array`. This is controlled by the generic `E` parameter on `WorkspaceRuntimeEvent<E>`. When `'utf8'` is specified, `transformModuleEvents` automatically decodes byte streams using `TextDecoder`.

### How do I add a new backend to the runtime?

Define your backend's options interface in [`types.ts`](https://github.com/cloudflare/computer/blob/main/types.ts), implement the `WorkspaceModuleBackend` interface, then register it in the runtime's backend selection logic. The backend must return a `WorkspaceModuleBackendHandle` with `exec`, `getExec`, `killExec`, and `disposeExec` methods that match the runtime's expected signatures.

### Why does WorkspaceRuntimeExecHandle guarantee exclusive use of streaming or result()?

This design prevents race conditions between consuming the `ReadableStream` and awaiting the `result()` promise. Once you call `result()`, the stream is drained and consumed. Attempting to iterate the stream after calling `result()` will yield no events. This invariant is enforced by the implementation in [`runtime.ts`](https://github.com/cloudflare/computer/blob/main/runtime.ts).

### Where should I add tests for new runtime types?

Add tests in [`packages/computer/src/runtime/runtime.test.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/runtime.test.ts) or create additional `*.test.ts` files in the same directory. Test coverage should include: valid and invalid option serialization, backend error propagation, timeout behavior, and edge cases like empty or oversized IDs.