What Is the Restore Forger in Cloudflare OS Agent Execution?

The restore forger is a transient worker that enables Cloudflare OS agents to create persistent RPC stubs for a gadget's [restore]() method without directly invoking the gadget's code.

In Cloudflare OS, gadgets expose long-lived capabilities through a [restore]() method that reconstructs stateful RPC stubs. The overseer cannot call this method directly because it resides inside the gadget's isolated worker context. The restore forger bridges this gap by acting as a minimal, delegating worker that forwards the restoration request on behalf of the overseer.

How the Restore Forger Works

The restore forger pattern consists of four distinct phases implemented in packages/workshop-backend/src/overseer.ts: loading the transient worker, forging the persistent stub, cleaning up temporary state, and scoping the resulting capability.

Loading the Forger via ctx.restore()

When an agent needs to create a persistent stub, the overseer first loads a one-off forger worker. This worker is stored temporarily in the worker table under a codeId that points to a minimal script.

forger = await this.ctx.restore({type: "gadget", gadgetId, codeId});

The ctx.restore() call targets the forger's code rather than the gadget's [restore]() method directly. This indirection is intentional: it allows the overseer to delegate capability creation while maintaining security boundaries between agents and gadgets.

Source: ctx.restore({type: "gadget", gadgetId, codeId})

Forging the Persistent Stub

Once loaded, the forger receives a forge() call containing the parameters that the target gadget's [restore]() implementation expects.

let stub = await forger.forge(params);

The forger then invokes ctx.restore() internally, targeting the actual gadget. This creates the persistent RPC stub that can be stored and reused across agent execution turns.

Source: let stub = await forger.forge(params);

Cleaning Up Temporary State

After the forger completes its delegation, the overseer removes the temporary codeId entry from the worker table. This prevents the transient forger code from persisting or being reused:

"...clear the ID from the table. When the forger then calls ctx.restore(P) on our behalf, the resulting..."

Source: Cleanup logic

Preserving Capability Security

The forger's transient nature is architecturally significant. According to the source comments:

"The forger is a transient stub argument, so the capability to forge persistent..."

By loading the forger through ctx.restore() rather than embedding its logic in the overseer, Cloudflare OS ensures that the restored capability is correctly scoped to the gadget's namespace. The forger carries no bindings of its own—it exists solely to redirect the restoration request to the appropriate gadget.

Source: Transient stub argument

Why Cloudflare OS Uses a Restore Forger Pattern

The restore forger solves three specific problems in the Cloudflare OS agent execution model:

  • Isolation boundaries — Agents cannot directly invoke gadget [restore]() methods; the forger provides a controlled delegation path
  • Capability scoping — The restored stub is created within the gadget's security context, not the overseer's
  • Temporary code execution — The forger's code lives only for the duration of the restoration, minimizing attack surface

This design aligns with the Cap'n Web RPC model throughout Cloudflare OS, where capabilities are tightly scoped and persistent references are explicitly constructed rather than implicitly inherited.

Practical Example: Creating a Persistent Stub

// Overseer logic to restore a gadget capability
async function forgeRestoreStubForBinding(
  ctx: WorkerContext,
  gadgetId: string,
  codeId: string,
  params: unknown
) {
  // Phase 1: Load the transient forger worker
  const forger = await ctx.restore({
    type: "gadget",
    gadgetId,  // Target gadget that owns the [restore]() method
    codeId,    // Temporary entry pointing to forger code
  });

  // Phase 2: Delegate to forger, which calls gadget's [restore]()
  const persistentStub = await forger.forge(params);

  // Phase 3: Cleanup - remove temporary codeId from table
  await ctx.clearCodeEntry(codeId);

  return persistentStub;
}

The params object typically includes type discriminators and initialization data that the gadget's [restore]() implementation uses to reconstruct its stateful behavior.

Summary

  • The restore forger is a minimal, transient worker that delegates ctx.restore() calls on behalf of the overseer
  • It enables creation of persistent RPC stubs for gadget [restore]() methods while maintaining security isolation
  • The pattern involves four phases: loading via ctx.restore(), forging through forger.forge(), cleanup of temporary state, and proper capability scoping
  • Source implementation resides in packages/workshop-backend/src/overseer.ts with key logic at lines 8640, 9613-9619, 9622, 9627, and 9681-9683

Frequently Asked Questions

What makes the restore forger "transient"?

The forger is transient because its code entry is removed from the worker table immediately after use. It exists only for the single restoration operation and carries no persistent state or bindings. As noted in overseer.ts line 8640, it is explicitly a "transient stub argument" designed for one-time delegation.

Why doesn't the overseer call ctx.restore() on the gadget directly?

Direct invocation would violate capability scoping rules. The overseer runs in a different security context than the gadget. By loading a forger that then calls ctx.restore(), the system ensures the resulting stub is created within the gadget's namespace and inherits the correct permission boundaries.

How does the forger know which gadget to target?

The overseer passes both gadgetId and codeId to ctx.restore(). The codeId identifies the temporary forger script, while gadgetId specifies the ultimate target. The forger's internal logic uses this gadgetId when it performs its own ctx.restore() call to reach the actual gadget.

Where is the forger's code stored before execution?

The forger's code is stored in the worker table under a temporary codeId. This table entry is created by the overseer, used once for the restoration, then explicitly cleared. Lines 9681-9683 in overseer.ts describe this "put the tiny 'forger' worker's code into the table under codeId" pattern.

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 →