# Which RPC Return Types Do Not Carry Stubs in Cloudflare Computer

> Discover which Cloudflare Computer RPC return types do not carry stubs. Learn about stub-free data serialization over Cap'n Web.

- Repository: [Cloudflare/computer](https://github.com/cloudflare/computer)
- Tags: deep-dive
- Published: 2026-08-13

---

**In the Cloudflare Computer RPC layer, only the top-level stub objects themselves (`WorkspaceRPC`, `SyncRPC`, and `ShellRPC`) carry RPC capabilities, while all other return values—including primitives, `Uint8Array`s, `ReadableStream`s, and plain objects—are serialized as stub-free data over the Cap'n Web wire.**

The Cloudflare Computer repository implements a Cap'n Proto-based RPC system where distinguishing between stub-carrying references and plain data is critical for understanding the API contract. When working with the RPC interface defined in [`packages/rpc/src/interface.ts`](https://github.com/cloudflare/computer/blob/main/packages/rpc/src/interface.ts), developers need to recognize which method returns contain active remote references versus simple serialized values. Only the initial connection objects obtained from `newWebSocketRpcSession` or `newHttpBatchRpcSession` contain RPC stubs; every other return type from subsequent method calls consists of pure data structures.

## RPC Methods That Return Stub-Free Data

According to the RPC contract in [`packages/rpc/src/interface.ts`](https://github.com/cloudflare/computer/blob/main/packages/rpc/src/interface.ts), the following methods return only plain data types that never embed stub capabilities:

- **`push`** – Returns `{ rev: number; appliedPushCursor: ChangeCursor }` containing only numeric values and a structural cursor record.
- **`fetchChanges`** – Returns `{ currentCursor: ChangeCursor; appliedPushCursor: ChangeCursor; stream: ReadableStream<ChangeEntry> }` where cursor objects are plain data and the stream itself carries `ChangeEntry` records, not stubs.
- **`watermarks`** – Returns `{ currentRev: number; pushRev: number; fetchCursor: ChangeCursor }` consisting solely of numbers and cursor records.
- **`readEntry`** – Returns `ChangeEntry | null`, a simple record containing path, hash, and size fields with no stub references.
- **`hasObjects`** – Returns `Uint8Array[]`, an array of raw hash bytes without any RPC capabilities.
- **`fetchObjects`** – Returns `ReadableStream<{ hash: Uint8Array; bytes: Uint8Array }>`, where stream items are raw hash and byte pairs.
- **`pushObjects`** – Returns `Promise<void>`, resolving to `undefined` with no data payload.
- **`exec`** – Returns `{ id: string; events: ReadableStream<ExecEvent> }` where the ID is a plain string and the event stream contains only serialized `ExecEvent` objects.
- **`getExec`** – Returns the same shape as `exec`, containing only strings and event streams.
- **`killExec`** / **`disposeExec`** – Both return `Promise<void>`, resolving to `undefined`.

Because these return types consist of primitives, simple objects, arrays, `Uint8Array`s, and `ReadableStream`s of plain data, they never carry a reference to a remote stub.

## The Only Objects That Carry Stubs

The only objects in the Cloudflare Computer RPC system that contain RPC-stub capabilities are the **stub objects themselves**: `WorkspaceRPC`, `SyncRPC`, and `ShellRPC`. You receive these stub objects exclusively when establishing a connection through `newWebSocketRpcSession` or `newHttpBatchRpcSession` as implemented in [`packages/rpc/src/client.ts`](https://github.com/cloudflare/computer/blob/main/packages/rpc/src/client.ts). Once you hold one of these top-level stubs, every method you invoke on them returns plain data, while the stub object itself remains the sole carrier of RPC capabilities.

## Practical Example: Working with Stub-Free Returns

The following TypeScript example demonstrates how `WorkspaceRPC` acts as the only stub object, while all method returns are plain data:

```typescript
import { newWebSocketRpcSession } from "@cloudflare/computer-rpc/client";
import type { WorkspaceRPC } from "@cloudflare/computer-rpc/interface";

async function demo(stubUrl: string) {
  // Create the top-level stub – this object *does* carry the RPC stub.
  const wsStub = (await newWebSocketRpcSession(stubUrl)) as WorkspaceRPC;

  // ---- Calls that return *no* stubs ----
  // 1. push – returns plain numbers and a cursor.
  const pushResult = await wsStub.sync.push({
    senderRev: 42,
    changes: new ReadableStream<ChangeEntry>({/* … */}),
  });
  console.log(pushResult.rev, pushResult.appliedPushCursor);

  // 2. watermarks – only numbers and a cursor.
  const wm = await wsStub.sync.watermarks();
  console.log(wm.currentRev, wm.pushRev);

  // 3. hasObjects – array of raw hashes.
  const missing = await wsStub.sync.hasObjects([/* Uint8Array hashes */]);
  console.log(missing.length);

  // 4. exec – returns an ID and a stream of ExecEvent objects (no stub).
  const { id, events } = await wsStub.shell.exec({
    source: "echo hello",
  });
  console.log(`Exec ID: ${id}`);
  for await (const ev of events) {
    console.log(ev);
  }

  // The `wsStub` itself is the only stub; everything you receive
  // from the calls above is pure data.
}

```

## Key Source Files

Understanding the distinction between stub-carrying and stub-free returns requires examining these specific files in the Cloudflare Computer repository:

- **[`packages/rpc/src/interface.ts`](https://github.com/cloudflare/computer/blob/main/packages/rpc/src/interface.ts)** – Defines the RPC contract and clearly distinguishes which return types are plain data versus which interfaces represent stub objects.
- **[`packages/rpc/src/client.ts`](https://github.com/cloudflare/computer/blob/main/packages/rpc/src/client.ts)** – Implements the client-side factory functions `newWebSocketRpcSession` and `newHttpBatchRpcSession` that create the top-level stub objects.
- **[`packages/rpc/src/server.ts`](https://github.com/cloudflare/computer/blob/main/packages/rpc/src/server.ts)** – Shows the server-side wiring of the same contract, confirming that only the stub handles themselves are passed across the wire as capabilities.

## Summary

- Only `WorkspaceRPC`, `SyncRPC`, and `ShellRPC` objects carry RPC stubs; all other return values are plain data.
- Stub-free returns include primitives (numbers, strings), structural objects (`ChangeCursor`), binary data (`Uint8Array`), and streams (`ReadableStream<ChangeEntry>` or `ReadableStream<ExecEvent>`).
- Methods like `push`, `watermarks`, `hasObjects`, `exec`, and `disposeExec` all return data structures that serialize directly over the Cap'n Web wire without embedding remote references.
- The RPC contract in [`packages/rpc/src/interface.ts`](https://github.com/cloudflare/computer/blob/main/packages/rpc/src/interface.ts) explicitly defines these return shapes to ensure type safety across the stub boundary.

## Frequently Asked Questions

### What is the difference between a stub object and a plain return value in Cloudflare Computer RPC?

A stub object (`WorkspaceRPC`, `SyncRPC`, or `ShellRPC`) contains an active reference to a remote capability that allows you to invoke further RPC methods, while plain return values are serialized data structures that contain no remote references and cannot be used to make additional RPC calls.

### Can ReadableStream return values contain stubs?

No, the `ReadableStream` instances returned by methods like `fetchChanges`, `fetchObjects`, and `exec` carry only plain data records (`ChangeEntry`, raw hash/byte pairs, or `ExecEvent` objects) and never embed RPC stubs according to the interface defined in [`packages/rpc/src/interface.ts`](https://github.com/cloudflare/computer/blob/main/packages/rpc/src/interface.ts).

### Where are the RPC stub objects created in the Cloudflare Computer codebase?

The stub objects are created in [`packages/rpc/src/client.ts`](https://github.com/cloudflare/computer/blob/main/packages/rpc/src/client.ts) by the `newWebSocketRpcSession` and `newHttpBatchRpcSession` functions, which establish the connection and return the top-level `WorkspaceRPC` stub that serves as the entry point for all subsequent stub-free method calls.

### Do any RPC methods return nested stub objects?

No, according to the current RPC contract, no methods return nested stub objects; only the initial session establishment returns stub capabilities, and all subsequent method invocations return plain data types that cannot perform remote operations.